diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-30 10:17:44 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-30 10:17:44 +0400 |
commit | 2f7fa1be66dce77608330c5eb918d6360b5525f2 (patch) | |
tree | 05dbfe86d2b192389da11686fa380e7ae45be225 /drivers/input | |
parent | 1338631433d8de7cf85b7e8fcf1a389ad89e7a8f (diff) | |
parent | f182394033d639679264d61e6dca62761e659ff7 (diff) | |
download | linux-2f7fa1be66dce77608330c5eb918d6360b5525f2.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull 2nd round of input updates from Dmitry Torokhov:
- update to Wacom driver to support wireless devices
- update to Sentelci touchpad driver to support newer hardware
- update to gpio-keys driver to support "interrupt-only" keys
- fixups to earlier commits
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
Input: wacom - check for allocation failure in probe()
Input: tegra-kbc - allocate pdata before using it
Input: amijoy - add missing platform check
Input: wacom - wireless battery status
Input: wacom - create inputs when wireless connect
Input: wacom - wireless monitor framework
Input: wacom - isolate input registration
Input: sentelic - improve packet debugging information
Input: sentelic - minor code cleanup
Input: sentelic - enabling absolute coordinates output for newer hardware
Input: sentelic - refactor code for upcoming new hardware support
Input: gpio_keys - add support for interrupt only keys
Input: gpio_keys - consolidate key destructor code
Input: revert "gpio_keys - switch to using threaded IRQs"
Input: gpio_keys - constify platform data
Input: spear-keyboard - remove kbd_set_plat_data()
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/joystick/amijoy.c | 3 | ||||
-rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 259 | ||||
-rw-r--r-- | drivers/input/keyboard/tegra-kbc.c | 1 | ||||
-rw-r--r-- | drivers/input/mouse/sentelic.c | 294 | ||||
-rw-r--r-- | drivers/input/mouse/sentelic.h | 35 | ||||
-rw-r--r-- | drivers/input/tablet/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/tablet/wacom.h | 9 | ||||
-rw-r--r-- | drivers/input/tablet/wacom_sys.c | 231 | ||||
-rw-r--r-- | drivers/input/tablet/wacom_wac.c | 49 | ||||
-rw-r--r-- | drivers/input/tablet/wacom_wac.h | 6 |
10 files changed, 694 insertions, 194 deletions
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index 24044dacbf70..c65b5fa69f1e 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -107,6 +107,9 @@ static int __init amijoy_init(void) int i, j; int err; + if (!MACH_IS_AMIGA) + return -ENODEV; + for (i = 0; i < 2; i++) { if (!amijoy[i]) continue; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ed1ed469d085..62bfce468f9f 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -28,14 +28,18 @@ #include <linux/gpio.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <linux/spinlock.h> struct gpio_button_data { - struct gpio_keys_button *button; + const struct gpio_keys_button *button; struct input_dev *input; struct timer_list timer; struct work_struct work; - int timer_debounce; /* in msecs */ + unsigned int timer_debounce; /* in msecs */ + unsigned int irq; + spinlock_t lock; bool disabled; + bool key_pressed; }; struct gpio_keys_drvdata { @@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) /* * Disable IRQ and possible debouncing timer. */ - disable_irq(gpio_to_irq(bdata->button->gpio)); + disable_irq(bdata->irq); if (bdata->timer_debounce) del_timer_sync(&bdata->timer); @@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) static void gpio_keys_enable_button(struct gpio_button_data *bdata) { if (bdata->disabled) { - enable_irq(gpio_to_irq(bdata->button->gpio)); + enable_irq(bdata->irq); bdata->disabled = false; } } @@ -195,7 +199,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, * @type: button type (%EV_KEY, %EV_SW) * * This function parses stringified bitmap from @buf and disables/enables - * GPIO buttons accordinly. Returns 0 on success and negative error + * GPIO buttons accordingly. Returns 0 on success and negative error * on failure. */ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, @@ -320,9 +324,9 @@ static struct attribute_group gpio_keys_attr_group = { .attrs = gpio_keys_attrs, }; -static void gpio_keys_report_event(struct gpio_button_data *bdata) +static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { - struct gpio_keys_button *button = bdata->button; + const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; @@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) input_sync(input); } -static void gpio_keys_work_func(struct work_struct *work) +static void gpio_keys_gpio_work_func(struct work_struct *work) { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work); - gpio_keys_report_event(bdata); + gpio_keys_gpio_report_event(bdata); } -static void gpio_keys_timer(unsigned long _data) +static void gpio_keys_gpio_timer(unsigned long _data) { - struct gpio_button_data *data = (struct gpio_button_data *)_data; + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; - schedule_work(&data->work); + schedule_work(&bdata->work); } -static irqreturn_t gpio_keys_isr(int irq, void *dev_id) +static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; - struct gpio_keys_button *button = bdata->button; - BUG_ON(irq != gpio_to_irq(button->gpio)); + BUG_ON(irq != bdata->irq); if (bdata->timer_debounce) mod_timer(&bdata->timer, @@ -367,50 +370,133 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void gpio_keys_irq_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + struct input_dev *input = bdata->input; + unsigned long flags; + + spin_lock_irqsave(&bdata->lock, flags); + if (bdata->key_pressed) { + input_event(input, EV_KEY, bdata->button->code, 0); + input_sync(input); + bdata->key_pressed = false; + } + spin_unlock_irqrestore(&bdata->lock, flags); +} + +static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned long flags; + + BUG_ON(irq != bdata->irq); + + spin_lock_irqsave(&bdata->lock, flags); + + if (!bdata->key_pressed) { + input_event(input, EV_KEY, button->code, 1); + input_sync(input); + + if (!bdata->timer_debounce) { + input_event(input, EV_KEY, button->code, 0); + input_sync(input); + goto out; + } + + bdata->key_pressed = true; + } + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); +out: + spin_unlock_irqrestore(&bdata->lock, flags); + return IRQ_HANDLED; +} + static int __devinit gpio_keys_setup_key(struct platform_device *pdev, + struct input_dev *input, struct gpio_button_data *bdata, - struct gpio_keys_button *button) + const struct gpio_keys_button *button) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; + irq_handler_t isr; unsigned long irqflags; int irq, error; - setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); - INIT_WORK(&bdata->work, gpio_keys_work_func); + bdata->input = input; + bdata->button = button; + spin_lock_init(&bdata->lock); - error = gpio_request(button->gpio, desc); - if (error < 0) { - dev_err(dev, "failed to request GPIO %d, error %d\n", - button->gpio, error); - goto fail2; - } + if (gpio_is_valid(button->gpio)) { - error = gpio_direction_input(button->gpio); - if (error < 0) { - dev_err(dev, "failed to configure" - " direction for GPIO %d, error %d\n", - button->gpio, error); - goto fail3; - } + error = gpio_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "Failed to request GPIO %d, error %d\n", + button->gpio, error); + return error; + } - if (button->debounce_interval) { - error = gpio_set_debounce(button->gpio, - button->debounce_interval * 1000); - /* use timer if gpiolib doesn't provide debounce */ - if (error < 0) - bdata->timer_debounce = button->debounce_interval; - } + error = gpio_direction_input(button->gpio); + if (error < 0) { + dev_err(dev, + "Failed to configure direction for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } - irq = gpio_to_irq(button->gpio); - if (irq < 0) { - error = irq; - dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", - button->gpio, error); - goto fail3; + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = + button->debounce_interval; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, + "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + bdata->irq = irq; + + INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); + setup_timer(&bdata->timer, + gpio_keys_gpio_timer, (unsigned long)bdata); + + isr = gpio_keys_gpio_isr; + irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + } else { + if (!button->irq) { + dev_err(dev, "No IRQ specified\n"); + return -EINVAL; + } + bdata->irq = button->irq; + + if (button->type && button->type != EV_KEY) { + dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); + return -EINVAL; + } + + bdata->timer_debounce = button->debounce_interval; + setup_timer(&bdata->timer, + gpio_keys_irq_timer, (unsigned long)bdata); + + isr = gpio_keys_irq_isr; + irqflags = 0; } - irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + input_set_capability(input, button->type ?: EV_KEY, button->code); + /* * If platform has specified that the button can be disabled, * we don't want it to share the interrupt line. @@ -418,18 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, if (!button->can_disable) irqflags |= IRQF_SHARED; - error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata); + error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", - irq, error); - goto fail3; + bdata->irq, error); + goto fail; } return 0; -fail3: - gpio_free(button->gpio); -fail2: +fail: + if (gpio_is_valid(button->gpio)) + gpio_free(button->gpio); + return error; } @@ -547,9 +634,19 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, #endif +static void gpio_remove_key(struct gpio_button_data *bdata) +{ + free_irq(bdata->irq, bdata); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + cancel_work_sync(&bdata->work); + if (gpio_is_valid(bdata->button->gpio)) + gpio_free(bdata->button->gpio); +} + static int __devinit gpio_keys_probe(struct platform_device *pdev) { - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct gpio_keys_platform_data alt_pdata; @@ -599,21 +696,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - unsigned int type = button->type ?: EV_KEY; - - bdata->input = input; - bdata->button = button; - error = gpio_keys_setup_key(pdev, bdata, button); + error = gpio_keys_setup_key(pdev, input, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; - - input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -630,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) goto fail3; } - /* get current state of buttons */ - for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_report_event(&ddata->data[i]); + /* get current state of buttons that are connected to GPIOs */ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } input_sync(input); device_init_wakeup(&pdev->dev, wakeup); @@ -642,13 +736,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) fail3: sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); fail2: - while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); - if (ddata->data[i].timer_debounce) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(pdata->buttons[i].gpio); - } + while (--i >= 0) + gpio_remove_key(&ddata->data[i]); platform_set_drvdata(pdev, NULL); fail1: @@ -671,14 +760,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); - for (i = 0; i < ddata->n_buttons; i++) { - int irq = gpio_to_irq(ddata->data[i].button->gpio); - free_irq(irq, &ddata->data[i]); - if (ddata->data[i].timer_debounce) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(ddata->data[i].button->gpio); - } + for (i = 0; i < ddata->n_buttons; i++) + gpio_remove_key(&ddata->data[i]); input_unregister_device(input); @@ -703,11 +786,9 @@ static int gpio_keys_suspend(struct device *dev) if (device_may_wakeup(dev)) { for (i = 0; i < ddata->n_buttons; i++) { - struct gpio_keys_button *button = ddata->data[i].button; - if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); - enable_irq_wake(irq); - } + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup) + enable_irq_wake(bdata->irq); } } @@ -720,14 +801,12 @@ static int gpio_keys_resume(struct device *dev) int i; for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup && device_may_wakeup(dev)) + disable_irq_wake(bdata->irq); - struct gpio_keys_button *button = ddata->data[i].button; - if (button->wakeup && device_may_wakeup(dev)) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); - } - - gpio_keys_report_event(&ddata->data[i]); + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); } input_sync(ddata->input); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 21c42f852343..fe4ac95ca6c8 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -630,6 +630,7 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) if (!np) return NULL; + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 2a77a52d2e62..a977bfaa6821 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -2,7 +2,7 @@ * Finger Sensing Pad PS/2 mouse driver. * * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/ctype.h> #include <linux/libps2.h> #include <linux/serio.h> @@ -36,6 +37,9 @@ #define FSP_CMD_TIMEOUT 200 #define FSP_CMD_TIMEOUT2 30 +#define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03)) +#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03)) + /** Driver version. */ static const char fsp_drv_ver[] = "1.0.0-K"; @@ -128,8 +132,9 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) out: ps2_end_command(ps2dev); psmouse_activate(psmouse); - dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", - reg_addr, *reg_val, rc); + psmouse_dbg(psmouse, + "READ REG: 0x%02x is 0x%02x (rc = %d)\n", + reg_addr, *reg_val, rc); return rc; } @@ -179,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) out: ps2_end_command(ps2dev); - dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", - reg_addr, reg_val, rc); + psmouse_dbg(psmouse, + "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", + reg_addr, reg_val, rc); return rc; } @@ -237,8 +243,9 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) out: ps2_end_command(ps2dev); psmouse_activate(psmouse); - dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", - *reg_val, rc); + psmouse_dbg(psmouse, + "READ PAGE REG: 0x%02x (rc = %d)\n", + *reg_val, rc); return rc; } @@ -274,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) out: ps2_end_command(ps2dev); - dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", - reg_val, rc); + psmouse_dbg(psmouse, + "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); return rc; } @@ -319,7 +327,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) int res = 0; if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { - dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); + psmouse_err(psmouse, "Unable get OPC state.\n"); return -EIO; } @@ -336,8 +344,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) } if (res != 0) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to enable OPC tag.\n"); + psmouse_err(psmouse, "Unable to enable OPC tag.\n"); res = -EIO; } @@ -615,18 +622,40 @@ static struct attribute_group fsp_attribute_group = { .attrs = fsp_attributes, }; -#ifdef FSP_DEBUG -static void fsp_packet_debug(unsigned char packet[]) +#ifdef FSP_DEBUG +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) { static unsigned int ps2_packet_cnt; static unsigned int ps2_last_second; unsigned int jiffies_msec; + const char *packet_type = "UNKNOWN"; + unsigned short abs_x = 0, abs_y = 0; + + /* Interpret & dump the packet data. */ + switch (packet[0] >> FSP_PKT_TYPE_SHIFT) { + case FSP_PKT_TYPE_ABS: + packet_type = "Absolute"; + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + break; + case FSP_PKT_TYPE_NORMAL: + packet_type = "Normal"; + break; + case FSP_PKT_TYPE_NOTIFY: + packet_type = "Notify"; + break; + case FSP_PKT_TYPE_NORMAL_OPC: + packet_type = "Normal-OPC"; + break; + } ps2_packet_cnt++; jiffies_msec = jiffies_to_msecs(jiffies); psmouse_dbg(psmouse, - "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", - jiffies_msec, packet[0], packet[1], packet[2], packet[3]); + "%08dms %s packets: %02x, %02x, %02x, %02x; " + "abs_x: %d, abs_y: %d\n", + jiffies_msec, packet_type, + packet[0], packet[1], packet[2], packet[3], abs_x, abs_y); if (jiffies_msec - ps2_last_second > 1000) { psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt); @@ -635,17 +664,29 @@ static void fsp_packet_debug(unsigned char packet[]) } } #else -static void fsp_packet_debug(unsigned char packet[]) +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) { } #endif +static void fsp_set_slot(struct input_dev *dev, int slot, bool active, + unsigned int x, unsigned int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct fsp_data *ad = psmouse->private; unsigned char *packet = psmouse->packet; unsigned char button_status = 0, lscroll = 0, rscroll = 0; + unsigned short abs_x, abs_y, fgrs = 0; int rel_x, rel_y; if (psmouse->pktcnt < 4) @@ -655,16 +696,76 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) * Full packet accumulated, process it */ + fsp_packet_debug(psmouse, packet); + switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { case FSP_PKT_TYPE_ABS: - dev_warn(&psmouse->ps2dev.serio->dev, - "Unexpected absolute mode packet, ignored.\n"); + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + + if (packet[0] & FSP_PB0_MFMC) { + /* + * MFMC packet: assume that there are two fingers on + * pad + */ + fgrs = 2; + + /* MFMC packet */ + if (packet[0] & FSP_PB0_MFMC_FGR2) { + /* 2nd finger */ + if (ad->last_mt_fgr == 2) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 1st finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 0, false, 0, 0); + } + ad->last_mt_fgr = 2; + + fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y); + } else { + /* 1st finger */ + if (ad->last_mt_fgr == 1) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 2nd finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 1, false, 0, 0); + } + ad->last_mt_fgr = 1; + fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y); + } + } else { + /* SFAC packet */ + + /* no multi-finger information */ + ad->last_mt_fgr = 0; + + if (abs_x != 0 && abs_y != 0) + fgrs = 1; + + fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y); + fsp_set_slot(dev, 1, false, 0, 0); + } + if (fgrs > 0) { + input_report_abs(dev, ABS_X, abs_x); + input_report_abs(dev, ABS_Y, abs_y); + } + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_TOUCH, fgrs); + input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2); break; case FSP_PKT_TYPE_NORMAL_OPC: /* on-pad click, filter it if necessary */ if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) - packet[0] &= ~BIT(0); + packet[0] &= ~FSP_PB0_LBTN; /* fall through */ case FSP_PKT_TYPE_NORMAL: @@ -711,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) input_sync(dev); - fsp_packet_debug(packet); - return PSMOUSE_FULL_PACKET; } @@ -736,42 +835,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse) ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 0x04) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to enable 4 bytes packet format.\n"); + psmouse_err(psmouse, + "Unable to enable 4 bytes packet format.\n"); return -EIO; } - if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to read SYSCTL5 register.\n"); - return -EIO; - } + if (pad->ver < FSP_VER_STL3888_C0) { + /* Preparing relative coordinates output for older hardware */ + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { + psmouse_err(psmouse, + "Unable to read SYSCTL5 register.\n"); + return -EIO; + } - val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); - /* Ensure we are not in absolute mode */ - val &= ~FSP_BIT_EN_PKT_G0; - if (pad->buttons == 0x06) { - /* Left/Middle/Right & Scroll Up/Down/Right/Left */ - val |= FSP_BIT_EN_MSID6; - } + if (fsp_get_buttons(psmouse, &pad->buttons)) { + psmouse_err(psmouse, + "Unable to retrieve number of buttons.\n"); + return -EIO; + } - if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to set up required mode bits.\n"); - return -EIO; + val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); + /* Ensure we are not in absolute mode */ + val &= ~FSP_BIT_EN_PKT_G0; + if (pad->buttons == 0x06) { + /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + val |= FSP_BIT_EN_MSID6; + } + + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { + psmouse_err(psmouse, + "Unable to set up required mode bits.\n"); + return -EIO; + } + + /* + * Enable OPC tags such that driver can tell the difference + * between on-pad and real button click + */ + if (fsp_opc_tag_enable(psmouse, true)) + psmouse_warn(psmouse, + "Failed to enable OPC tag mode.\n"); + /* enable on-pad click by default */ + pad->flags |= FSPDRV_FLAG_EN_OPC; + + /* Enable on-pad vertical and horizontal scrolling */ + fsp_onpad_vscr(psmouse, true); + fsp_onpad_hscr(psmouse, true); + } else { + /* Enable absolute coordinates output for Cx/Dx hardware */ + if (fsp_reg_write(psmouse, FSP_REG_SWC1, + FSP_BIT_SWC1_EN_ABS_1F | + FSP_BIT_SWC1_EN_ABS_2F | + FSP_BIT_SWC1_EN_FUP_OUT | + FSP_BIT_SWC1_EN_ABS_CON)) { + psmouse_err(psmouse, + "Unable to enable absolute coordinates output.\n"); + return -EIO; + } } - /* - * Enable OPC tags such that driver can tell the difference between - * on-pad and real button click - */ - if (fsp_opc_tag_enable(psmouse, true)) - dev_warn(&psmouse->ps2dev.serio->dev, - "Failed to enable OPC tag mode.\n"); + return 0; +} - /* Enable on-pad vertical and horizontal scrolling */ - fsp_onpad_vscr(psmouse, true); - fsp_onpad_hscr(psmouse, true); +static int fsp_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct fsp_data *pad = psmouse->private; + + if (pad->ver < FSP_VER_STL3888_C0) { + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + } else { + /* + * Hardware prior to Cx performs much better in relative mode; + * hence, only enable absolute coordinates output as well as + * multi-touch output for the newer hardware. + * + * Maximum coordinates can be computed as: + * + * number of scanlines * 64 - 57 + * + * where number of X/Y scanline lines are 16/12. + */ + int abs_x = 967, abs_y = 711; + + __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + + input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); + } return 0; } @@ -829,18 +992,16 @@ static int fsp_reconnect(struct psmouse *psmouse) int fsp_init(struct psmouse *psmouse) { struct fsp_data *priv; - int ver, rev, buttons; + int ver, rev; int error; if (fsp_get_version(psmouse, &ver) || - fsp_get_revision(psmouse, &rev) || - fsp_get_buttons(psmouse, &buttons)) { + fsp_get_revision(psmouse, &rev)) { return -ENODEV; } - psmouse_info(psmouse, - "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", - ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); + psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n", + ver >> 4, ver & 0x0F, rev, fsp_drv_ver); psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); if (!priv) @@ -848,17 +1009,6 @@ int fsp_init(struct psmouse *psmouse) priv->ver = ver; priv->rev = rev; - priv->buttons = buttons; - - /* enable on-pad click by default */ - priv->flags |= FSPDRV_FLAG_EN_OPC; - - /* Set up various supported input event bits */ - __set_bit(BTN_MIDDLE, psmouse->dev->keybit); - __set_bit(BTN_BACK, psmouse->dev->keybit); - __set_bit(BTN_FORWARD, psmouse->dev->keybit); - __set_bit(REL_WHEEL, psmouse->dev->relbit); - __set_bit(REL_HWHEEL, psmouse->dev->relbit); psmouse->protocol_handler = fsp_process_byte; psmouse->disconnect = fsp_disconnect; @@ -866,16 +1016,20 @@ int fsp_init(struct psmouse *psmouse) psmouse->cleanup = fsp_reset; psmouse->pktsize = 4; - /* set default packet output based on number of buttons we found */ error = fsp_activate_protocol(psmouse); if (error) goto err_out; + /* Set up various supported input event bits */ + error = fsp_set_input_params(psmouse); + if (error) + goto err_out; + error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &fsp_attribute_group); if (error) { - dev_err(&psmouse->ps2dev.serio->dev, - "Failed to create sysfs attributes (%d)", error); + psmouse_err(psmouse, + "Failed to create sysfs attributes (%d)", error); goto err_out; } diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index 2e4af24f8c15..334de19e5ddb 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -2,7 +2,7 @@ * Finger Sensing Pad PS/2 mouse driver. * * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,6 +55,16 @@ #define FSP_BIT_FIX_HSCR BIT(5) #define FSP_BIT_DRAG_LOCK BIT(6) +#define FSP_REG_SWC1 (0x90) +#define FSP_BIT_SWC1_EN_ABS_1F BIT(0) +#define FSP_BIT_SWC1_EN_GID BIT(1) +#define FSP_BIT_SWC1_EN_ABS_2F BIT(2) +#define FSP_BIT_SWC1_EN_FUP_OUT BIT(3) +#define FSP_BIT_SWC1_EN_ABS_CON BIT(4) +#define FSP_BIT_SWC1_GST_GRP0 BIT(5) +#define FSP_BIT_SWC1_GST_GRP1 BIT(6) +#define FSP_BIT_SWC1_BX_COMPAT BIT(7) + /* Finger-sensing Pad packet formating related definitions */ /* absolute packet type */ @@ -64,12 +74,32 @@ #define FSP_PKT_TYPE_NORMAL_OPC (0x03) #define FSP_PKT_TYPE_SHIFT (6) +/* bit definitions for the first byte of report packet */ +#define FSP_PB0_LBTN BIT(0) +#define FSP_PB0_RBTN BIT(1) +#define FSP_PB0_MBTN BIT(2) +#define FSP_PB0_MFMC_FGR2 FSP_PB0_MBTN +#define FSP_PB0_MUST_SET BIT(3) +#define FSP_PB0_PHY_BTN BIT(4) +#define FSP_PB0_MFMC BIT(5) + +/* hardware revisions */ +#define FSP_VER_STL3888_A4 (0xC1) +#define FSP_VER_STL3888_B0 (0xD0) +#define FSP_VER_STL3888_B1 (0xD1) +#define FSP_VER_STL3888_B2 (0xD2) +#define FSP_VER_STL3888_C0 (0xE0) +#define FSP_VER_STL3888_C1 (0xE1) +#define FSP_VER_STL3888_D0 (0xE2) +#define FSP_VER_STL3888_D1 (0xE3) +#define FSP_VER_STL3888_E0 (0xE4) + #ifdef __KERNEL__ struct fsp_data { unsigned char ver; /* hardware version */ unsigned char rev; /* hardware revison */ - unsigned char buttons; /* Number of buttons */ + unsigned int buttons; /* Number of buttons */ unsigned int flags; #define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ @@ -78,6 +108,7 @@ struct fsp_data { unsigned char last_reg; /* Last register we requested read from */ unsigned char last_val; + unsigned int last_mt_fgr; /* Last seen finger(multitouch) */ }; #ifdef CONFIG_MOUSE_PS2_SENTELIC diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index e53f4081a586..bed7cbf84cfd 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -76,6 +76,7 @@ config TABLET_USB_KBTAB config TABLET_USB_WACOM tristate "Wacom Intuos/Graphire tablet support (USB)" depends on USB_ARCH_HAS_HCD + select POWER_SUPPLY select USB select NEW_LEDS select LEDS_CLASS diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 0783864a7dc2..b4842d0e61dd 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -88,6 +88,7 @@ #include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/usb/input.h> +#include <linux/power_supply.h> #include <asm/unaligned.h> /* @@ -112,6 +113,7 @@ struct wacom { struct urb *irq; struct wacom_wac wacom_wac; struct mutex lock; + struct work_struct work; bool open; char phys[32]; struct wacom_led { @@ -120,8 +122,15 @@ struct wacom { u8 hlv; /* status led brightness button pressed (1..127) */ u8 img_lum; /* OLED matrix display brightness */ } led; + struct power_supply battery; }; +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + schedule_work(&wacom->work); +} + extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index ca28066dc81e..0d269212931e 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev) usb_autopm_put_interface(wacom->intf); } +/* + * Static values for max X/Y and resolution of Pen interface is stored in + * features. This mean physical size of active area can be computed. + * This is useful to do when Pen and Touch have same active area of tablet. + * This means for Touch device, we only need to find max X/Y value and we + * have enough information to compute resolution of touch. + */ +static void wacom_set_phy_from_res(struct wacom_features *features) +{ + features->x_phy = (features->x_max * 100) / features->x_resolution; + features->y_phy = (features->y_max * 100) / features->y_resolution; +} + static int wacom_parse_logical_collection(unsigned char *report, struct wacom_features *features) { @@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report, features->pktlen = WACOM_PKGLEN_BBTOUCH3; features->device_type = BTN_TOOL_FINGER; - /* - * Stylus and Touch have same active area - * so compute physical size based on stylus - * data before its overwritten. - */ - features->x_phy = - (features->x_max * 100) / features->x_resolution; - features->y_phy = - (features->y_max * 100) / features->y_resolution; + wacom_set_phy_from_res(features); features->x_max = features->y_max = get_unaligned_le16(&report[10]); @@ -422,6 +427,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat report_id, rep_data, 4, 1); } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); } else if (features->type != TABLETPC && + features->type != WIRELESS && features->device_type == BTN_TOOL_PEN) { do { rep_data[0] = 2; @@ -454,6 +460,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, features->pressure_fuzz = 0; features->distance_fuzz = 0; + /* + * The wireless device HID is basic and layout conflicts with + * other tablets (monitor and touch interface can look like pen). + * Skip the query for this type and modify defaults based on + * interface number. + */ + if (features->type == WIRELESS) { + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + features->device_type = 0; + } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) { + features->device_type = BTN_TOOL_DOUBLETAP; + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + } + } + /* only Tablet PCs and Bamboo P&T need to retrieve the info */ if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && (features->type != BAMBOO_PT)) @@ -822,6 +843,152 @@ static void wacom_destroy_leds(struct wacom *wacom) } } +static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY +}; + +static int wacom_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wacom *wacom = container_of(psy, struct wacom, battery); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = + wacom->wacom_wac.battery_capacity * 100 / 31; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wacom_initialize_battery(struct wacom *wacom) +{ + int error = 0; + + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) { + wacom->battery.properties = wacom_battery_props; + wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); + wacom->battery.get_property = wacom_battery_get_property; + wacom->battery.name = "wacom_battery"; + wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wacom->battery.use_for_apm = 0; + + error = power_supply_register(&wacom->usbdev->dev, + &wacom->battery); + } + + return error; +} + +static void wacom_destroy_battery(struct wacom *wacom) +{ + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) + power_supply_unregister(&wacom->battery); +} + +static int wacom_register_input(struct wacom *wacom) +{ + struct input_dev *input_dev; + struct usb_interface *intf = wacom->intf; + struct usb_device *dev = interface_to_usbdev(intf); + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + int error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + 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_wac->input = input_dev; + wacom_setup_input_capabilities(input_dev, wacom_wac); + + error = input_register_device(input_dev); + if (error) { + input_free_device(input_dev); + wacom_wac->input = NULL; + } + + return error; +} + +static void wacom_wireless_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + struct usb_device *usbdev = wacom->usbdev; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + /* + * Regardless if this is a disconnect or a new tablet, + * remove any existing input devices. + */ + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + if (wacom_wac->pid == 0) { + printk(KERN_INFO "wacom: wireless tablet disconnected\n"); + } else { + const struct usb_device_id *id = wacom_ids; + + printk(KERN_INFO + "wacom: wireless tablet connected with PID %x\n", + wacom_wac->pid); + + while (id->match_flags) { + if (id->idVendor == USB_VENDOR_ID_WACOM && + id->idProduct == wacom_wac->pid) + break; + id++; + } + + if (!id->match_flags) { + printk(KERN_INFO + "wacom: ignorning unknown PID.\n"); + return; + } + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.device_type = BTN_TOOL_PEN; + wacom_register_input(wacom); + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac->features.device_type = BTN_TOOL_FINGER; + wacom_set_phy_from_res(&wacom_wac->features); + wacom_wac->features.x_max = wacom_wac->features.y_max = 4096; + wacom_register_input(wacom); + } +} + static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -829,18 +996,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i struct wacom *wacom; struct wacom_wac *wacom_wac; struct wacom_features *features; - struct input_dev *input_dev; int error; if (!id->driver_info) return -EINVAL; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!wacom || !input_dev) { - error = -ENOMEM; - goto fail1; - } + if (!wacom) + return -ENOMEM; wacom_wac = &wacom->wacom_wac; wacom_wac->features = *((struct wacom_features *)id->driver_info); @@ -866,11 +1029,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); + INIT_WORK(&wacom->work, wacom_wireless_work); usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - wacom_wac->input = input_dev; - endpoint = &intf->cur_altsetting->endpoint[0].desc; /* Retrieve the physical and logical size for OEM devices */ @@ -894,15 +1056,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i goto fail3; } - 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_setup_input_capabilities(input_dev, wacom_wac); - usb_fill_int_urb(wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), wacom_wac->data, features->pktlen, @@ -914,22 +1067,34 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail4; - error = input_register_device(input_dev); + error = wacom_initialize_battery(wacom); if (error) goto fail5; + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_register_input(wacom); + if (error) + goto fail6; + } + /* Note that if query fails it is not a hard failure */ wacom_query_tablet_data(intf, features); usb_set_intfdata(intf, wacom); + + if (features->quirks & WACOM_QUIRK_MONITOR) { + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) + goto fail5; + } + return 0; + fail6: wacom_destroy_battery(wacom); fail5: wacom_destroy_leds(wacom); fail4: wacom_remove_shared_data(wacom_wac); fail3: usb_free_urb(wacom->irq); fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); - fail1: input_free_device(input_dev); - kfree(wacom); + fail1: kfree(wacom); return error; } @@ -940,7 +1105,10 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(wacom->irq); - input_unregister_device(wacom->wacom_wac.input); + cancel_work_sync(&wacom->work); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom_destroy_battery(wacom); wacom_destroy_leds(wacom); usb_free_urb(wacom->irq); usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, @@ -972,7 +1140,8 @@ static int wacom_resume(struct usb_interface *intf) wacom_query_tablet_data(intf, features); wacom_led_control(wacom); - if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) + if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR) + && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) rv = -EIO; mutex_unlock(&wacom->lock); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 89a96427faa0..cecd35c8f0b3 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1044,6 +1044,35 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) return 0; } +static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + int connected; + + if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80) + return 0; + + connected = data[1] & 0x01; + if (connected) { + int pid, battery; + + pid = get_unaligned_be16(&data[6]); + battery = data[5] & 0x3f; + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + wacom->battery_capacity = battery; + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + wacom->battery_capacity = 0; + } + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -1094,6 +1123,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_bpt_irq(wacom_wac, len); break; + case WIRELESS: + sync = wacom_wireless_irq(wacom_wac, len); + break; + default: sync = false; break; @@ -1155,7 +1188,7 @@ void wacom_setup_device_quirks(struct wacom_features *features) /* these device have multiple inputs */ if (features->type == TABLETPC || features->type == TABLETPC2FG || - features->type == BAMBOO_PT) + features->type == BAMBOO_PT || features->type == WIRELESS) features->quirks |= WACOM_QUIRK_MULTI_INPUT; /* quirk for bamboo touch with 2 low res touches */ @@ -1167,6 +1200,16 @@ void wacom_setup_device_quirks(struct wacom_features *features) features->y_fuzz <<= 5; features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; } + + if (features->type == WIRELESS) { + + /* monitor never has input and pen/touch have delayed create */ + features->quirks |= WACOM_QUIRK_NO_INPUT; + + /* must be monitor interface if no device_type set */ + if (!features->device_type) + features->quirks |= WACOM_QUIRK_MONITOR; + } } static unsigned int wacom_calculate_touch_res(unsigned int logical_max, @@ -1640,6 +1683,9 @@ static const struct wacom_features wacom_features_0xEC = static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x84 = + { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0, + 0, WIRELESS, 0, 0 }; static const struct wacom_features wacom_features_0xD0 = { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -1766,6 +1812,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, + { USB_DEVICE_WACOM(0x84) }, { USB_DEVICE_WACOM(0xD0) }, { USB_DEVICE_WACOM(0xD1) }, { USB_DEVICE_WACOM(0xD2) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 4f0ba21b0196..ba5a334e54d6 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -24,6 +24,7 @@ #define WACOM_PKGLEN_BBTOUCH 20 #define WACOM_PKGLEN_BBTOUCH3 64 #define WACOM_PKGLEN_BBPEN 10 +#define WACOM_PKGLEN_WIRELESS 32 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -45,6 +46,8 @@ /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 #define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 +#define WACOM_QUIRK_NO_INPUT 0x0004 +#define WACOM_QUIRK_MONITOR 0x0008 enum { PENPARTNER = 0, @@ -54,6 +57,7 @@ enum { PL, DTU, BAMBOO_PT, + WIRELESS, INTUOS, INTUOS3S, INTUOS3, @@ -107,6 +111,8 @@ struct wacom_wac { struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; + int pid; + int battery_capacity; }; #endif |