From 598d3b707979847d96bc4bef455ac3a0e1ec45bd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 19 Mar 2012 17:46:58 -0700 Subject: [PATCH 01/16] Input: spear-keyboard - remove kbd_set_plat_data() We must use platform_device_add_data() instead of kbd_set_plat_data() so let's remove it. Signed-off-by: Viresh Kumar Signed-off-by: Dmitry Torokhov --- arch/arm/plat-spear/include/plat/keyboard.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/plat-spear/include/plat/keyboard.h b/arch/arm/plat-spear/include/plat/keyboard.h index c16cc31ecbe..0562f134621 100644 --- a/arch/arm/plat-spear/include/plat/keyboard.h +++ b/arch/arm/plat-spear/include/plat/keyboard.h @@ -159,11 +159,4 @@ struct kbd_platform_data { unsigned int mode; }; -/* This function is used to set platform data field of pdev->dev */ -static inline void -kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data) -{ - pdev->dev.platform_data = data; -} - #endif /* __PLAT_KEYBOARD_H */ From d9080921aa32c70a95476ce387e973787b892591 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Mar 2012 23:36:29 -0700 Subject: [PATCH 02/16] Input: gpio_keys - constify platform data The platform data should not be altered and therefore should be accessed through const pointers. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 31 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ed1ed469d08..19887fc1028 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -30,7 +30,7 @@ #include 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; @@ -322,7 +322,7 @@ static struct attribute_group gpio_keys_attr_group = { static void gpio_keys_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; @@ -354,7 +354,7 @@ static void gpio_keys_timer(unsigned long _data) static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; - struct gpio_keys_button *button = bdata->button; + const struct gpio_keys_button *button = bdata->button; BUG_ON(irq != gpio_to_irq(button->gpio)); @@ -368,8 +368,9 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) } 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; @@ -378,6 +379,8 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); INIT_WORK(&bdata->work, gpio_keys_work_func); + bdata->input = input; + bdata->button = button; error = gpio_request(button->gpio, desc); if (error < 0) { @@ -425,6 +428,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, goto fail3; } + input_set_capability(input, button->type ?: EV_KEY, button->code); return 0; fail3: @@ -549,7 +553,7 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, 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 +603,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); @@ -699,11 +697,12 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + const struct gpio_keys_button *button; int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->n_buttons; i++) { - struct gpio_keys_button *button = ddata->data[i].button; + button = ddata->data[i].button; if (button->wakeup) { int irq = gpio_to_irq(button->gpio); enable_irq_wake(irq); @@ -717,11 +716,11 @@ static int gpio_keys_suspend(struct device *dev) static int gpio_keys_resume(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); + const struct gpio_keys_button *button; int i; for (i = 0; i < ddata->n_buttons; i++) { - - struct gpio_keys_button *button = ddata->data[i].button; + button = ddata->data[i].button; if (button->wakeup && device_may_wakeup(dev)) { int irq = gpio_to_irq(button->gpio); disable_irq_wake(irq); From 6709c9a5d8c53092cbe89128df4e0a549e93133b Mon Sep 17 00:00:00 2001 From: David Jander Date: Sun, 18 Mar 2012 23:36:29 -0700 Subject: [PATCH 03/16] Input: revert "gpio_keys - switch to using threaded IRQs" request_any_context_irq() should handle the case when using GPIO expanders that themselves use threaded IRQs, and so the premise of change 7e2ecdf438bb479e2b4667fc16b1a84d6348da04 is incorrect. Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 19887fc1028..6f067587cdf 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -421,7 +421,7 @@ 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(irq, gpio_keys_isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", irq, error); From a16ca23935afc0d72215b139720bd07df3162a9f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 18 Mar 2012 23:36:30 -0700 Subject: [PATCH 04/16] Input: gpio_keys - consolidate key destructor code Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6f067587cdf..8f44f7b8c94 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -195,7 +195,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, @@ -551,6 +551,15 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, #endif +static void gpio_remove_key(struct gpio_button_data *bdata) +{ + free_irq(gpio_to_irq(bdata->button->gpio), bdata); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + cancel_work_sync(&bdata->work); + gpio_free(bdata->button->gpio); +} + static int __devinit gpio_keys_probe(struct platform_device *pdev) { const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; @@ -640,13 +649,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: @@ -669,14 +673,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); From d8ee4a1c90529ed06e1aa43d034986649f7b670b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 19 Mar 2012 17:54:31 -0700 Subject: [PATCH 05/16] Input: gpio_keys - add support for interrupt only keys Some of buttons, like power-on key or onkey, may only generate interrupts when pressed and not actually be mapped as gpio in the system. Allow setting gpio to invalid value and specify IRQ instead to support such keys. The debounce timer is used not to debounce but to ignore new IRQs coming while button is kept pressed. Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 218 ++++++++++++++++++++--------- include/linux/gpio_keys.h | 3 +- 2 files changed, 152 insertions(+), 69 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 8f44f7b8c94..62bfce468f9 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -28,14 +28,18 @@ #include #include #include +#include struct gpio_button_data { 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; } } @@ -320,7 +324,7 @@ 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) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; @@ -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; - const 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,6 +370,53 @@ 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, @@ -374,46 +424,79 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, { 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_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "Failed to request GPIO %d, error %d\n", + button->gpio, error); + return error; + } + + 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; + } + + 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; } - 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; - } + input_set_capability(input, button->type ?: EV_KEY, button->code); - 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 fail3; - } - - irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; /* * If platform has specified that the button can be disabled, * we don't want it to share the interrupt line. @@ -421,19 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, if (!button->can_disable) irqflags |= IRQF_SHARED; - error = request_any_context_irq(irq, 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; } - input_set_capability(input, button->type ?: EV_KEY, button->code); return 0; -fail3: - gpio_free(button->gpio); -fail2: +fail: + if (gpio_is_valid(button->gpio)) + gpio_free(button->gpio); + return error; } @@ -553,11 +636,12 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, static void gpio_remove_key(struct gpio_button_data *bdata) { - free_irq(gpio_to_irq(bdata->button->gpio), bdata); + free_irq(bdata->irq, bdata); if (bdata->timer_debounce) del_timer_sync(&bdata->timer); cancel_work_sync(&bdata->work); - gpio_free(bdata->button->gpio); + if (gpio_is_valid(bdata->button->gpio)) + gpio_free(bdata->button->gpio); } static int __devinit gpio_keys_probe(struct platform_device *pdev) @@ -637,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); @@ -695,16 +782,13 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); - const struct gpio_keys_button *button; int i; if (device_may_wakeup(dev)) { for (i = 0; i < ddata->n_buttons; i++) { - 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); } } @@ -714,17 +798,15 @@ static int gpio_keys_suspend(struct device *dev) static int gpio_keys_resume(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); - const struct gpio_keys_button *button; int i; for (i = 0; i < ddata->n_buttons; i++) { - button = ddata->data[i].button; - if (button->wakeup && device_may_wakeup(dev)) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); - } + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup && device_may_wakeup(dev)) + disable_irq_wake(bdata->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/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index 004ff33ab38..a7e977ff4ab 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -6,7 +6,7 @@ struct device; struct gpio_keys_button { /* Configuration parameters */ unsigned int code; /* input event code (KEY_*, SW_*) */ - int gpio; + int gpio; /* -1 if this key does not support gpio */ int active_low; const char *desc; unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ @@ -14,6 +14,7 @@ struct gpio_keys_button { int debounce_interval; /* debounce ticks interval in msecs */ bool can_disable; int value; /* axis value for EV_ABS */ + unsigned int irq; /* Irq number in case of interrupt keys */ }; struct gpio_keys_platform_data { From 3ac1780f9e6ed212e56d4132e997551297a97112 Mon Sep 17 00:00:00 2001 From: Tai-hwa Liang Date: Sun, 25 Mar 2012 17:15:03 -0700 Subject: [PATCH 06/16] Input: sentelic - refactor code for upcoming new hardware support - Move event bits setup code into a separate function, fsp_set_input_params(), so that we can perform hardware-specific settings in the future; - Take hardware version information into account when activating protocol; - Remove button information from boot message as it's somewhat confusing and is only for internal processing. While there, also move button retrieval code to be a part of protocol activation process. Signed-off-by: Tai-hwa Liang Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/sentelic.c | 147 +++++++++++++++++++-------------- drivers/input/mouse/sentelic.h | 13 ++- 2 files changed, 96 insertions(+), 64 deletions(-) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 2a77a52d2e6..62fa6982567 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -128,8 +128,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 +180,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 +239,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 +277,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 +323,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 +340,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; } @@ -657,8 +660,8 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) 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"); + psmouse_warn(psmouse, + "Unexpected absolute mode packet, ignored.\n"); break; case FSP_PKT_TYPE_NORMAL_OPC: @@ -736,43 +739,70 @@ 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; + } + + if (fsp_get_buttons(psmouse, &pad->buttons)) { + psmouse_err(psmouse, + "Unable to retrieve number of buttons.\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); } - 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; + return 0; +} + +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); } - 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; - } - - /* - * 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"); - - /* Enable on-pad vertical and horizontal scrolling */ - fsp_onpad_vscr(psmouse, true); - fsp_onpad_hscr(psmouse, true); - return 0; } @@ -829,18 +859,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 +876,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 +883,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 2e4af24f8c1..23bd25e539b 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -64,12 +64,23 @@ #define FSP_PKT_TYPE_NORMAL_OPC (0x03) #define FSP_PKT_TYPE_SHIFT (6) +/* 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 */ From a4c85075f00d56b38f5c277ab89f9aaad69eb17b Mon Sep 17 00:00:00 2001 From: Tai-hwa Liang Date: Sun, 25 Mar 2012 17:16:36 -0700 Subject: [PATCH 07/16] Input: sentelic - enabling absolute coordinates output for newer hardware - Hooking multi-finger coordinates output with kernel multitouch library; - Enabling absolute coordinates output for Cx+ hardware. The older hardware performs much better in relative mode; thus relative mode related code are preserved. Part of the code is based on the work done by Oskari Saarenmaa , which was used to support the clickpad found on ASUS UX21/31 Ultrabook. On the other hand, the FSP found on UX21/31 doesn't have hardware capability register other than PnP ID, which means that we'll have to figure out an alternative approach to identify such pad correctly; otherwise, blindly adding INPUT_PROP_BUTTONPAD property may compatability issues amongst existing FSPs. Signed-off-by: Tai-hwa Liang Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/sentelic.c | 114 ++++++++++++++++++++++++++++++++- drivers/input/mouse/sentelic.h | 22 ++++++- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 62fa6982567..5ea5c689d26 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 #include +#include #include #include #include @@ -643,12 +644,24 @@ static void fsp_packet_debug(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) @@ -660,8 +673,66 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { case FSP_PKT_TYPE_ABS: - psmouse_warn(psmouse, - "Unexpected absolute mode packet, ignored.\n"); + abs_x = (packet[1] << 2) | ((packet[3] >> 2) & 0x03); + abs_y = (packet[2] << 2) | (packet[3] & 0x03); + + 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: @@ -785,6 +856,17 @@ static int fsp_activate_protocol(struct psmouse *psmouse) /* 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; + } } return 0; @@ -801,6 +883,32 @@ static int fsp_set_input_params(struct psmouse *psmouse) __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; diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index 23bd25e539b..334de19e5dd 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,6 +74,15 @@ #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) @@ -89,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 From 7b85f73d0461188aa397d428e6c53419ebfd86b4 Mon Sep 17 00:00:00 2001 From: Tai-hwa Liang Date: Sun, 25 Mar 2012 17:17:00 -0700 Subject: [PATCH 08/16] Input: sentelic - minor code cleanup Improve code readability by converting yet another magic number into a pre-defined constant. Signed-off-by: Tai-hwa Liang Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/sentelic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 5ea5c689d26..d6bc5a0b286 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -738,7 +738,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) 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: From 727f9b480754dfcb82e36d431e85984893011b79 Mon Sep 17 00:00:00 2001 From: Oskari Saarenmaa Date: Sun, 25 Mar 2012 17:17:27 -0700 Subject: [PATCH 09/16] Input: sentelic - improve packet debugging information Signed-off-by: Oskari Saarenmaa Signed-off-by: Tai-hwa Liang Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/sentelic.c | 43 +++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index d6bc5a0b286..a977bfaa682 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -37,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"; @@ -619,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); @@ -639,7 +664,7 @@ 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 @@ -671,10 +696,12 @@ 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: - abs_x = (packet[1] << 2) | ((packet[3] >> 2) & 0x03); - abs_y = (packet[2] << 2) | (packet[3] & 0x03); + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); if (packet[0] & FSP_PB0_MFMC) { /* @@ -785,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) input_sync(dev); - fsp_packet_debug(packet); - return PSMOUSE_FULL_PACKET; } From 3aac0ef10bf5c76ba4262cfd9b044a6c067d5aae Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 25 Mar 2012 23:25:45 -0700 Subject: [PATCH 10/16] Input: wacom - isolate input registration Although this better co-locates input registration logic, the main goal is to make it easier to optionally create input devices or delay creation to later time periods. Signed-off-by: Chris Bagwell Tested-by: Jason Gerecke Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 53 ++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 39205eddf71..f6da2f8a9c1 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -822,6 +822,37 @@ static void wacom_destroy_leds(struct wacom *wacom) } } +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 int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -829,18 +860,12 @@ 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; - } wacom_wac = &wacom->wacom_wac; wacom_wac->features = *((struct wacom_features *)id->driver_info); @@ -869,8 +894,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i 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 +917,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,7 +928,7 @@ 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_register_input(wacom); if (error) goto fail5; @@ -928,8 +942,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i 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; } From d3825d51c3eddb8a3c7d1281f27181aff6db19b8 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 25 Mar 2012 23:26:11 -0700 Subject: [PATCH 11/16] Input: wacom - wireless monitor framework The 3rd gen Bamboo Pen & Touch and Intuos5 tablets support an optional wireless module. When its receiver is plugged into USB, it presents 3 interfaces: 0) Monitor 1) Pen and 2) Touch. The exact capabilities of the Pen and Touch interfaces can not be determined until a tablet connection is established and reported over the Monitor interface. This patch detects this wireless receiver and enables interrupt packets to be processed for the Monitor interface. Processing the data in packets will be left to another patch. Since it doesn't make sense to create an input device for the Monitor interface, it is not created. Creation of Pen and Touch input device is also delayed until monitor packets can be processed. Signed-off-by: Chris Bagwell Tested-by: Jason Gerecke Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 36 +++++++++++++++++++++++++++----- drivers/input/tablet/wacom_wac.c | 28 ++++++++++++++++++++++++- drivers/input/tablet/wacom_wac.h | 4 ++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index f6da2f8a9c1..d8cc9ce165f 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -422,6 +422,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 +455,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)) @@ -928,14 +944,22 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail4; - error = wacom_register_input(wacom); - if (error) - goto fail5; + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_register_input(wacom); + if (error) + goto fail5; + } /* 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; fail5: wacom_destroy_leds(wacom); @@ -953,7 +977,8 @@ 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); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); wacom_destroy_leds(wacom); usb_free_urb(wacom->irq); usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, @@ -985,7 +1010,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 07a1f218b5c..6264e6a8d51 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1044,6 +1044,14 @@ 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) +{ + if (len != WACOM_PKGLEN_WIRELESS) + return 0; + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -1094,6 +1102,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 +1167,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 +1179,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 +1662,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 +1791,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 4f0ba21b019..2c04b6248a5 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, From 16bf288c4be67b68c3fcb6561ff145702cb7bd22 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 25 Mar 2012 23:26:20 -0700 Subject: [PATCH 12/16] Input: wacom - create inputs when wireless connect When a tablet connect or disconnect is detected, schedule work queue to register or unregister related input devices. When a wireless tablet connects, it reports same USB PID used if tablet is connected with USB cable. Use this to update features values, set input capabilities, and then register device. From there, the Pen and Touch interfaces will reuse the existing tablet's IRQ routines. Its possible that 1 receiver is shared with 2 tablets with different PID (small and medium Bamboo for example) so the input is unregister at disconnect to better support this case. Signed-off-by: Chris Bagwell Tested-by: Jason Gerecke Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom.h | 7 +++ drivers/input/tablet/wacom_sys.c | 91 ++++++++++++++++++++++++++++---- drivers/input/tablet/wacom_wac.c | 20 ++++++- drivers/input/tablet/wacom_wac.h | 1 + 4 files changed, 109 insertions(+), 10 deletions(-) diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 0783864a7dc..febbfd9f3a8 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -112,6 +112,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 { @@ -122,6 +123,12 @@ struct wacom { } led; }; +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 d8cc9ce165f..2fc77053960 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 * features->x_resolution) / 100; - features->y_phy = - (features->y_max * features->y_resolution) / 100; + wacom_set_phy_from_res(features); features->x_max = features->y_max = get_unaligned_le16(&report[10]); @@ -869,6 +874,72 @@ static int wacom_register_input(struct wacom *wacom) 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); @@ -907,6 +978,7 @@ 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)); @@ -977,6 +1049,7 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(wacom->irq); + cancel_work_sync(&wacom->work); if (wacom->wacom_wac.input) input_unregister_device(wacom->wacom_wac.input); wacom_destroy_leds(wacom); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6264e6a8d51..fce7a09fb5d 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1046,9 +1046,27 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) { - if (len != WACOM_PKGLEN_WIRELESS) + 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; + + pid = get_unaligned_be16(&data[6]); + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + } + return 0; } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 2c04b6248a5..cffaf6b7e6e 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -111,6 +111,7 @@ struct wacom_wac { struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; + int pid; }; #endif From a1d552cc15b0be9124ccba593f99f59c4ec1e153 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 25 Mar 2012 23:26:30 -0700 Subject: [PATCH 13/16] Input: wacom - wireless battery status Signed-off-by: Chris Bagwell Tested-by: Jason Gerecke Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/Kconfig | 1 + drivers/input/tablet/wacom.h | 2 ++ drivers/input/tablet/wacom_sys.c | 57 +++++++++++++++++++++++++++++++- drivers/input/tablet/wacom_wac.c | 5 ++- drivers/input/tablet/wacom_wac.h | 1 + 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index 58a87755b93..0edeb949109 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 help Say Y here if you want to use the USB version of the Wacom Intuos diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index febbfd9f3a8..b4842d0e61d 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -88,6 +88,7 @@ #include #include #include +#include #include /* @@ -121,6 +122,7 @@ 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) diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 2fc77053960..19ba58640dc 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -843,6 +843,55 @@ 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; @@ -1016,10 +1065,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail4; + error = wacom_initialize_battery(wacom); + if (error) + goto fail5; + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { error = wacom_register_input(wacom); if (error) - goto fail5; + goto fail6; } /* Note that if query fails it is not a hard failure */ @@ -1034,6 +1087,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i 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); @@ -1052,6 +1106,7 @@ static void wacom_disconnect(struct usb_interface *intf) 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, diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index fce7a09fb5d..99fb6fed2bf 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1054,17 +1054,20 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) connected = data[1] & 0x01; if (connected) { - int pid; + 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; diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index cffaf6b7e6e..ba5a334e54d 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -112,6 +112,7 @@ struct wacom_wac { struct wacom_shared *shared; struct input_dev *input; int pid; + int battery_capacity; }; #endif From 3183968cbfb76c02f357151f66a1260101f9d82c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 25 Mar 2012 23:29:30 -0700 Subject: [PATCH 14/16] Input: amijoy - add missing platform check On multi-platform kernels, the Amiga joystick driver may be initialized when running on Amiga only. Else it may crash later. Fortunately this driver is almost always compiled as a module (to avoid conflicts with the mouse driver), so it needs an explicit insmod to trigger a crash. Signed-off-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/amijoy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index 0bc86204213..3aa93bf60ae 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -108,6 +108,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; From 131c713fd96e251fcb18f4d2d05f263aa41bb11f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 26 Mar 2012 23:55:24 -0700 Subject: [PATCH 15/16] Input: tegra-kbc - allocate pdata before using it Following commit broke DT support for tegra-kbc by removing pdata allocation completely: commit 023cea0ecfa2df034096c3f4afa796a0b2d1188a Author: Shridhar Rasal Date: Fri Feb 3 00:27:30 2012 -0800 Input: tegra-kbc - allow skipping setting up some of GPIO pins This patch restores it. Signed-off-by: Viresh Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 21c42f85234..fe4ac95ca6c 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; From f182394033d639679264d61e6dca62761e659ff7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 29 Mar 2012 22:38:11 -0700 Subject: [PATCH 16/16] Input: wacom - check for allocation failure in probe() We accidentally removed the check for NULL in 3aac0ef10b "Input: wacom - isolate input registration". Signed-off-by: Dan Carpenter Reviewed-by: Chris Bagwell Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 19ba58640dc..0d269212931 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -1002,6 +1002,8 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i return -EINVAL; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + if (!wacom) + return -ENOMEM; wacom_wac = &wacom->wacom_wac; wacom_wac->features = *((struct wacom_features *)id->driver_info);