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()
This commit is contained in:
Коммит
2f7fa1be66
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_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.
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,43 +835,107 @@ 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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
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 +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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче