diff options
Diffstat (limited to 'drivers/input')
42 files changed, 1544 insertions, 126 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 38b523a1ece0..a11ff74a5127 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -80,7 +80,7 @@ config INPUT_MATRIXKMAP comment "Userland interfaces" config INPUT_MOUSEDEV - tristate "Mouse interface" if EXPERT + tristate "Mouse interface" default y help Say Y here if you want your mouse to be accessible as char devices diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index b6ded17b3be3..a06e12552886 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -18,6 +18,8 @@ #include <linux/poll.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/init.h> #include <linux/input/mt.h> @@ -369,7 +371,11 @@ static int evdev_release(struct inode *inode, struct file *file) mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); - kfree(client); + + if (is_vmalloc_addr(client)) + vfree(client); + else + kfree(client); evdev_close_device(evdev); @@ -389,12 +395,14 @@ static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); + unsigned int size = sizeof(struct evdev_client) + + bufsize * sizeof(struct input_event); struct evdev_client *client; int error; - client = kzalloc(sizeof(struct evdev_client) + - bufsize * sizeof(struct input_event), - GFP_KERNEL); + client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); + if (!client) + client = vzalloc(size); if (!client) return -ENOMEM; diff --git a/drivers/input/input.c b/drivers/input/input.c index e75d015024a1..846ccdd905b1 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -2052,7 +2052,7 @@ int input_register_device(struct input_dev *dev) if (dev->hint_events_per_packet < packet_size) dev->hint_events_per_packet = packet_size; - dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2; + dev->max_vals = dev->hint_events_per_packet + 2; dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL); if (!dev->vals) { error = -ENOMEM; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c1edd39bc5ba..bb174c1a9886 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # menuconfig INPUT_KEYBOARD - bool "Keyboards" if EXPERT || !X86 + bool "Keyboards" default y help Say Y here, and a list of supported keyboards will be displayed. @@ -67,7 +67,7 @@ config KEYBOARD_ATARI module will be called atakbd. config KEYBOARD_ATKBD - tristate "AT keyboard" if EXPERT || !X86 + tristate "AT keyboard" default y select SERIO select SERIO_LIBPS2 @@ -525,7 +525,7 @@ config KEYBOARD_SUNKBD config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" - depends on SUPERH || ARCH_SHMOBILE + depends on SUPERH || ARM || COMPILE_TEST help Say Y here if you want to use a keypad attached to the KEYSC block on SuperH processors such as sh7722 and sh7343. diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 440ce32462ba..2db13246eb8e 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -26,6 +26,7 @@ #include <linux/gpio_keys.h> #include <linux/workqueue.h> #include <linux/gpio.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> #include <linux/spinlock.h> diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index cd5ed9e22168..4e428199e580 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -25,6 +25,7 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/gpio_keys.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 42181435fe67..8b1b01361ec6 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -383,7 +383,7 @@ static struct platform_driver lpc32xx_kscan_driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = &lpc32xx_kscan_pm_ops, - .of_match_table = of_match_ptr(lpc32xx_kscan_match), + .of_match_table = lpc32xx_kscan_match, } }; diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c index b3e3edab6d9f..b31064981e96 100644 --- a/drivers/input/keyboard/nspire-keypad.c +++ b/drivers/input/keyboard/nspire-keypad.c @@ -143,8 +143,10 @@ static int nspire_keypad_open(struct input_dev *input) return error; error = nspire_keypad_chip_init(keypad); - if (error) + if (error) { + clk_disable_unprepare(keypad->clk); return error; + } return 0; } @@ -267,7 +269,7 @@ static struct platform_driver nspire_keypad_driver = { .driver = { .name = "nspire-keypad", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(nspire_keypad_dt_match), + .of_match_table = nspire_keypad_dt_match, }, .probe = nspire_keypad_probe, }; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index a2e758d27584..186138c720c7 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -27,6 +27,7 @@ #include <linux/err.h> #include <linux/input/matrix_keypad.h> #include <linux/slab.h> +#include <linux/of.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 9cd20e6905a0..8508879f6faf 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -614,7 +614,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) unsigned int keymap_rows; const struct of_device_id *match; - match = of_match_device(of_match_ptr(tegra_kbc_of_match), &pdev->dev); + match = of_match_device(tegra_kbc_of_match, &pdev->dev); kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); if (!kbc) { diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index 5f7b427dd7ed..8bd24d52bf1b 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -60,8 +60,8 @@ struct keypad_data { struct clk *clk; struct device *dev; spinlock_t lock; - u32 irq_press; - u32 irq_release; + int irq_press; + int irq_release; int rows, cols, row_shift; int debounce_ms, active_low; u32 prev_keys[3]; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index aa51baaa9b1e..5f4967d01bc3 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY config INPUT_MAX8997_HAPTIC tristate "MAXIM MAX8997 haptic controller support" - depends on HAVE_PWM && MFD_MAX8997 + depends on PWM && HAVE_PWM && MFD_MAX8997 select INPUT_FF_MEMLESS help This option enables device driver support for the haptic controller @@ -461,7 +461,7 @@ config INPUT_PCF8574 config INPUT_PWM_BEEPER tristate "PWM beeper support" - depends on HAVE_PWM || PWM + depends on PWM && HAVE_PWM help Say Y here to get support for PWM based beeper devices. diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index 61891486067c..3a90b710e309 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -108,7 +108,6 @@ static int 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; } diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index 4f77f87847e8..b5d71d245854 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -131,7 +131,6 @@ static int cobalt_buttons_probe(struct platform_device *pdev) err_free_mem: input_free_polled_device(poll_dev); kfree(bdev); - dev_set_drvdata(&pdev->dev, NULL); return error; } @@ -144,7 +143,6 @@ static int cobalt_buttons_remove(struct platform_device *pdev) input_free_polled_device(bdev->poll_dev); iounmap(bdev->reg); kfree(bdev); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index f3309696d053..59d4dcddf6de 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -168,7 +168,7 @@ static void mma8450_close(struct input_polled_dev *dev) * I2C init/probing/exit functions */ static int mma8450_probe(struct i2c_client *c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct input_polled_dev *idev; struct mma8450 *m; @@ -204,6 +204,8 @@ static int mma8450_probe(struct i2c_client *c, goto err_free_mem; } + i2c_set_clientdata(c, m); + return 0; err_free_mem: diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index dce0d95943c5..6983ffbbfb94 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -383,6 +383,7 @@ static int mpu3050_probe(struct i2c_client *client, pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY); + i2c_set_clientdata(client, sensor); return 0; diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 2ff4d1c78ab8..940566e7be13 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -16,6 +16,7 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c index fb4f8ac3343b..83fff38b86b3 100644 --- a/drivers/input/misc/rb532_button.c +++ b/drivers/input/misc/rb532_button.c @@ -87,7 +87,6 @@ static int rb532_button_remove(struct platform_device *pdev) input_unregister_polled_device(poll_dev); input_free_polled_device(poll_dev); - dev_set_drvdata(&pdev->dev, NULL); return 0; } diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 5b1aff825138..f920ba7ab51f 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -24,6 +24,7 @@ #include <linux/gpio.h> #include <linux/rotary_encoder.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 0621c367049a..7b8b03e0d0be 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -153,7 +153,7 @@ static struct platform_driver sirfsoc_pwrc_driver = { .name = "sirfsoc-pwrc", .owner = THIS_MODULE, .pm = &sirfsoc_pwrc_pm_ops, - .of_match_table = of_match_ptr(sirfsoc_pwrc_of_match), + .of_match_table = sirfsoc_pwrc_of_match, } }; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index a0a4bbaef02c..772835938a52 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -430,20 +430,30 @@ static int uinput_setup_device(struct uinput_device *udev, return retval; } -static ssize_t uinput_inject_event(struct uinput_device *udev, - const char __user *buffer, size_t count) +static ssize_t uinput_inject_events(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct input_event ev; + size_t bytes = 0; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; - if (input_event_from_user(buffer, &ev)) - return -EFAULT; + while (bytes + input_event_size() <= count) { + /* + * Note that even if some events were fetched successfully + * we are still going to return EFAULT instead of partial + * count to let userspace know that it got it's buffers + * all wrong. + */ + if (input_event_from_user(buffer + bytes, &ev)) + return -EFAULT; - input_event(udev->dev, ev.type, ev.code, ev.value); + input_event(udev->dev, ev.type, ev.code, ev.value); + bytes += input_event_size(); + } - return input_event_size(); + return bytes; } static ssize_t uinput_write(struct file *file, const char __user *buffer, @@ -460,7 +470,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, return retval; retval = udev->state == UIST_CREATED ? - uinput_inject_event(udev, buffer, count) : + uinput_inject_events(udev, buffer, count) : uinput_setup_device(udev, buffer, count); mutex_unlock(&udev->mutex); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 83658472ad25..ca7a26f1dce8 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -103,7 +103,6 @@ static const struct alps_model_info alps_model_data[] = { /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, - { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_DUALPOINT }, /* Dell XT2 */ { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ @@ -1793,7 +1792,7 @@ int alps_init(struct psmouse *psmouse) snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; dev2->name = (priv->flags & ALPS_DUALPOINT) ? - "DualPoint Stick" : "PS/2 Mouse"; + "DualPoint Stick" : "ALPS PS/2 Device"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; dev2->id.product = PSMOUSE_ALPS; diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index f51765fff054..a5869a856ea5 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -439,7 +439,7 @@ static int cypress_get_finger_count(unsigned char header_byte) case 2: return 5; default: /* Invalid contact (e.g. palm). Ignore it. */ - return -1; + return 0; } } @@ -452,17 +452,10 @@ static int cypress_parse_packet(struct psmouse *psmouse, { unsigned char *packet = psmouse->packet; unsigned char header_byte = packet[0]; - int contact_cnt; memset(report_data, 0, sizeof(struct cytp_report_data)); - contact_cnt = cypress_get_finger_count(header_byte); - - if (contact_cnt < 0) /* e.g. palm detect */ - return -EINVAL; - - report_data->contact_cnt = contact_cnt; - + report_data->contact_cnt = cypress_get_finger_count(header_byte); report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; if (report_data->contact_cnt == 1) { @@ -535,11 +528,9 @@ static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) int slots[CYTP_MAX_MT_SLOTS]; int n; - if (cypress_parse_packet(psmouse, cytp, &report_data)) - return; + cypress_parse_packet(psmouse, cytp, &report_data); n = report_data.contact_cnt; - if (n > CYTP_MAX_MT_SLOTS) n = CYTP_MAX_MT_SLOTS; @@ -605,10 +596,6 @@ static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; contact_cnt = cypress_get_finger_count(packet[0]); - - if (contact_cnt < 0) - return PSMOUSE_BAD_DATA; - if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); else @@ -679,15 +666,15 @@ int cypress_init(struct psmouse *psmouse) { struct cytp_data *cytp; - cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); - psmouse->private = (void *)cytp; - if (cytp == NULL) + cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL); + if (!cytp) return -ENOMEM; - cypress_reset(psmouse); - + psmouse->private = cytp; psmouse->pktsize = 8; + cypress_reset(psmouse); + if (cypress_query_hardware(psmouse)) { psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); goto err_exit; diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 1de1e5f8f795..8541f949778d 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # config SERIO - tristate "Serial I/O support" if EXPERT || !X86 + tristate "Serial I/O support" default y help Say Yes here if you have any input device that uses serial I/O to @@ -19,7 +19,7 @@ config SERIO if SERIO config SERIO_I8042 - tristate "i8042 PC Keyboard controller" if EXPERT || !X86 + tristate "i8042 PC Keyboard controller" default y depends on !PARISC && (!ARM || FOOTBRIDGE_HOST) && \ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \ @@ -170,7 +170,7 @@ config SERIO_MACEPS2 module will be called maceps2. config SERIO_LIBPS2 - tristate "PS/2 driver library" if EXPERT + tristate "PS/2 driver library" depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected @@ -266,4 +266,14 @@ config SERIO_OLPC_APSP To compile this driver as a module, choose M here: the module will be called olpc_apsp. +config HYPERV_KEYBOARD + tristate "Microsoft Synthetic Keyboard driver" + depends on HYPERV + default HYPERV + help + Select this option to enable the Hyper-V Keyboard driver. + + To compile this driver as a module, choose M here: the module will + be called hyperv_keyboard. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 12298b1c0e71..815d874fe724 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_APBPS2) += apbps2.o obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o +obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c new file mode 100644 index 000000000000..3a83c3c14b23 --- /dev/null +++ b/drivers/input/serio/hyperv-keyboard.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2013, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/completion.h> +#include <linux/hyperv.h> +#include <linux/serio.h> +#include <linux/slab.h> + +/* + * Current version 1.0 + * + */ +#define SYNTH_KBD_VERSION_MAJOR 1 +#define SYNTH_KBD_VERSION_MINOR 0 +#define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \ + (SYNTH_KBD_VERSION_MAJOR << 16)) + + +/* + * Message types in the synthetic input protocol + */ +enum synth_kbd_msg_type { + SYNTH_KBD_PROTOCOL_REQUEST = 1, + SYNTH_KBD_PROTOCOL_RESPONSE = 2, + SYNTH_KBD_EVENT = 3, + SYNTH_KBD_LED_INDICATORS = 4, +}; + +/* + * Basic message structures. + */ +struct synth_kbd_msg_hdr { + __le32 type; +}; + +struct synth_kbd_msg { + struct synth_kbd_msg_hdr header; + char data[]; /* Enclosed message */ +}; + +union synth_kbd_version { + __le32 version; +}; + +/* + * Protocol messages + */ +struct synth_kbd_protocol_request { + struct synth_kbd_msg_hdr header; + union synth_kbd_version version_requested; +}; + +#define PROTOCOL_ACCEPTED BIT(0) +struct synth_kbd_protocol_response { + struct synth_kbd_msg_hdr header; + __le32 proto_status; +}; + +#define IS_UNICODE BIT(0) +#define IS_BREAK BIT(1) +#define IS_E0 BIT(2) +#define IS_E1 BIT(3) +struct synth_kbd_keystroke { + struct synth_kbd_msg_hdr header; + __le16 make_code; + __le16 reserved0; + __le32 info; /* Additional information */ +}; + + +#define HK_MAXIMUM_MESSAGE_SIZE 256 + +#define KBD_VSC_SEND_RING_BUFFER_SIZE (10 * PAGE_SIZE) +#define KBD_VSC_RECV_RING_BUFFER_SIZE (10 * PAGE_SIZE) + +#define XTKBD_EMUL0 0xe0 +#define XTKBD_EMUL1 0xe1 +#define XTKBD_RELEASE 0x80 + + +/* + * Represents a keyboard device + */ +struct hv_kbd_dev { + struct hv_device *hv_dev; + struct serio *hv_serio; + struct synth_kbd_protocol_request protocol_req; + struct synth_kbd_protocol_response protocol_resp; + /* Synchronize the request/response if needed */ + struct completion wait_event; + spinlock_t lock; /* protects 'started' field */ + bool started; +}; + +static void hv_kbd_on_receive(struct hv_device *hv_dev, + struct synth_kbd_msg *msg, u32 msg_length) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + struct synth_kbd_keystroke *ks_msg; + unsigned long flags; + u32 msg_type = __le32_to_cpu(msg->header.type); + u32 info; + u16 scan_code; + + switch (msg_type) { + case SYNTH_KBD_PROTOCOL_RESPONSE: + /* + * Validate the information provided by the host. + * If the host is giving us a bogus packet, + * drop the packet (hoping the problem + * goes away). + */ + if (msg_length < sizeof(struct synth_kbd_protocol_response)) { + dev_err(&hv_dev->device, + "Illegal protocol response packet (len: %d)\n", + msg_length); + break; + } + + memcpy(&kbd_dev->protocol_resp, msg, + sizeof(struct synth_kbd_protocol_response)); + complete(&kbd_dev->wait_event); + break; + + case SYNTH_KBD_EVENT: + /* + * Validate the information provided by the host. + * If the host is giving us a bogus packet, + * drop the packet (hoping the problem + * goes away). + */ + if (msg_length < sizeof(struct synth_kbd_keystroke)) { + dev_err(&hv_dev->device, + "Illegal keyboard event packet (len: %d)\n", + msg_length); + break; + } + + ks_msg = (struct synth_kbd_keystroke *)msg; + info = __le32_to_cpu(ks_msg->info); + + /* + * Inject the information through the serio interrupt. + */ + spin_lock_irqsave(&kbd_dev->lock, flags); + if (kbd_dev->started) { + if (info & IS_E0) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL0, 0); + + scan_code = __le16_to_cpu(ks_msg->make_code); + if (info & IS_BREAK) + scan_code |= XTKBD_RELEASE; + + serio_interrupt(kbd_dev->hv_serio, scan_code, 0); + } + spin_unlock_irqrestore(&kbd_dev->lock, flags); + break; + + default: + dev_err(&hv_dev->device, + "unhandled message type %d\n", msg_type); + } +} + +static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, + struct vmpacket_descriptor *desc, + u32 bytes_recvd, + u64 req_id) +{ + struct synth_kbd_msg *msg; + u32 msg_sz; + + switch (desc->type) { + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + /* + * We have a packet that has "inband" data. The API used + * for retrieving the packet guarantees that the complete + * packet is read. So, minimally, we should be able to + * parse the payload header safely (assuming that the host + * can be trusted. Trusting the host seems to be a + * reasonable assumption because in a virtualized + * environment there is not whole lot you can do if you + * don't trust the host. + * + * Nonetheless, let us validate if the host can be trusted + * (in a trivial way). The interesting aspect of this + * validation is how do you recover if we discover that the + * host is not to be trusted? Simply dropping the packet, I + * don't think is an appropriate recovery. In the interest + * of failing fast, it may be better to crash the guest. + * For now, I will just drop the packet! + */ + + msg_sz = bytes_recvd - (desc->offset8 << 3); + if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) { + /* + * Drop the packet and hope + * the problem magically goes away. + */ + dev_err(&hv_dev->device, + "Illegal packet (type: %d, tid: %llx, size: %d)\n", + desc->type, req_id, msg_sz); + break; + } + + msg = (void *)desc + (desc->offset8 << 3); + hv_kbd_on_receive(hv_dev, msg, msg_sz); + break; + + default: + dev_err(&hv_dev->device, + "unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); + break; + } +} + +static void hv_kbd_on_channel_callback(void *context) +{ + struct hv_device *hv_dev = context; + void *buffer; + int bufferlen = 0x100; /* Start with sensible size */ + u32 bytes_recvd; + u64 req_id; + int error; + + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; + + while (1) { + error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen, + &bytes_recvd, &req_id); + switch (error) { + case 0: + if (bytes_recvd == 0) { + kfree(buffer); + return; + } + + hv_kbd_handle_received_packet(hv_dev, buffer, + bytes_recvd, req_id); + break; + + case -ENOBUFS: + kfree(buffer); + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); + if (!buffer) + return; + break; + } + } +} + +static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + struct synth_kbd_protocol_request *request; + struct synth_kbd_protocol_response *response; + u32 proto_status; + int error; + + request = &kbd_dev->protocol_req; + memset(request, 0, sizeof(struct synth_kbd_protocol_request)); + request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); + request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); + + error = vmbus_sendpacket(hv_dev->channel, request, + sizeof(struct synth_kbd_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (error) + return error; + + if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ)) + return -ETIMEDOUT; + + response = &kbd_dev->protocol_resp; + proto_status = __le32_to_cpu(response->proto_status); + if (!(proto_status & PROTOCOL_ACCEPTED)) { + dev_err(&hv_dev->device, + "synth_kbd protocol request failed (version %d)\n", + SYNTH_KBD_VERSION); + return -ENODEV; + } + + return 0; +} + +static int hv_kbd_start(struct serio *serio) +{ + struct hv_kbd_dev *kbd_dev = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&kbd_dev->lock, flags); + kbd_dev->started = true; + spin_unlock_irqrestore(&kbd_dev->lock, flags); + + return 0; +} + +static void hv_kbd_stop(struct serio *serio) +{ + struct hv_kbd_dev *kbd_dev = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&kbd_dev->lock, flags); + kbd_dev->started = false; + spin_unlock_irqrestore(&kbd_dev->lock, flags); +} + +static int hv_kbd_probe(struct hv_device *hv_dev, + const struct hv_vmbus_device_id *dev_id) +{ + struct hv_kbd_dev *kbd_dev; + struct serio *hv_serio; + int error; + + kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL); + hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!kbd_dev || !hv_serio) { + error = -ENOMEM; + goto err_free_mem; + } + + kbd_dev->hv_dev = hv_dev; + kbd_dev->hv_serio = hv_serio; + spin_lock_init(&kbd_dev->lock); + init_completion(&kbd_dev->wait_event); + hv_set_drvdata(hv_dev, kbd_dev); + + hv_serio->dev.parent = &hv_dev->device; + hv_serio->id.type = SERIO_8042_XL; + hv_serio->port_data = kbd_dev; + strlcpy(hv_serio->name, dev_name(&hv_dev->device), + sizeof(hv_serio->name)); + strlcpy(hv_serio->phys, dev_name(&hv_dev->device), + sizeof(hv_serio->phys)); + + hv_serio->start = hv_kbd_start; + hv_serio->stop = hv_kbd_stop; + + error = vmbus_open(hv_dev->channel, + KBD_VSC_SEND_RING_BUFFER_SIZE, + KBD_VSC_RECV_RING_BUFFER_SIZE, + NULL, 0, + hv_kbd_on_channel_callback, + hv_dev); + if (error) + goto err_free_mem; + + error = hv_kbd_connect_to_vsp(hv_dev); + if (error) + goto err_close_vmbus; + + serio_register_port(kbd_dev->hv_serio); + return 0; + +err_close_vmbus: + vmbus_close(hv_dev->channel); +err_free_mem: + kfree(hv_serio); + kfree(kbd_dev); + return error; +} + +static int hv_kbd_remove(struct hv_device *hv_dev) +{ + struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + + serio_unregister_port(kbd_dev->hv_serio); + vmbus_close(hv_dev->channel); + kfree(kbd_dev); + + hv_set_drvdata(hv_dev, NULL); + + return 0; +} + +/* + * Keyboard GUID + * {f912ad6d-2b17-48ea-bd65-f927a61c7684} + */ +#define HV_KBD_GUID \ + .guid = { \ + 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \ + 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \ + } + +static const struct hv_vmbus_device_id id_table[] = { + /* Keyboard guid */ + { HV_KBD_GUID, }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct hv_driver hv_kbd_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = hv_kbd_probe, + .remove = hv_kbd_remove, +}; + +static int __init hv_kbd_init(void) +{ + return vmbus_driver_register(&hv_kbd_drv); +} + +static void __exit hv_kbd_exit(void) +{ + vmbus_driver_unregister(&hv_kbd_drv); +} + +MODULE_LICENSE("GPL"); +module_init(hv_kbd_init); +module_exit(hv_kbd_exit); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 5f306f79da0c..0ec9abbe31fe 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -765,6 +765,7 @@ static struct pnp_device_id pnp_kbd_devids[] = { { .id = "CPQA0D7", .driver_data = 0 }, { .id = "", }, }; +MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids); static struct pnp_driver i8042_pnp_kbd_driver = { .name = "i8042 kbd", @@ -786,6 +787,7 @@ static struct pnp_device_id pnp_aux_devids[] = { { .id = "SYN0801", .driver_data = 0 }, { .id = "", }, }; +MODULE_DEVICE_TABLE(pnp, pnp_aux_devids); static struct pnp_driver i8042_pnp_aux_driver = { .name = "i8042 aux", diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index e53416a4d7f3..867e7c33ac55 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -524,9 +524,6 @@ static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, report_id, rep_data, length, 1); - if (error >= 0) - error = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, - report_id, rep_data, length, 1); } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); kfree(rep_data); @@ -548,7 +545,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat /* MT Tablet PC touch */ return wacom_set_device_mode(intf, 3, 4, 4); } - else if (features->type == WACOM_24HDT) { + else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { return wacom_set_device_mode(intf, 18, 3, 2); } } else if (features->device_type == BTN_TOOL_PEN) { @@ -719,7 +716,7 @@ static int wacom_led_control(struct wacom *wacom) return -ENOMEM; if (wacom->wacom_wac.features.type >= INTUOS5S && - wacom->wacom_wac.features.type <= INTUOS5L) { + wacom->wacom_wac.features.type <= INTUOSPL) { /* * Touch Ring and crop mark LED luminance may take on * one of four values: @@ -981,14 +978,20 @@ static int wacom_initialize_leds(struct wacom *wacom) case INTUOS5S: case INTUOS5: case INTUOS5L: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; - wacom->led.llv = 32; - wacom->led.hlv = 0; - wacom->led.img_lum = 0; - - error = sysfs_create_group(&wacom->intf->dev.kobj, - &intuos5_led_attr_group); + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: + if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) { + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 32; + wacom->led.hlv = 0; + wacom->led.img_lum = 0; + + error = sysfs_create_group(&wacom->intf->dev.kobj, + &intuos5_led_attr_group); + } else + return 0; break; default: @@ -1024,8 +1027,12 @@ static void wacom_destroy_leds(struct wacom *wacom) case INTUOS5S: case INTUOS5: case INTUOS5L: - sysfs_remove_group(&wacom->intf->dev.kobj, - &intuos5_led_attr_group); + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: + if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) + sysfs_remove_group(&wacom->intf->dev.kobj, + &intuos5_led_attr_group); break; } } @@ -1185,34 +1192,47 @@ static void wacom_wireless_work(struct work_struct *work) wacom_wac1->features = *((struct wacom_features *)id->driver_info); wacom_wac1->features.device_type = BTN_TOOL_PEN; + snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen", + wacom_wac1->features.name); error = wacom_register_input(wacom1); if (error) - goto fail1; + goto fail; /* Touch interface */ - wacom_wac2->features = - *((struct wacom_features *)id->driver_info); - wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; - wacom_wac2->features.device_type = BTN_TOOL_FINGER; - wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; - error = wacom_register_input(wacom2); - if (error) - goto fail2; + if (wacom_wac1->features.touch_max) { + wacom_wac2->features = + *((struct wacom_features *)id->driver_info); + wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac2->features.device_type = BTN_TOOL_FINGER; + wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; + if (wacom_wac2->features.touch_max) + snprintf(wacom_wac2->name, WACOM_NAME_MAX, + "%s (WL) Finger",wacom_wac2->features.name); + else + snprintf(wacom_wac2->name, WACOM_NAME_MAX, + "%s (WL) Pad",wacom_wac2->features.name); + error = wacom_register_input(wacom2); + if (error) + goto fail; + } error = wacom_initialize_battery(wacom); if (error) - goto fail3; + goto fail; } return; -fail3: - input_unregister_device(wacom_wac2->input); - wacom_wac2->input = NULL; -fail2: - input_unregister_device(wacom_wac1->input); - wacom_wac1->input = NULL; -fail1: +fail: + if (wacom_wac2->input) { + input_unregister_device(wacom_wac2->input); + wacom_wac2->input = NULL; + } + + if (wacom_wac1->input) { + input_unregister_device(wacom_wac1->input); + wacom_wac1->input = NULL; + } return; } @@ -1302,7 +1322,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i * HID descriptor. If this is the touch interface (wMaxPacketSize * of WACOM_PKGLEN_BBTOUCH3), override the table values. */ - if (features->type >= INTUOS5S && features->type <= INTUOS5L) { + if (features->type >= INTUOS5S && features->type <= INTUOSPL) { if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) { features->device_type = BTN_TOOL_FINGER; features->pktlen = WACOM_PKGLEN_BBTOUCH3; @@ -1329,10 +1349,12 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i struct usb_device *other_dev; /* Append the device type to the name */ - strlcat(wacom_wac->name, - features->device_type == BTN_TOOL_PEN ? - " Pen" : " Finger", - sizeof(wacom_wac->name)); + if (features->device_type != BTN_TOOL_FINGER) + strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); + else if (features->touch_max) + strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX); + else + strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); other_dev = wacom_get_sibling(dev, features->oVid, features->oPid); if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index c59b797eeafa..782c2535f1d8 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -427,6 +427,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (features->type == WACOM_21UX2)) return 1; + /* Range Report */ + if ((data[1] & 0xfe) == 0x20) { + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); + } + /* Exit report */ if ((data[1] & 0xfe) == 0x80) { if (features->quirks == WACOM_QUIRK_MULTI_INPUT) @@ -477,7 +484,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom) /* general pen packet */ if ((data[1] & 0xb8) == 0xa0) { t = (data[6] << 2) | ((data[7] >> 6) & 3); - if (features->type >= INTUOS4S && features->type <= WACOM_24HD) { + if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) { t = (t << 1) | (data[1] & 1); } input_report_abs(input, ABS_PRESSURE, t); @@ -621,14 +628,30 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) } else { input_report_abs(input, ABS_MISC, 0); } - } else if (features->type >= INTUOS5S && features->type <= INTUOS5L) { + } else if (features->type == CINTIQ_HYBRID) { + /* + * Do not send hardware buttons under Android. They + * are already sent to the system through GPIO (and + * have different meaning). + */ + input_report_key(input, BTN_1, (data[4] & 0x01)); + input_report_key(input, BTN_2, (data[4] & 0x02)); + input_report_key(input, BTN_3, (data[4] & 0x04)); + input_report_key(input, BTN_4, (data[4] & 0x08)); + + input_report_key(input, BTN_5, (data[4] & 0x10)); /* Right */ + input_report_key(input, BTN_6, (data[4] & 0x20)); /* Up */ + input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */ + input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */ + input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */ + } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { int i; /* Touch ring mode switch has no capacitive sensor */ input_report_key(input, BTN_0, (data[3] & 0x01)); /* - * ExpressKeys on Intuos5 have a capacitive sensor in + * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in * addition to the mechanical switch. Switch data is * stored in data[4], capacitive data in data[5]. */ @@ -716,7 +739,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) features->type == INTUOS4 || features->type == INTUOS4S || features->type == INTUOS5 || - features->type == INTUOS5S)) { + features->type == INTUOS5S || + features->type == INTUOSPM || + features->type == INTUOSPS)) { return 0; } @@ -769,8 +794,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* I4 mouse */ - if ((features->type >= INTUOS4S && features->type <= INTUOS4L) || - (features->type >= INTUOS5S && features->type <= INTUOS5L)) { + if (features->type >= INTUOS4S && features->type <= INTUOSPL) { input_report_key(input, BTN_LEFT, data[6] & 0x01); input_report_key(input, BTN_MIDDLE, data[6] & 0x02); input_report_key(input, BTN_RIGHT, data[6] & 0x04); @@ -797,7 +821,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) } } } else if ((features->type < INTUOS3S || features->type == INTUOS3L || - features->type == INTUOS4L || features->type == INTUOS5L) && + features->type == INTUOS4L || features->type == INTUOS5L || + features->type == INTUOSPL) && wacom->tool[idx] == BTN_TOOL_LENS) { /* Lens cursor packets */ input_report_key(input, BTN_LEFT, data[8] & 0x01); @@ -1107,6 +1132,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) { + struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; bool touch = data[1] & 0x80; int slot = input_mt_get_slot_by_key(input, data[0]); @@ -1122,14 +1148,23 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) if (touch) { int x = (data[2] << 4) | (data[4] >> 4); int y = (data[3] << 4) | (data[4] & 0x0f); - int a = data[5]; + int width, height; - // "a" is a scaled-down area which we assume is roughly - // circular and which can be described as: a=(pi*r^2)/C. - int x_res = input_abs_get_res(input, ABS_X); - int y_res = input_abs_get_res(input, ABS_Y); - int width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE); - int height = width * y_res / x_res; + if (features->type >= INTUOSPS && features->type <= INTUOSPL) { + width = data[5]; + height = data[6]; + } else { + /* + * "a" is a scaled-down area which we assume is + * roughly circular and which can be described as: + * a=(pi*r^2)/C. + */ + int a = data[5]; + int x_res = input_abs_get_res(input, ABS_X); + int y_res = input_abs_get_res(input, ABS_Y); + width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE); + height = width * y_res / x_res; + } input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -1327,6 +1362,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case WACOM_22HD: case WACOM_24HD: case DTK: + case CINTIQ_HYBRID: sync = wacom_intuos_irq(wacom_wac); break; @@ -1337,6 +1373,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case INTUOS5S: case INTUOS5: case INTUOS5L: + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: if (len == WACOM_PKGLEN_BBTOUCH3) sync = wacom_bpt3_touch(wacom_wac); else @@ -1420,7 +1459,7 @@ void wacom_setup_device_quirks(struct wacom_features *features) /* these device have multiple inputs */ if (features->type >= WIRELESS || - (features->type >= INTUOS5S && features->type <= INTUOS5L) || + (features->type >= INTUOS5S && features->type <= INTUOSPL) || (features->oVid && features->oPid)) features->quirks |= WACOM_QUIRK_MULTI_INPUT; @@ -1627,6 +1666,8 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case INTUOS5: case INTUOS5L: + case INTUOSPM: + case INTUOSPL: if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_7, input_dev->keybit); __set_bit(BTN_8, input_dev->keybit); @@ -1634,6 +1675,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, /* fall through */ case INTUOS5S: + case INTUOSPS: __set_bit(INPUT_PROP_POINTER, input_dev->propbit); if (features->device_type == BTN_TOOL_PEN) { @@ -1765,6 +1807,24 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, 0, 0); } break; + + case CINTIQ_HYBRID: + __set_bit(BTN_1, input_dev->keybit); + __set_bit(BTN_2, input_dev->keybit); + __set_bit(BTN_3, input_dev->keybit); + __set_bit(BTN_4, input_dev->keybit); + + __set_bit(BTN_5, input_dev->keybit); + __set_bit(BTN_6, input_dev->keybit); + __set_bit(BTN_7, input_dev->keybit); + __set_bit(BTN_8, input_dev->keybit); + __set_bit(BTN_0, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + wacom_setup_cintiq(wacom_wac); + break; } return 0; } @@ -1952,6 +2012,18 @@ static const struct wacom_features wacom_features_0x29 = static const struct wacom_features wacom_features_0x2A = { "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, 63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x314 = + { "Wacom Intuos Pro S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, + 63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + .touch_max = 16 }; +static const struct wacom_features wacom_features_0x315 = + { "Wacom Intuos Pro M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, + 63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + .touch_max = 16 }; +static const struct wacom_features wacom_features_0x317 = + { "Wacom Intuos Pro L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, + 63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + .touch_max = 16 }; static const struct wacom_features wacom_features_0xF4 = { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047, 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; @@ -2131,6 +2203,13 @@ static const struct wacom_features wacom_features_0x301 = static const struct wacom_features wacom_features_0x6004 = { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x0307 = + { "Wacom ISDv5 307", WACOM_PKGLEN_INTUOS, 59552, 33848, 2047, + 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; +static const struct wacom_features wacom_features_0x0309 = + { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10 }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -2259,12 +2338,17 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x300) }, { USB_DEVICE_WACOM(0x301) }, { USB_DEVICE_WACOM(0x304) }, + { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x47) }, { USB_DEVICE_WACOM(0xF4) }, { USB_DEVICE_WACOM(0xF8) }, { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_WACOM(0xFA) }, + { USB_DEVICE_WACOM(0x0307) }, + { USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_LENOVO(0x6004) }, { } }; diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index dfc9e08e7f70..fd23a3790605 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -14,6 +14,8 @@ /* maximum packet length for USB devices */ #define WACOM_PKGLEN_MAX 64 +#define WACOM_NAME_MAX 64 + /* packet length for individual models */ #define WACOM_PKGLEN_PENPRTN 7 #define WACOM_PKGLEN_GRAPHIRE 8 @@ -76,10 +78,14 @@ enum { INTUOS5S, INTUOS5, INTUOS5L, + INTUOSPS, + INTUOSPM, + INTUOSPL, WACOM_21UX2, WACOM_22HD, DTK, WACOM_24HD, + CINTIQ_HYBRID, CINTIQ, WACOM_BEE, WACOM_13HD, @@ -126,7 +132,7 @@ struct wacom_shared { }; struct wacom_wac { - char name[64]; + char name[WACOM_NAME_MAX]; unsigned char *data; int tool[2]; int id[2]; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e09ec67957a3..00d1e547b211 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -919,4 +919,17 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_ZFORCE + tristate "Neonode zForce infrared touchscreens" + depends on I2C + depends on GPIOLIB + help + Say Y here if you have a touchscreen using the zforce + infraread technology from Neonode. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called zforce_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f5216c1bf53e..7587883b8d38 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index f3a174a83c82..69834dd3c313 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -806,7 +806,6 @@ err_free_irq: err_free_mem: input_free_device(input_dev); kfree(ts); - spi_set_drvdata(spi, NULL); return err; } @@ -823,7 +822,6 @@ static int ad7877_remove(struct spi_device *spi) kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); - spi_set_drvdata(spi, NULL); return 0; } diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 606da5bd6115..1a7b1143536e 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -142,7 +142,6 @@ static int ad7879_spi_remove(struct spi_device *spi) struct ad7879 *ts = spi_get_drvdata(spi); ad7879_remove(ts); - spi_set_drvdata(spi, NULL); return 0; } diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index d038575f49db..42d830efa316 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -2113,7 +2113,6 @@ error_startup: error_request_irq: if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); - dev_set_drvdata(dev, NULL); error_free_xfer: kfree(cd->xfer_buf); error_free_cd: @@ -2151,7 +2150,6 @@ int cyttsp4_remove(struct cyttsp4 *cd) free_irq(cd->irq, cd); if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); - dev_set_drvdata(dev, NULL); cyttsp4_free_si_ptrs(cd); kfree(cd); return 0; diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c index a71e1141d638..b19434cebbf6 100644 --- a/drivers/input/touchscreen/cyttsp4_spi.c +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -171,10 +171,7 @@ static int cyttsp4_spi_probe(struct spi_device *spi) ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, CY_SPI_DATA_BUF_SIZE); - if (IS_ERR(ts)) - return PTR_ERR(ts); - - return 0; + return PTR_ERR_OR_ZERO(ts); } static int cyttsp4_spi_remove(struct spi_device *spi) diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index ef5fcb0945e9..054d22583248 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -273,7 +273,7 @@ static struct i2c_driver egalax_ts_driver = { .name = "egalax_ts", .owner = THIS_MODULE, .pm = &egalax_ts_pm_ops, - .of_match_table = of_match_ptr(egalax_ts_dt_ids), + .of_match_table = egalax_ts_dt_ids, }, .id_table = egalax_ts_id, .probe = egalax_ts_probe, diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c index 66500852341b..92e2243fb77d 100644 --- a/drivers/input/touchscreen/htcpen.c +++ b/drivers/input/touchscreen/htcpen.c @@ -186,8 +186,6 @@ static int htcpen_isa_remove(struct device *dev, unsigned int id) release_region(HTCPEN_PORT_INIT, 1); release_region(HTCPEN_PORT_IRQ_CLEAR, 1); - dev_set_drvdata(dev, NULL); - return 0; } diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 1740a2496371..2f03b2f289dd 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -24,6 +24,7 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_gpio.h> #include <linux/pm_qos.h> #include <linux/slab.h> diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 24e625c0b531..68beadaabceb 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -354,9 +354,16 @@ static int titsc_parse_dt(struct platform_device *pdev, if (err < 0) return err; - err = of_property_read_u32(node, "ti,coordiante-readouts", + /* + * Try with the new binding first. If it fails, try again with + * bogus, miss-spelled version. + */ + err = of_property_read_u32(node, "ti,coordinate-readouts", &ts_dev->coordinate_readouts); if (err < 0) + err = of_property_read_u32(node, "ti,coordiante-readouts", + &ts_dev->coordinate_readouts); + if (err < 0) return err; return of_property_read_u32_array(node, "ti,wire-config", @@ -511,7 +518,7 @@ static struct platform_driver ti_tsc_driver = { .name = "TI-am335x-tsc", .owner = THIS_MODULE, .pm = TITSC_PM_OPS, - .of_match_table = of_match_ptr(ti_tsc_dt_ids), + .of_match_table = ti_tsc_dt_ids, }, }; module_platform_driver(ti_tsc_driver); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 7213e8b07e79..811353353917 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -678,7 +678,6 @@ static int tsc2005_probe(struct spi_device *spi) err_remove_sysfs: sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); err_clear_drvdata: - spi_set_drvdata(spi, NULL); free_irq(spi->irq, ts); err_free_mem: input_free_device(input_dev); @@ -696,7 +695,6 @@ static int tsc2005_remove(struct spi_device *spi) input_unregister_device(ts->idev); kfree(ts); - spi_set_drvdata(spi, NULL); return 0; } diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c new file mode 100644 index 000000000000..75762d6ff3ba --- /dev/null +++ b/drivers/input/touchscreen/zforce_ts.c @@ -0,0 +1,836 @@ +/* + * Copyright (C) 2012-2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * based in parts on Nook zforce driver + * + * Copyright (C) 2010 Barnes & Noble, Inc. + * Author: Pieter Truter<ptruter@intrinsyc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/hrtimer.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/input/mt.h> +#include <linux/platform_data/zforce_ts.h> + +#define WAIT_TIMEOUT msecs_to_jiffies(1000) + +#define FRAME_START 0xee + +/* Offsets of the different parts of the payload the controller sends */ +#define PAYLOAD_HEADER 0 +#define PAYLOAD_LENGTH 1 +#define PAYLOAD_BODY 2 + +/* Response offsets */ +#define RESPONSE_ID 0 +#define RESPONSE_DATA 1 + +/* Commands */ +#define COMMAND_DEACTIVATE 0x00 +#define COMMAND_INITIALIZE 0x01 +#define COMMAND_RESOLUTION 0x02 +#define COMMAND_SETCONFIG 0x03 +#define COMMAND_DATAREQUEST 0x04 +#define COMMAND_SCANFREQ 0x08 +#define COMMAND_STATUS 0X1e + +/* + * Responses the controller sends as a result of + * command requests + */ +#define RESPONSE_DEACTIVATE 0x00 +#define RESPONSE_INITIALIZE 0x01 +#define RESPONSE_RESOLUTION 0x02 +#define RESPONSE_SETCONFIG 0x03 +#define RESPONSE_SCANFREQ 0x08 +#define RESPONSE_STATUS 0X1e + +/* + * Notifications are send by the touch controller without + * being requested by the driver and include for example + * touch indications + */ +#define NOTIFICATION_TOUCH 0x04 +#define NOTIFICATION_BOOTCOMPLETE 0x07 +#define NOTIFICATION_OVERRUN 0x25 +#define NOTIFICATION_PROXIMITY 0x26 +#define NOTIFICATION_INVALID_COMMAND 0xfe + +#define ZFORCE_REPORT_POINTS 2 +#define ZFORCE_MAX_AREA 0xff + +#define STATE_DOWN 0 +#define STATE_MOVE 1 +#define STATE_UP 2 + +#define SETCONFIG_DUALTOUCH (1 << 0) + +struct zforce_point { + int coord_x; + int coord_y; + int state; + int id; + int area_major; + int area_minor; + int orientation; + int pressure; + int prblty; +}; + +/* + * @client the i2c_client + * @input the input device + * @suspending in the process of going to suspend (don't emit wakeup + * events for commands executed to suspend the device) + * @suspended device suspended + * @access_mutex serialize i2c-access, to keep multipart reads together + * @command_done completion to wait for the command result + * @command_mutex serialize commands send to the ic + * @command_waiting the id of the command that that is currently waiting + * for a result + * @command_result returned result of the command + */ +struct zforce_ts { + struct i2c_client *client; + struct input_dev *input; + const struct zforce_ts_platdata *pdata; + char phys[32]; + + bool suspending; + bool suspended; + bool boot_complete; + + /* Firmware version information */ + u16 version_major; + u16 version_minor; + u16 version_build; + u16 version_rev; + + struct mutex access_mutex; + + struct completion command_done; + struct mutex command_mutex; + int command_waiting; + int command_result; +}; + +static int zforce_command(struct zforce_ts *ts, u8 cmd) +{ + struct i2c_client *client = ts->client; + char buf[3]; + int ret; + + dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); + + buf[0] = FRAME_START; + buf[1] = 1; /* data size, command only */ + buf[2] = cmd; + + mutex_lock(&ts->access_mutex); + ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf)); + mutex_unlock(&ts->access_mutex); + if (ret < 0) { + dev_err(&client->dev, "i2c send data request error: %d\n", ret); + return ret; + } + + return 0; +} + +static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len) +{ + struct i2c_client *client = ts->client; + int ret; + + ret = mutex_trylock(&ts->command_mutex); + if (!ret) { + dev_err(&client->dev, "already waiting for a command\n"); + return -EBUSY; + } + + dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n", + buf[1], buf[2]); + + ts->command_waiting = buf[2]; + + mutex_lock(&ts->access_mutex); + ret = i2c_master_send(client, buf, len); + mutex_unlock(&ts->access_mutex); + if (ret < 0) { + dev_err(&client->dev, "i2c send data request error: %d\n", ret); + goto unlock; + } + + dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]); + + if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) { + ret = -ETIME; + goto unlock; + } + + ret = ts->command_result; + +unlock: + mutex_unlock(&ts->command_mutex); + return ret; +} + +static int zforce_command_wait(struct zforce_ts *ts, u8 cmd) +{ + struct i2c_client *client = ts->client; + char buf[3]; + int ret; + + dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); + + buf[0] = FRAME_START; + buf[1] = 1; /* data size, command only */ + buf[2] = cmd; + + ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); + if (ret < 0) { + dev_err(&client->dev, "i2c send data request error: %d\n", ret); + return ret; + } + + return 0; +} + +static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y) +{ + struct i2c_client *client = ts->client; + char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION, + (x & 0xff), ((x >> 8) & 0xff), + (y & 0xff), ((y >> 8) & 0xff) }; + + dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y); + + return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger, + u16 stylus) +{ + struct i2c_client *client = ts->client; + char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ, + (idle & 0xff), ((idle >> 8) & 0xff), + (finger & 0xff), ((finger >> 8) & 0xff), + (stylus & 0xff), ((stylus >> 8) & 0xff) }; + + dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n", + idle, finger, stylus); + + return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_setconfig(struct zforce_ts *ts, char b1) +{ + struct i2c_client *client = ts->client; + char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG, + b1, 0, 0, 0 }; + + dev_dbg(&client->dev, "set config to (%d)\n", b1); + + return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_start(struct zforce_ts *ts) +{ + struct i2c_client *client = ts->client; + const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + int ret; + + dev_dbg(&client->dev, "starting device\n"); + + ret = zforce_command_wait(ts, COMMAND_INITIALIZE); + if (ret) { + dev_err(&client->dev, "Unable to initialize, %d\n", ret); + return ret; + } + + ret = zforce_resolution(ts, pdata->x_max, pdata->y_max); + if (ret) { + dev_err(&client->dev, "Unable to set resolution, %d\n", ret); + goto error; + } + + ret = zforce_scan_frequency(ts, 10, 50, 50); + if (ret) { + dev_err(&client->dev, "Unable to set scan frequency, %d\n", + ret); + goto error; + } + + if (zforce_setconfig(ts, SETCONFIG_DUALTOUCH)) { + dev_err(&client->dev, "Unable to set config\n"); + goto error; + } + + /* start sending touch events */ + ret = zforce_command(ts, COMMAND_DATAREQUEST); + if (ret) { + dev_err(&client->dev, "Unable to request data\n"); + goto error; + } + + /* + * Per NN, initial cal. take max. of 200msec. + * Allow time to complete this calibration + */ + msleep(200); + + return 0; + +error: + zforce_command_wait(ts, COMMAND_DEACTIVATE); + return ret; +} + +static int zforce_stop(struct zforce_ts *ts) +{ + struct i2c_client *client = ts->client; + int ret; + + dev_dbg(&client->dev, "stopping device\n"); + + /* Deactivates touch sensing and puts the device into sleep. */ + ret = zforce_command_wait(ts, COMMAND_DEACTIVATE); + if (ret != 0) { + dev_err(&client->dev, "could not deactivate device, %d\n", + ret); + return ret; + } + + return 0; +} + +static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) +{ + struct i2c_client *client = ts->client; + const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + struct zforce_point point; + int count, i, num = 0; + + count = payload[0]; + if (count > ZFORCE_REPORT_POINTS) { + dev_warn(&client->dev, "to many coordinates %d, expected max %d\n", + count, ZFORCE_REPORT_POINTS); + count = ZFORCE_REPORT_POINTS; + } + + for (i = 0; i < count; i++) { + point.coord_x = + payload[9 * i + 2] << 8 | payload[9 * i + 1]; + point.coord_y = + payload[9 * i + 4] << 8 | payload[9 * i + 3]; + + if (point.coord_x > pdata->x_max || + point.coord_y > pdata->y_max) { + dev_warn(&client->dev, "coordinates (%d,%d) invalid\n", + point.coord_x, point.coord_y); + point.coord_x = point.coord_y = 0; + } + + point.state = payload[9 * i + 5] & 0x03; + point.id = (payload[9 * i + 5] & 0xfc) >> 2; + + /* determine touch major, minor and orientation */ + point.area_major = max(payload[9 * i + 6], + payload[9 * i + 7]); + point.area_minor = min(payload[9 * i + 6], + payload[9 * i + 7]); + point.orientation = payload[9 * i + 6] > payload[9 * i + 7]; + + point.pressure = payload[9 * i + 8]; + point.prblty = payload[9 * i + 9]; + + dev_dbg(&client->dev, + "point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n", + i, count, point.state, point.id, + point.pressure, point.prblty, + point.coord_x, point.coord_y, + point.area_major, point.area_minor, + point.orientation); + + /* the zforce id starts with "1", so needs to be decreased */ + input_mt_slot(ts->input, point.id - 1); + + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, + point.state != STATE_UP); + + if (point.state != STATE_UP) { + input_report_abs(ts->input, ABS_MT_POSITION_X, + point.coord_x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + point.coord_y); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + point.area_major); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, + point.area_minor); + input_report_abs(ts->input, ABS_MT_ORIENTATION, + point.orientation); + num++; + } + } + + input_mt_sync_frame(ts->input); + + input_mt_report_finger_count(ts->input, num); + + input_sync(ts->input); + + return 0; +} + +static int zforce_read_packet(struct zforce_ts *ts, u8 *buf) +{ + struct i2c_client *client = ts->client; + int ret; + + mutex_lock(&ts->access_mutex); + + /* read 2 byte message header */ + ret = i2c_master_recv(client, buf, 2); + if (ret < 0) { + dev_err(&client->dev, "error reading header: %d\n", ret); + goto unlock; + } + + if (buf[PAYLOAD_HEADER] != FRAME_START) { + dev_err(&client->dev, "invalid frame start: %d\n", buf[0]); + ret = -EIO; + goto unlock; + } + + if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) { + dev_err(&client->dev, "invalid payload length: %d\n", + buf[PAYLOAD_LENGTH]); + ret = -EIO; + goto unlock; + } + + /* read the message */ + ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]); + if (ret < 0) { + dev_err(&client->dev, "error reading payload: %d\n", ret); + goto unlock; + } + + dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n", + buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]); + +unlock: + mutex_unlock(&ts->access_mutex); + return ret; +} + +static void zforce_complete(struct zforce_ts *ts, int cmd, int result) +{ + struct i2c_client *client = ts->client; + + if (ts->command_waiting == cmd) { + dev_dbg(&client->dev, "completing command 0x%x\n", cmd); + ts->command_result = result; + complete(&ts->command_done); + } else { + dev_dbg(&client->dev, "command %d not for us\n", cmd); + } +} + +static irqreturn_t zforce_interrupt(int irq, void *dev_id) +{ + struct zforce_ts *ts = dev_id; + struct i2c_client *client = ts->client; + const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + int ret; + u8 payload_buffer[512]; + u8 *payload; + + /* + * When suspended, emit a wakeup signal if necessary and return. + * Due to the level-interrupt we will get re-triggered later. + */ + if (ts->suspended) { + if (device_may_wakeup(&client->dev)) + pm_wakeup_event(&client->dev, 500); + msleep(20); + return IRQ_HANDLED; + } + + dev_dbg(&client->dev, "handling interrupt\n"); + + /* Don't emit wakeup events from commands run by zforce_suspend */ + if (!ts->suspending && device_may_wakeup(&client->dev)) + pm_stay_awake(&client->dev); + + while (!gpio_get_value(pdata->gpio_int)) { + ret = zforce_read_packet(ts, payload_buffer); + if (ret < 0) { + dev_err(&client->dev, "could not read packet, ret: %d\n", + ret); + break; + } + + payload = &payload_buffer[PAYLOAD_BODY]; + + switch (payload[RESPONSE_ID]) { + case NOTIFICATION_TOUCH: + /* + * Always report touch-events received while + * suspending, when being a wakeup source + */ + if (ts->suspending && device_may_wakeup(&client->dev)) + pm_wakeup_event(&client->dev, 500); + zforce_touch_event(ts, &payload[RESPONSE_DATA]); + break; + + case NOTIFICATION_BOOTCOMPLETE: + ts->boot_complete = payload[RESPONSE_DATA]; + zforce_complete(ts, payload[RESPONSE_ID], 0); + break; + + case RESPONSE_INITIALIZE: + case RESPONSE_DEACTIVATE: + case RESPONSE_SETCONFIG: + case RESPONSE_RESOLUTION: + case RESPONSE_SCANFREQ: + zforce_complete(ts, payload[RESPONSE_ID], + payload[RESPONSE_DATA]); + break; + + case RESPONSE_STATUS: + /* + * Version Payload Results + * [2:major] [2:minor] [2:build] [2:rev] + */ + ts->version_major = (payload[RESPONSE_DATA + 1] << 8) | + payload[RESPONSE_DATA]; + ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) | + payload[RESPONSE_DATA + 2]; + ts->version_build = (payload[RESPONSE_DATA + 5] << 8) | + payload[RESPONSE_DATA + 4]; + ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) | + payload[RESPONSE_DATA + 6]; + dev_dbg(&ts->client->dev, "Firmware Version %04x:%04x %04x:%04x\n", + ts->version_major, ts->version_minor, + ts->version_build, ts->version_rev); + + zforce_complete(ts, payload[RESPONSE_ID], 0); + break; + + case NOTIFICATION_INVALID_COMMAND: + dev_err(&ts->client->dev, "invalid command: 0x%x\n", + payload[RESPONSE_DATA]); + break; + + default: + dev_err(&ts->client->dev, "unrecognized response id: 0x%x\n", + payload[RESPONSE_ID]); + break; + } + } + + if (!ts->suspending && device_may_wakeup(&client->dev)) + pm_relax(&client->dev); + + dev_dbg(&client->dev, "finished interrupt\n"); + + return IRQ_HANDLED; +} + +static int zforce_input_open(struct input_dev *dev) +{ + struct zforce_ts *ts = input_get_drvdata(dev); + int ret; + + ret = zforce_start(ts); + if (ret) + return ret; + + return 0; +} + +static void zforce_input_close(struct input_dev *dev) +{ + struct zforce_ts *ts = input_get_drvdata(dev); + struct i2c_client *client = ts->client; + int ret; + + ret = zforce_stop(ts); + if (ret) + dev_warn(&client->dev, "stopping zforce failed\n"); + + return; +} + +#ifdef CONFIG_PM_SLEEP +static int zforce_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct zforce_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + int ret = 0; + + mutex_lock(&input->mutex); + ts->suspending = true; + + /* + * When configured as a wakeup source device should always wake + * the system, therefore start device if necessary. + */ + if (device_may_wakeup(&client->dev)) { + dev_dbg(&client->dev, "suspend while being a wakeup source\n"); + + /* Need to start device, if not open, to be a wakeup source. */ + if (!input->users) { + ret = zforce_start(ts); + if (ret) + goto unlock; + } + + enable_irq_wake(client->irq); + } else if (input->users) { + dev_dbg(&client->dev, "suspend without being a wakeup source\n"); + + ret = zforce_stop(ts); + if (ret) + goto unlock; + + disable_irq(client->irq); + } + + ts->suspended = true; + +unlock: + ts->suspending = false; + mutex_unlock(&input->mutex); + + return ret; +} + +static int zforce_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct zforce_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + int ret = 0; + + mutex_lock(&input->mutex); + + ts->suspended = false; + + if (device_may_wakeup(&client->dev)) { + dev_dbg(&client->dev, "resume from being a wakeup source\n"); + + disable_irq_wake(client->irq); + + /* need to stop device if it was not open on suspend */ + if (!input->users) { + ret = zforce_stop(ts); + if (ret) + goto unlock; + } + } else if (input->users) { + dev_dbg(&client->dev, "resume without being a wakeup source\n"); + + enable_irq(client->irq); + + ret = zforce_start(ts); + if (ret < 0) + goto unlock; + } + +unlock: + mutex_unlock(&input->mutex); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume); + +static void zforce_reset(void *data) +{ + struct zforce_ts *ts = data; + + gpio_set_value(ts->pdata->gpio_rst, 0); +} + +static int zforce_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + struct zforce_ts *ts; + struct input_dev *input_dev; + int ret; + + if (!pdata) + return -EINVAL; + + ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN, + "zforce_ts_int"); + if (ret) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_int, ret); + return ret; + } + + ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst, + GPIOF_OUT_INIT_LOW, "zforce_ts_rst"); + if (ret) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_rst, ret); + return ret; + } + + ret = devm_add_action(&client->dev, zforce_reset, ts); + if (ret) { + dev_err(&client->dev, "failed to register reset action, %d\n", + ret); + return ret; + } + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "could not allocate input device\n"); + return -ENOMEM; + } + + mutex_init(&ts->access_mutex); + mutex_init(&ts->command_mutex); + + ts->pdata = pdata; + ts->client = client; + ts->input = input_dev; + + input_dev->name = "Neonode zForce touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->open = zforce_input_open; + input_dev->close = zforce_input_close; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max, 0, 0); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, + ZFORCE_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, + ZFORCE_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT); + + input_set_drvdata(ts->input, ts); + + init_completion(&ts->command_done); + + /* + * The zforce pulls the interrupt low when it has data ready. + * After it is triggered the isr thread runs until all the available + * packets have been read and the interrupt is high again. + * Therefore we can trigger the interrupt anytime it is low and do + * not need to limit it to the interrupt edge. + */ + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + zforce_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + input_dev->name, ts); + if (ret) { + dev_err(&client->dev, "irq %d request failed\n", client->irq); + return ret; + } + + i2c_set_clientdata(client, ts); + + /* let the controller boot */ + gpio_set_value(pdata->gpio_rst, 1); + + ts->command_waiting = NOTIFICATION_BOOTCOMPLETE; + if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) + dev_warn(&client->dev, "bootcomplete timed out\n"); + + /* need to start device to get version information */ + ret = zforce_command_wait(ts, COMMAND_INITIALIZE); + if (ret) { + dev_err(&client->dev, "unable to initialize, %d\n", ret); + return ret; + } + + /* this gets the firmware version among other informations */ + ret = zforce_command_wait(ts, COMMAND_STATUS); + if (ret < 0) { + dev_err(&client->dev, "couldn't get status, %d\n", ret); + zforce_stop(ts); + return ret; + } + + /* stop device and put it into sleep until it is opened */ + ret = zforce_stop(ts); + if (ret < 0) + return ret; + + device_set_wakeup_capable(&client->dev, true); + + ret = input_register_device(input_dev); + if (ret) { + dev_err(&client->dev, "could not register input device, %d\n", + ret); + return ret; + } + + return 0; +} + +static struct i2c_device_id zforce_idtable[] = { + { "zforce-ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, zforce_idtable); + +static struct i2c_driver zforce_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "zforce-ts", + .pm = &zforce_pm_ops, + }, + .probe = zforce_probe, + .id_table = zforce_idtable, +}; + +module_i2c_driver(zforce_driver); + +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("zForce TouchScreen Driver"); +MODULE_LICENSE("GPL"); |