Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:

 - new driver for PhoenixRC Flight Controller Adapter

 - new driver for RAVE SP Power button

 - fixes for autosuspend-related deadlocks in a few unput USB dirvers

 - support for 2nd wheel in ATech PS/2 mouse

 - fix for ALPS trackpoint detection on Thinkpad L570 and Latitude 7370

 - bunch of cleanups in various in PS/2 protocols

 - other assorted changes and fixes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (35 commits)
  Input: i8042 - enable MUX on Sony VAIO VGN-CS series to fix touchpad
  Input: stmfts, s6sy761 - update my e-mail
  Input: stmfts - use async probe & suspend/resume to avoid 2s delay
  Input: ALPS - fix TrackStick detection on Thinkpad L570 and Latitude 7370
  Input: xpad - add PDP device id 0x02a4
  Input: alps - report pressure of v3 and v7 trackstick
  Input: pxrc - new driver for PhoenixRC Flight Controller Adapter
  Input: usbtouchscreen - do not rely on input_dev->users
  Input: usbtouchscreen - fix deadlock in autosuspend
  Input: pegasus_notetaker - do not rely on input_dev->users
  Input: pagasus_notetaker - fix deadlock in autosuspend
  Input: synaptics_usb - do not rely on input_dev->users
  Input: synaptics_usb - fix deadlock in autosuspend
  Input: gpio-keys - add support for wakeup event action
  Input: appletouch - use true and false for boolean values
  Input: silead - add Chuwi Hi8 support
  Input: analog - use get_cycles() on PPC
  Input: stmpe-keypad - remove VLA usage
  Input: i8042 - add Lenovo ThinkPad L460 to i8042 reset list
  Input: add RAVE SP Powerbutton driver
  ...
This commit is contained in:
Linus Torvalds 2018-04-05 13:21:57 -07:00
commit 5414ab31b1
34 changed files with 1359 additions and 492 deletions

View File

@ -26,6 +26,14 @@ Optional subnode-properties:
If not specified defaults to 5. If not specified defaults to 5.
- wakeup-source: Boolean, button can wake-up the system. - wakeup-source: Boolean, button can wake-up the system.
(Legacy property supported: "gpio-key,wakeup") (Legacy property supported: "gpio-key,wakeup")
- wakeup-event-action: Specifies whether the key should wake the
system when asserted, when deasserted, or both. This property is
only valid for keys that wake up the system (e.g., when the
"wakeup-source" property is also provided).
Supported values are defined in linux-event-codes.h:
EV_ACT_ASSERTED - asserted
EV_ACT_DEASSERTED - deasserted
EV_ACT_ANY - both asserted and deasserted
- linux,can-disable: Boolean, indicates that button is connected - linux,can-disable: Boolean, indicates that button is connected
to dedicated (not shared) interrupt which can be disabled to to dedicated (not shared) interrupt which can be disabled to
suppress events from the button. suppress events from the button.

View File

@ -0,0 +1,22 @@
Zodiac Inflight Innovations RAVE Supervisory Processor Power Button Bindings
RAVE SP input device is a "MFD cell" device corresponding to power
button functionality of RAVE Supervisory Processor. It is expected
that its Device Tree node is specified as a child of the node
corresponding to the parent RAVE SP device (as documented in
Documentation/devicetree/bindings/mfd/zii,rave-sp.txt)
Required properties:
- compatible: Should be "zii,rave-sp-pwrbutton"
Example:
rave-sp {
compatible = "zii,rave-sp-rdu1";
current-speed = <38400>;
pwrbutton {
compatible = "zii,rave-sp-pwrbutton";
};
}

View File

@ -0,0 +1,57 @@
=======================================================
pxrc - PhoenixRC Flight Controller Adapter
=======================================================
:Author: Marcus Folkesson <marcus.folkesson@gmail.com>
This driver let you use your own RC controller plugged into the
adapter that comes with PhoenixRC [1]_ or other compatible adapters.
The adapter supports 7 analog channels and 1 digital input switch.
Notes
=====
Many RC controllers is able to configure which stick goes to which channel.
This is also configurable in most simulators, so a matching is not necessary.
The driver is generating the following input event for analog channels:
+---------+----------------+
| Channel | Event |
+=========+================+
| 1 | ABS_X |
+---------+----------------+
| 2 | ABS_Y |
+---------+----------------+
| 3 | ABS_RX |
+---------+----------------+
| 4 | ABS_RY |
+---------+----------------+
| 5 | ABS_RUDDER |
+---------+----------------+
| 6 | ABS_THROTTLE |
+---------+----------------+
| 7 | ABS_MISC |
+---------+----------------+
The digital input switch is generated as an `BTN_A` event.
Manual Testing
==============
To test this driver's functionality you may use `input-event` which is part of
the `input layer utilities` suite [2]_.
For example::
> modprobe pxrc
> input-events <devnr>
To print all input events from input `devnr`.
References
==========
.. [1] http://www.phoenix-sim.com/
.. [2] https://www.kraxel.org/cgit/input/

View File

@ -351,4 +351,14 @@ config JOYSTICK_PSXPAD_SPI_FF
To drive rumble motor a dedicated power supply is required. To drive rumble motor a dedicated power supply is required.
config JOYSTICK_PXRC
tristate "PhoenixRC Flight Controller Adapter"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use the PhoenixRC Flight Controller Adapter.
To compile this driver as a module, choose M here: the
module will be called pxrc.
endif endif

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o

View File

@ -163,7 +163,7 @@ static unsigned int get_time_pit(void)
#define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0) #define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0)
#define DELTA(x,y) ((y)-(x)) #define DELTA(x,y) ((y)-(x))
#define TIME_NAME "TSC" #define TIME_NAME "TSC"
#elif defined(__alpha__) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV) #elif defined(__alpha__) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC) || defined(CONFIG_RISCV)
#define GET_TIME(x) do { x = get_cycles(); } while (0) #define GET_TIME(x) do { x = get_cycles(); } while (0)
#define DELTA(x,y) ((y)-(x)) #define DELTA(x,y) ((y)-(x))
#define TIME_NAME "get_cycles" #define TIME_NAME "get_cycles"

View File

@ -0,0 +1,303 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Phoenix RC Flight Controller Adapter
*
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/mutex.h>
#include <linux/input.h>
#define PXRC_VENDOR_ID (0x1781)
#define PXRC_PRODUCT_ID (0x0898)
static const struct usb_device_id pxrc_table[] = {
{ USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE(usb, pxrc_table);
struct pxrc {
struct input_dev *input;
struct usb_device *udev;
struct usb_interface *intf;
struct urb *urb;
struct mutex pm_mutex;
bool is_open;
__u8 epaddr;
char phys[64];
unsigned char *data;
size_t bsize;
};
static void pxrc_usb_irq(struct urb *urb)
{
struct pxrc *pxrc = urb->context;
int error;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* this urb is timing out */
dev_dbg(&pxrc->intf->dev,
"%s - urb timed out - was the device unplugged?\n",
__func__);
return;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
case -EPIPE:
/* this urb is terminated, clean up */
dev_dbg(&pxrc->intf->dev, "%s - urb shutting down with status: %d\n",
__func__, urb->status);
return;
default:
dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n",
__func__, urb->status);
goto exit;
}
if (urb->actual_length == 8) {
input_report_abs(pxrc->input, ABS_X, pxrc->data[0]);
input_report_abs(pxrc->input, ABS_Y, pxrc->data[2]);
input_report_abs(pxrc->input, ABS_RX, pxrc->data[3]);
input_report_abs(pxrc->input, ABS_RY, pxrc->data[4]);
input_report_abs(pxrc->input, ABS_RUDDER, pxrc->data[5]);
input_report_abs(pxrc->input, ABS_THROTTLE, pxrc->data[6]);
input_report_abs(pxrc->input, ABS_MISC, pxrc->data[7]);
input_report_key(pxrc->input, BTN_A, pxrc->data[1]);
}
exit:
/* Resubmit to fetch new fresh URBs */
error = usb_submit_urb(urb, GFP_ATOMIC);
if (error && error != -EPERM)
dev_err(&pxrc->intf->dev,
"%s - usb_submit_urb failed with result: %d",
__func__, error);
}
static int pxrc_open(struct input_dev *input)
{
struct pxrc *pxrc = input_get_drvdata(input);
int retval;
mutex_lock(&pxrc->pm_mutex);
retval = usb_submit_urb(pxrc->urb, GFP_KERNEL);
if (retval) {
dev_err(&pxrc->intf->dev,
"%s - usb_submit_urb failed, error: %d\n",
__func__, retval);
retval = -EIO;
goto out;
}
pxrc->is_open = true;
out:
mutex_unlock(&pxrc->pm_mutex);
return retval;
}
static void pxrc_close(struct input_dev *input)
{
struct pxrc *pxrc = input_get_drvdata(input);
mutex_lock(&pxrc->pm_mutex);
usb_kill_urb(pxrc->urb);
pxrc->is_open = false;
mutex_unlock(&pxrc->pm_mutex);
}
static int pxrc_usb_init(struct pxrc *pxrc)
{
struct usb_endpoint_descriptor *epirq;
unsigned int pipe;
int retval;
/* Set up the endpoint information */
/* This device only has an interrupt endpoint */
retval = usb_find_common_endpoints(pxrc->intf->cur_altsetting,
NULL, NULL, &epirq, NULL);
if (retval) {
dev_err(&pxrc->intf->dev,
"Could not find endpoint\n");
goto error;
}
pxrc->bsize = usb_endpoint_maxp(epirq);
pxrc->epaddr = epirq->bEndpointAddress;
pxrc->data = devm_kmalloc(&pxrc->intf->dev, pxrc->bsize, GFP_KERNEL);
if (!pxrc->data) {
retval = -ENOMEM;
goto error;
}
usb_set_intfdata(pxrc->intf, pxrc);
usb_make_path(pxrc->udev, pxrc->phys, sizeof(pxrc->phys));
strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys));
pxrc->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pxrc->urb) {
retval = -ENOMEM;
goto error;
}
pipe = usb_rcvintpipe(pxrc->udev, pxrc->epaddr),
usb_fill_int_urb(pxrc->urb, pxrc->udev, pipe, pxrc->data, pxrc->bsize,
pxrc_usb_irq, pxrc, 1);
error:
return retval;
}
static int pxrc_input_init(struct pxrc *pxrc)
{
pxrc->input = devm_input_allocate_device(&pxrc->intf->dev);
if (pxrc->input == NULL) {
dev_err(&pxrc->intf->dev, "couldn't allocate input device\n");
return -ENOMEM;
}
pxrc->input->name = "PXRC Flight Controller Adapter";
pxrc->input->phys = pxrc->phys;
usb_to_input_id(pxrc->udev, &pxrc->input->id);
pxrc->input->open = pxrc_open;
pxrc->input->close = pxrc_close;
input_set_capability(pxrc->input, EV_KEY, BTN_A);
input_set_abs_params(pxrc->input, ABS_X, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_Y, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_RX, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_RY, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_RUDDER, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_THROTTLE, 0, 255, 0, 0);
input_set_abs_params(pxrc->input, ABS_MISC, 0, 255, 0, 0);
input_set_drvdata(pxrc->input, pxrc);
return input_register_device(pxrc->input);
}
static int pxrc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct pxrc *pxrc;
int retval;
pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL);
if (!pxrc)
return -ENOMEM;
mutex_init(&pxrc->pm_mutex);
pxrc->udev = usb_get_dev(interface_to_usbdev(intf));
pxrc->intf = intf;
retval = pxrc_usb_init(pxrc);
if (retval)
goto error;
retval = pxrc_input_init(pxrc);
if (retval)
goto err_free_urb;
return 0;
err_free_urb:
usb_free_urb(pxrc->urb);
error:
return retval;
}
static void pxrc_disconnect(struct usb_interface *intf)
{
struct pxrc *pxrc = usb_get_intfdata(intf);
usb_free_urb(pxrc->urb);
usb_set_intfdata(intf, NULL);
}
static int pxrc_suspend(struct usb_interface *intf, pm_message_t message)
{
struct pxrc *pxrc = usb_get_intfdata(intf);
mutex_lock(&pxrc->pm_mutex);
if (pxrc->is_open)
usb_kill_urb(pxrc->urb);
mutex_unlock(&pxrc->pm_mutex);
return 0;
}
static int pxrc_resume(struct usb_interface *intf)
{
struct pxrc *pxrc = usb_get_intfdata(intf);
int retval = 0;
mutex_lock(&pxrc->pm_mutex);
if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
retval = -EIO;
mutex_unlock(&pxrc->pm_mutex);
return retval;
}
static int pxrc_pre_reset(struct usb_interface *intf)
{
struct pxrc *pxrc = usb_get_intfdata(intf);
mutex_lock(&pxrc->pm_mutex);
usb_kill_urb(pxrc->urb);
return 0;
}
static int pxrc_post_reset(struct usb_interface *intf)
{
struct pxrc *pxrc = usb_get_intfdata(intf);
int retval = 0;
if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
retval = -EIO;
mutex_unlock(&pxrc->pm_mutex);
return retval;
}
static int pxrc_reset_resume(struct usb_interface *intf)
{
return pxrc_resume(intf);
}
static struct usb_driver pxrc_driver = {
.name = "pxrc",
.probe = pxrc_probe,
.disconnect = pxrc_disconnect,
.id_table = pxrc_table,
.suspend = pxrc_suspend,
.resume = pxrc_resume,
.pre_reset = pxrc_pre_reset,
.post_reset = pxrc_post_reset,
.reset_resume = pxrc_reset_resume,
};
module_usb_driver(pxrc_driver);
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
MODULE_LICENSE("GPL v2");

View File

@ -227,6 +227,7 @@ static const struct xpad_device {
{ 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
{ 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x02ab, "PDP Controller for Xbox One", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x02ab, "PDP Controller for Xbox One", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x02a4, "PDP Wired Controller for Xbox One - Stealth Series", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
{ 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
@ -475,7 +476,8 @@ static const u8 xboxone_hori_init[] = {
/* /*
* This packet is required for some of the PDP pads to start * This packet is required for some of the PDP pads to start
* sending input reports. One of those pads is (0x0e6f:0x02ab). * sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4).
*/ */
static const u8 xboxone_pdp_init1[] = { static const u8 xboxone_pdp_init1[] = {
0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
@ -483,7 +485,8 @@ static const u8 xboxone_pdp_init1[] = {
/* /*
* This packet is required for some of the PDP pads to start * This packet is required for some of the PDP pads to start
* sending input reports. One of those pads is (0x0e6f:0x02ab). * sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4).
*/ */
static const u8 xboxone_pdp_init2[] = { static const u8 xboxone_pdp_init2[] = {
0x06, 0x20, 0x00, 0x02, 0x01, 0x00 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
@ -521,6 +524,8 @@ static const struct xboxone_init_packet xboxone_init_packets[] = {
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init), XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init1), XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init1),
XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init2), XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init2),
XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init1),
XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init2),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init), XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),

View File

@ -30,6 +30,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <dt-bindings/input/gpio-keys.h>
struct gpio_button_data { struct gpio_button_data {
const struct gpio_keys_button *button; const struct gpio_keys_button *button;
@ -45,6 +46,7 @@ struct gpio_button_data {
unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
unsigned int irq; unsigned int irq;
unsigned int wakeup_trigger_type;
spinlock_t lock; spinlock_t lock;
bool disabled; bool disabled;
bool key_pressed; bool key_pressed;
@ -540,6 +542,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
} }
if (bdata->gpiod) { if (bdata->gpiod) {
bool active_low = gpiod_is_active_low(bdata->gpiod);
if (button->debounce_interval) { if (button->debounce_interval) {
error = gpiod_set_debounce(bdata->gpiod, error = gpiod_set_debounce(bdata->gpiod,
button->debounce_interval * 1000); button->debounce_interval * 1000);
@ -568,6 +572,24 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_gpio_isr; isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
switch (button->wakeup_event_action) {
case EV_ACT_ASSERTED:
bdata->wakeup_trigger_type = active_low ?
IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
break;
case EV_ACT_DEASSERTED:
bdata->wakeup_trigger_type = active_low ?
IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
break;
case EV_ACT_ANY:
/* fall through */
default:
/*
* For other cases, we are OK letting suspend/resume
* not reconfigure the trigger type.
*/
break;
}
} else { } else {
if (!button->irq) { if (!button->irq) {
dev_err(dev, "Found button without gpio or irq\n"); dev_err(dev, "Found button without gpio or irq\n");
@ -586,6 +608,11 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_irq_isr; isr = gpio_keys_irq_isr;
irqflags = 0; irqflags = 0;
/*
* For IRQ buttons, there is no interrupt for release.
* So we don't need to reconfigure the trigger type for wakeup.
*/
} }
bdata->code = &ddata->keymap[idx]; bdata->code = &ddata->keymap[idx];
@ -718,6 +745,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
/* legacy name */ /* legacy name */
fwnode_property_read_bool(child, "gpio-key,wakeup"); fwnode_property_read_bool(child, "gpio-key,wakeup");
fwnode_property_read_u32(child, "wakeup-event-action",
&button->wakeup_event_action);
button->can_disable = button->can_disable =
fwnode_property_read_bool(child, "linux,can-disable"); fwnode_property_read_bool(child, "linux,can-disable");
@ -845,19 +875,112 @@ static int gpio_keys_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int __maybe_unused
gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata)
{
int error;
error = enable_irq_wake(bdata->irq);
if (error) {
dev_err(bdata->input->dev.parent,
"failed to configure IRQ %d as wakeup source: %d\n",
bdata->irq, error);
return error;
}
if (bdata->wakeup_trigger_type) {
error = irq_set_irq_type(bdata->irq,
bdata->wakeup_trigger_type);
if (error) {
dev_err(bdata->input->dev.parent,
"failed to set wakeup trigger %08x for IRQ %d: %d\n",
bdata->wakeup_trigger_type, bdata->irq, error);
disable_irq_wake(bdata->irq);
return error;
}
}
return 0;
}
static void __maybe_unused
gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata)
{
int error;
/*
* The trigger type is always both edges for gpio-based keys and we do
* not support changing wakeup trigger for interrupt-based keys.
*/
if (bdata->wakeup_trigger_type) {
error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH);
if (error)
dev_warn(bdata->input->dev.parent,
"failed to restore interrupt trigger for IRQ %d: %d\n",
bdata->irq, error);
}
error = disable_irq_wake(bdata->irq);
if (error)
dev_warn(bdata->input->dev.parent,
"failed to disable IRQ %d as wake source: %d\n",
bdata->irq, error);
}
static int __maybe_unused
gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata)
{
struct gpio_button_data *bdata;
int error;
int i;
for (i = 0; i < ddata->pdata->nbuttons; i++) {
bdata = &ddata->data[i];
if (bdata->button->wakeup) {
error = gpio_keys_button_enable_wakeup(bdata);
if (error)
goto err_out;
}
bdata->suspended = true;
}
return 0;
err_out:
while (i--) {
bdata = &ddata->data[i];
if (bdata->button->wakeup)
gpio_keys_button_disable_wakeup(bdata);
bdata->suspended = false;
}
return error;
}
static void __maybe_unused
gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata)
{
struct gpio_button_data *bdata;
int i;
for (i = 0; i < ddata->pdata->nbuttons; i++) {
bdata = &ddata->data[i];
bdata->suspended = false;
if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq)))
gpio_keys_button_disable_wakeup(bdata);
}
}
static int __maybe_unused gpio_keys_suspend(struct device *dev) static int __maybe_unused gpio_keys_suspend(struct device *dev)
{ {
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input; struct input_dev *input = ddata->input;
int i; int error;
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) { error = gpio_keys_enable_wakeup(ddata);
struct gpio_button_data *bdata = &ddata->data[i]; if (error)
if (bdata->button->wakeup) return error;
enable_irq_wake(bdata->irq);
bdata->suspended = true;
}
} else { } else {
mutex_lock(&input->mutex); mutex_lock(&input->mutex);
if (input->users) if (input->users)
@ -873,15 +996,9 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input; struct input_dev *input = ddata->input;
int error = 0; int error = 0;
int i;
if (device_may_wakeup(dev)) { if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) { gpio_keys_disable_wakeup(ddata);
struct gpio_button_data *bdata = &ddata->data[i];
if (bdata->button->wakeup)
disable_irq_wake(bdata->irq);
bdata->suspended = false;
}
} else { } else {
mutex_lock(&input->mutex); mutex_lock(&input->mutex);
if (input->users) if (input->users)

View File

@ -48,6 +48,14 @@
#define STMPE_KEYPAD_KEYMAP_MAX_SIZE \ #define STMPE_KEYPAD_KEYMAP_MAX_SIZE \
(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS) (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
#define STMPE1601_NUM_DATA 5
#define STMPE2401_NUM_DATA 3
#define STMPE2403_NUM_DATA 5
/* Make sure it covers all cases above */
#define MAX_NUM_DATA 5
/** /**
* struct stmpe_keypad_variant - model-specific attributes * struct stmpe_keypad_variant - model-specific attributes
* @auto_increment: whether the KPC_DATA_BYTE register address * @auto_increment: whether the KPC_DATA_BYTE register address
@ -74,7 +82,7 @@ struct stmpe_keypad_variant {
static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE1601] = { [STMPE1601] = {
.auto_increment = true, .auto_increment = true,
.num_data = 5, .num_data = STMPE1601_NUM_DATA,
.num_normal_data = 3, .num_normal_data = 3,
.max_cols = 8, .max_cols = 8,
.max_rows = 8, .max_rows = 8,
@ -84,7 +92,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE2401] = { [STMPE2401] = {
.auto_increment = false, .auto_increment = false,
.set_pullup = true, .set_pullup = true,
.num_data = 3, .num_data = STMPE2401_NUM_DATA,
.num_normal_data = 2, .num_normal_data = 2,
.max_cols = 8, .max_cols = 8,
.max_rows = 12, .max_rows = 12,
@ -94,7 +102,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE2403] = { [STMPE2403] = {
.auto_increment = true, .auto_increment = true,
.set_pullup = true, .set_pullup = true,
.num_data = 5, .num_data = STMPE2403_NUM_DATA,
.num_normal_data = 3, .num_normal_data = 3,
.max_cols = 8, .max_cols = 8,
.max_rows = 12, .max_rows = 12,
@ -156,7 +164,7 @@ static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
struct stmpe_keypad *keypad = dev; struct stmpe_keypad *keypad = dev;
struct input_dev *input = keypad->input; struct input_dev *input = keypad->input;
const struct stmpe_keypad_variant *variant = keypad->variant; const struct stmpe_keypad_variant *variant = keypad->variant;
u8 fifo[variant->num_data]; u8 fifo[MAX_NUM_DATA];
int ret; int ret;
int i; int i;

View File

@ -832,4 +832,13 @@ config INPUT_HISI_POWERKEY
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called hisi_powerkey. module will be called hisi_powerkey.
config INPUT_RAVE_SP_PWRBUTTON
tristate "RAVE SP Power button Driver"
depends on RAVE_SP_CORE
help
Say Y here if you want to enable power key reporting from RAVE SP
To compile this driver as a module, choose M here: the
module will be called rave-sp-pwrbutton.
endif endif

View File

@ -59,6 +59,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o
obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o

View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Power Button driver for RAVE SP
//
// Copyright (C) 2017 Zodiac Inflight Innovations
//
//
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/rave-sp.h>
#include <linux/platform_device.h>
#define RAVE_SP_EVNT_BUTTON_PRESS (RAVE_SP_EVNT_BASE + 0x00)
struct rave_sp_power_button {
struct input_dev *idev;
struct notifier_block nb;
};
static int rave_sp_power_button_event(struct notifier_block *nb,
unsigned long action, void *data)
{
struct rave_sp_power_button *pb =
container_of(nb, struct rave_sp_power_button, nb);
const u8 event = rave_sp_action_unpack_event(action);
const u8 value = rave_sp_action_unpack_value(action);
struct input_dev *idev = pb->idev;
if (event == RAVE_SP_EVNT_BUTTON_PRESS) {
input_report_key(idev, KEY_POWER, value);
input_sync(idev);
return NOTIFY_STOP;
}
return NOTIFY_DONE;
}
static int rave_sp_pwrbutton_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rave_sp_power_button *pb;
struct input_dev *idev;
int error;
pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
if (!pb)
return -ENOMEM;
idev = devm_input_allocate_device(dev);
if (!idev)
return -ENOMEM;
idev->name = pdev->name;
input_set_capability(idev, EV_KEY, KEY_POWER);
error = input_register_device(idev);
if (error)
return error;
pb->idev = idev;
pb->nb.notifier_call = rave_sp_power_button_event;
pb->nb.priority = 128;
error = devm_rave_sp_register_event_notifier(dev, &pb->nb);
if (error)
return error;
return 0;
}
static const struct of_device_id rave_sp_pwrbutton_of_match[] = {
{ .compatible = "zii,rave-sp-pwrbutton" },
{}
};
static struct platform_driver rave_sp_pwrbutton_driver = {
.probe = rave_sp_pwrbutton_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = rave_sp_pwrbutton_of_match,
},
};
module_platform_driver(rave_sp_pwrbutton_driver);
MODULE_DEVICE_TABLE(of, rave_sp_pwrbutton_of_match);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
MODULE_DESCRIPTION("RAVE SP Power Button driver");

View File

@ -139,11 +139,11 @@ static const struct alps_model_info alps_model_data[] = {
}; };
static const struct alps_protocol_info alps_v3_protocol_data = { static const struct alps_protocol_info alps_v3_protocol_data = {
ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
}; };
static const struct alps_protocol_info alps_v3_rushmore_data = { static const struct alps_protocol_info alps_v3_rushmore_data = {
ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
}; };
static const struct alps_protocol_info alps_v4_protocol_data = { static const struct alps_protocol_info alps_v4_protocol_data = {
@ -155,7 +155,7 @@ static const struct alps_protocol_info alps_v5_protocol_data = {
}; };
static const struct alps_protocol_info alps_v7_protocol_data = { static const struct alps_protocol_info alps_v7_protocol_data = {
ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
}; };
static const struct alps_protocol_info alps_v8_protocol_data = { static const struct alps_protocol_info alps_v8_protocol_data = {
@ -583,7 +583,7 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
z = (packet[4] & 0x7c) >> 2; z = packet[4] & 0x7c;
/* /*
* The x and y values tend to be quite large, and when used * The x and y values tend to be quite large, and when used
@ -595,6 +595,7 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
input_report_rel(dev, REL_X, x); input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y); input_report_rel(dev, REL_Y, -y);
input_report_abs(dev, ABS_PRESSURE, z);
/* /*
* Most ALPS models report the trackstick buttons in the touchpad * Most ALPS models report the trackstick buttons in the touchpad
@ -827,7 +828,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2; struct input_dev *dev2 = priv->dev2;
int x, y, z, left, right, middle; int x, y, z;
/* /*
* We can use Byte5 to distinguish if the packet is from Touchpad * We can use Byte5 to distinguish if the packet is from Touchpad
@ -847,9 +848,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x20) << 2); x = packet[1] | ((packet[3] & 0x20) << 2);
y = packet[2] | ((packet[3] & 0x40) << 1); y = packet[2] | ((packet[3] & 0x40) << 1);
z = packet[4]; z = packet[4];
left = packet[3] & 0x01;
right = packet[3] & 0x02;
middle = packet[3] & 0x04;
/* To prevent the cursor jump when finger lifted */ /* To prevent the cursor jump when finger lifted */
if (x == 0x7F && y == 0x7F && z == 0x7F) if (x == 0x7F && y == 0x7F && z == 0x7F)
@ -859,9 +857,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_rel(dev2, REL_X, (char)x / 4); input_report_rel(dev2, REL_X, (char)x / 4);
input_report_rel(dev2, REL_Y, -((char)y / 4)); input_report_rel(dev2, REL_Y, -((char)y / 4));
input_report_key(dev2, BTN_LEFT, left); psmouse_report_standard_buttons(dev2, packet[3]);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
input_sync(dev2); input_sync(dev2);
return; return;
@ -871,8 +867,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x78) << 4); x = packet[1] | ((packet[3] & 0x78) << 4);
y = packet[2] | ((packet[4] & 0x78) << 4); y = packet[2] | ((packet[4] & 0x78) << 4);
z = packet[5]; z = packet[5];
left = packet[3] & 0x01;
right = packet[3] & 0x02;
if (z > 30) if (z > 30)
input_report_key(dev, BTN_TOUCH, 1); input_report_key(dev, BTN_TOUCH, 1);
@ -888,8 +882,8 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, z > 0); input_report_key(dev, BTN_TOOL_FINGER, z > 0);
/* v6 touchpad does not have middle button */ /* v6 touchpad does not have middle button */
input_report_key(dev, BTN_LEFT, left); packet[3] &= ~BIT(2);
input_report_key(dev, BTN_RIGHT, right); psmouse_report_standard_buttons(dev2, packet[3]);
input_sync(dev); input_sync(dev);
} }
@ -1098,7 +1092,7 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
struct alps_data *priv = psmouse->private; struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
struct input_dev *dev2 = priv->dev2; struct input_dev *dev2 = priv->dev2;
int x, y, z, left, right, middle; int x, y, z;
/* It should be a DualPoint when received trackstick packet */ /* It should be a DualPoint when received trackstick packet */
if (!(priv->flags & ALPS_DUALPOINT)) { if (!(priv->flags & ALPS_DUALPOINT)) {
@ -1112,16 +1106,11 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
((packet[3] & 0x20) << 1); ((packet[3] & 0x20) << 1);
z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
left = (packet[1] & 0x01);
right = (packet[1] & 0x02) >> 1;
middle = (packet[1] & 0x04) >> 2;
input_report_rel(dev2, REL_X, (char)x); input_report_rel(dev2, REL_X, (char)x);
input_report_rel(dev2, REL_Y, -((char)y)); input_report_rel(dev2, REL_Y, -((char)y));
input_report_abs(dev2, ABS_PRESSURE, z);
input_report_key(dev2, BTN_LEFT, left); psmouse_report_standard_buttons(dev2, packet[1]);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
input_sync(dev2); input_sync(dev2);
} }
@ -1503,10 +1492,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
alps_report_buttons(dev, dev2, alps_report_buttons(dev, dev2,
packet[0] & 1, packet[0] & 2, packet[0] & 4); packet[0] & 1, packet[0] & 2, packet[0] & 4);
input_report_rel(dev, REL_X, psmouse_report_standard_motion(dev, packet);
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y,
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
input_sync(dev); input_sync(dev);
} }
@ -2544,13 +2530,31 @@ static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
} }
static int alps_update_dual_info_ss4_v2(unsigned char otp[][4], static int alps_update_dual_info_ss4_v2(unsigned char otp[][4],
struct alps_data *priv) struct alps_data *priv,
struct psmouse *psmouse)
{ {
bool is_dual = false; bool is_dual = false;
int reg_val = 0;
struct ps2dev *ps2dev = &psmouse->ps2dev;
if (IS_SS4PLUS_DEV(priv->dev_id)) if (IS_SS4PLUS_DEV(priv->dev_id)) {
is_dual = (otp[0][0] >> 4) & 0x01; is_dual = (otp[0][0] >> 4) & 0x01;
if (!is_dual) {
/* For support TrackStick of Thinkpad L/E series */
if (alps_exit_command_mode(psmouse) == 0 &&
alps_enter_command_mode(psmouse) == 0) {
reg_val = alps_command_mode_read_reg(psmouse,
0xD7);
}
alps_exit_command_mode(psmouse);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
if (reg_val == 0x0C || reg_val == 0x1D)
is_dual = true;
}
}
if (is_dual) if (is_dual)
priv->flags |= ALPS_DUALPOINT | priv->flags |= ALPS_DUALPOINT |
ALPS_DUALPOINT_WITH_PRESSURE; ALPS_DUALPOINT_WITH_PRESSURE;
@ -2573,7 +2577,7 @@ static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
alps_update_btn_info_ss4_v2(otp, priv); alps_update_btn_info_ss4_v2(otp, priv);
alps_update_dual_info_ss4_v2(otp, priv); alps_update_dual_info_ss4_v2(otp, priv, psmouse);
return 0; return 0;
} }

View File

@ -587,7 +587,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
/* Perform size detection, if not done already */ /* Perform size detection, if not done already */
if (unlikely(!dev->size_detect_done)) { if (unlikely(!dev->size_detect_done)) {
atp_detect_size(dev); atp_detect_size(dev);
dev->size_detect_done = 1; dev->size_detect_done = true;
goto exit; goto exit;
} }
} }
@ -813,7 +813,7 @@ static int atp_open(struct input_dev *input)
if (usb_submit_urb(dev->urb, GFP_ATOMIC)) if (usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO; return -EIO;
dev->open = 1; dev->open = true;
return 0; return 0;
} }
@ -823,7 +823,7 @@ static void atp_close(struct input_dev *input)
usb_kill_urb(dev->urb); usb_kill_urb(dev->urb);
cancel_work_sync(&dev->work); cancel_work_sync(&dev->work);
dev->open = 0; dev->open = false;
} }
static int atp_handle_geyser(struct atp *dev) static int atp_handle_geyser(struct atp *dev)

View File

@ -35,7 +35,7 @@
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
unsigned char *param) unsigned char *param)
{ {
if (psmouse_sliced_command(psmouse, c) || if (ps2_sliced_command(&psmouse->ps2dev, c) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
return -1; return -1;
@ -107,8 +107,8 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) { switch (etd->hw_version) {
case 1: case 1:
if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
psmouse_sliced_command(psmouse, reg) || ps2_sliced_command(&psmouse->ps2dev, reg) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
rc = -1; rc = -1;
} }
@ -162,9 +162,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) { switch (etd->hw_version) {
case 1: case 1:
if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
psmouse_sliced_command(psmouse, reg) || ps2_sliced_command(&psmouse->ps2dev, reg) ||
psmouse_sliced_command(psmouse, val) || ps2_sliced_command(&psmouse->ps2dev, val) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
rc = -1; rc = -1;
} }
@ -279,8 +279,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); psmouse_report_standard_buttons(dev, packet[0]);
if (etd->fw_version < 0x020000 && if (etd->fw_version < 0x020000 &&
(etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
@ -390,8 +390,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
if (etd->reports_pressure) { if (etd->reports_pressure) {
input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width); input_report_abs(dev, ABS_TOOL_WIDTH, width);
@ -434,9 +433,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
x = packet[4] - (int)((packet[1]^0x80) << 1); x = packet[4] - (int)((packet[1]^0x80) << 1);
y = (int)((packet[2]^0x80) << 1) - packet[5]; y = (int)((packet[2]^0x80) << 1) - packet[5];
input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(tp_dev, packet[0]);
input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
input_report_rel(tp_dev, REL_X, x); input_report_rel(tp_dev, REL_X, x);
input_report_rel(tp_dev, REL_Y, y); input_report_rel(tp_dev, REL_Y, y);
@ -526,12 +523,10 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */ /* For clickpads map both buttons to BTN_LEFT */
if (etd->fw_version & 0x001000) { if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
} else { else
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
}
input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width); input_report_abs(dev, ABS_TOOL_WIDTH, width);
@ -546,13 +541,10 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */ /* For clickpads map both buttons to BTN_LEFT */
if (etd->fw_version & 0x001000) { if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
} else { else
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
}
input_mt_report_pointer_emulation(dev, true); input_mt_report_pointer_emulation(dev, true);
input_sync(dev); input_sync(dev);

View File

@ -17,6 +17,7 @@
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "lifebook.h" #include "lifebook.h"
@ -136,7 +137,7 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
struct lifebook_data *priv = psmouse->private; struct lifebook_data *priv = psmouse->private;
struct input_dev *dev1 = psmouse->dev; struct input_dev *dev1 = psmouse->dev;
struct input_dev *dev2 = priv ? priv->dev2 : NULL; struct input_dev *dev2 = priv ? priv->dev2 : NULL;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
bool relative_packet = packet[0] & 0x08; bool relative_packet = packet[0] & 0x08;
if (relative_packet || !lifebook_use_6byte_proto) { if (relative_packet || !lifebook_use_6byte_proto) {
@ -188,14 +189,10 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
} }
if (dev2) { if (dev2) {
if (relative_packet) { if (relative_packet)
input_report_rel(dev2, REL_X, psmouse_report_standard_motion(dev2, packet);
((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
input_report_rel(dev2, REL_Y, psmouse_report_standard_buttons(dev2, packet[0]);
-(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
}
input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
input_sync(dev2); input_sync(dev2);
} }
@ -205,10 +202,12 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
static int lifebook_absolute_mode(struct psmouse *psmouse) static int lifebook_absolute_mode(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param; u8 param;
int error;
if (psmouse_reset(psmouse)) error = psmouse_reset(psmouse);
return -1; if (error)
return error;
/* /*
* Enable absolute output -- ps2_command fails always but if * Enable absolute output -- ps2_command fails always but if
@ -224,15 +223,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
static void lifebook_relative_mode(struct psmouse *psmouse) static void lifebook_relative_mode(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 0x06; u8 param = 0x06;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES); ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
} }
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{ {
static const unsigned char params[] = { 0, 1, 2, 2, 3 }; static const u8 params[] = { 0, 1, 2, 2, 3 };
unsigned char p; u8 p;
if (resolution == 0 || resolution > 400) if (resolution == 0 || resolution > 400)
resolution = 400; resolution = 400;
@ -257,11 +256,11 @@ static void lifebook_disconnect(struct psmouse *psmouse)
int lifebook_detect(struct psmouse *psmouse, bool set_properties) int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{ {
if (!lifebook_present) if (!lifebook_present)
return -1; return -ENXIO;
if (desired_serio_phys && if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
return -1; return -ENXIO;
if (set_properties) { if (set_properties) {
psmouse->vendor = "Fujitsu"; psmouse->vendor = "Fujitsu";
@ -294,10 +293,10 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
dev2->id.version = 0x0000; dev2->id.version = 0x0000;
dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->dev.parent = &psmouse->ps2dev.serio->dev;
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_set_capability(dev2, EV_REL, REL_X);
dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); input_set_capability(dev2, EV_REL, REL_Y);
dev2->keybit[BIT_WORD(BTN_LEFT)] = input_set_capability(dev2, EV_KEY, BTN_LEFT);
BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); input_set_capability(dev2, EV_KEY, BTN_RIGHT);
error = input_register_device(priv->dev2); error = input_register_device(priv->dev2);
if (error) if (error)
@ -316,21 +315,26 @@ int lifebook_init(struct psmouse *psmouse)
{ {
struct input_dev *dev1 = psmouse->dev; struct input_dev *dev1 = psmouse->dev;
int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
int error;
if (lifebook_absolute_mode(psmouse)) error = lifebook_absolute_mode(psmouse);
return -1; if (error)
return error;
dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); /* Clear default capabilities */
dev1->relbit[0] = 0; bitmap_zero(dev1->evbit, EV_CNT);
dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; bitmap_zero(dev1->relbit, REL_CNT);
dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); bitmap_zero(dev1->keybit, KEY_CNT);
input_set_capability(dev1, EV_KEY, BTN_TOUCH);
input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
if (!desired_serio_phys) { if (!desired_serio_phys) {
if (lifebook_create_relative_device(psmouse)) { error = lifebook_create_relative_device(psmouse);
if (error) {
lifebook_relative_mode(psmouse); lifebook_relative_mode(psmouse);
return -1; return error;
} }
} }

View File

@ -9,9 +9,11 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/bitops.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "logips2pp.h" #include "logips2pp.h"
@ -22,12 +24,12 @@
#define PS2PP_KIND_TRACKMAN 4 #define PS2PP_KIND_TRACKMAN 4
/* Logitech mouse features */ /* Logitech mouse features */
#define PS2PP_WHEEL 0x01 #define PS2PP_WHEEL BIT(0)
#define PS2PP_HWHEEL 0x02 #define PS2PP_HWHEEL BIT(1)
#define PS2PP_SIDE_BTN 0x04 #define PS2PP_SIDE_BTN BIT(2)
#define PS2PP_EXTRA_BTN 0x08 #define PS2PP_EXTRA_BTN BIT(3)
#define PS2PP_TASK_BTN 0x10 #define PS2PP_TASK_BTN BIT(4)
#define PS2PP_NAV_BTN 0x20 #define PS2PP_NAV_BTN BIT(5)
struct ps2pp_info { struct ps2pp_info {
u8 model; u8 model;
@ -42,7 +44,7 @@ struct ps2pp_info {
static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
if (psmouse->pktcnt < 3) if (psmouse->pktcnt < 3)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -58,28 +60,30 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
case 0x0d: /* Mouse extra info */ case 0x0d: /* Mouse extra info */
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev,
(int) (packet[2] & 8) - (int) (packet[2] & 7)); packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); -sign_extend32(packet[2], 3));
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); input_report_key(dev, BTN_SIDE, packet[2] & BIT(4));
input_report_key(dev, BTN_EXTRA, packet[2] & BIT(5));
break; break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1); input_report_key(dev, BTN_SIDE, packet[2] & BIT(0));
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); input_report_key(dev, BTN_EXTRA, packet[2] & BIT(1));
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); input_report_key(dev, BTN_TASK, packet[2] & BIT(2));
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); input_report_key(dev, BTN_BACK, packet[2] & BIT(3));
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); input_report_key(dev, BTN_FORWARD, packet[2] & BIT(4));
break; break;
case 0x0f: /* TouchPad extra info */ case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
packet[0] = packet[2] | 0x08; -sign_extend32(packet[2] >> 4, 3));
packet[0] = packet[2] | BIT(3);
break; break;
default: default:
@ -88,16 +92,14 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
(packet[1] >> 4) | (packet[0] & 0x30)); (packet[1] >> 4) | (packet[0] & 0x30));
break; break;
} }
psmouse_report_standard_buttons(dev, packet[0]);
} else { } else {
/* Standard PS/2 motion data */ /* Standard PS/2 motion data */
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); psmouse_report_standard_packet(dev, packet);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
} }
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
input_sync(dev); input_sync(dev);
return PSMOUSE_FULL_PACKET; return PSMOUSE_FULL_PACKET;
@ -111,13 +113,17 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
* Ugly. * Ugly.
*/ */
static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command)
{ {
if (psmouse_sliced_command(psmouse, command)) int error;
return -1;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) error = ps2_sliced_command(&psmouse->ps2dev, command);
return -1; if (error)
return error;
error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300);
if (error)
return error;
return 0; return 0;
} }
@ -133,7 +139,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4]; u8 param[4];
ps2pp_cmd(psmouse, param, 0x32); ps2pp_cmd(psmouse, param, 0x32);
@ -171,7 +177,7 @@ static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
} }
PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
/* /*
* Support 800 dpi resolution _only_ if the user wants it (there are good * Support 800 dpi resolution _only_ if the user wants it (there are good
@ -179,11 +185,12 @@ PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
* also good reasons to use it, let the user decide). * also good reasons to use it, let the user decide).
*/ */
static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) static void ps2pp_set_resolution(struct psmouse *psmouse,
unsigned int resolution)
{ {
if (resolution > 400) { if (resolution > 400) {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 3; u8 param = 3;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
@ -196,7 +203,8 @@ static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolutio
static void ps2pp_disconnect(struct psmouse *psmouse) static void ps2pp_disconnect(struct psmouse *psmouse)
{ {
device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
} }
static const struct ps2pp_info *get_model_info(unsigned char model) static const struct ps2pp_info *get_model_info(unsigned char model)
@ -269,24 +277,24 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
struct input_dev *input_dev = psmouse->dev; struct input_dev *input_dev = psmouse->dev;
if (model_info->features & PS2PP_SIDE_BTN) if (model_info->features & PS2PP_SIDE_BTN)
__set_bit(BTN_SIDE, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_SIDE);
if (model_info->features & PS2PP_EXTRA_BTN) if (model_info->features & PS2PP_EXTRA_BTN)
__set_bit(BTN_EXTRA, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_EXTRA);
if (model_info->features & PS2PP_TASK_BTN) if (model_info->features & PS2PP_TASK_BTN)
__set_bit(BTN_TASK, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_TASK);
if (model_info->features & PS2PP_NAV_BTN) { if (model_info->features & PS2PP_NAV_BTN) {
__set_bit(BTN_FORWARD, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_FORWARD);
__set_bit(BTN_BACK, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_BACK);
} }
if (model_info->features & PS2PP_WHEEL) if (model_info->features & PS2PP_WHEEL)
__set_bit(REL_WHEEL, input_dev->relbit); input_set_capability(input_dev, EV_REL, REL_WHEEL);
if (model_info->features & PS2PP_HWHEEL) if (model_info->features & PS2PP_HWHEEL)
__set_bit(REL_HWHEEL, input_dev->relbit); input_set_capability(input_dev, EV_REL, REL_HWHEEL);
switch (model_info->kind) { switch (model_info->kind) {
@ -318,6 +326,30 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
} }
} }
static int ps2pp_setup_protocol(struct psmouse *psmouse,
const struct ps2pp_info *model_info)
{
int error;
psmouse->protocol_handler = ps2pp_process_byte;
psmouse->pktsize = 3;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
error = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
psmouse_err(psmouse,
"failed to create smartscroll sysfs attribute, error: %d\n",
error);
return error;
}
}
return 0;
}
/* /*
* Logitech magic init. Detect whether the mouse is a Logitech one * Logitech magic init. Detect whether the mouse is a Logitech one
@ -328,9 +360,9 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
int ps2pp_detect(struct psmouse *psmouse, bool set_properties) int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
const struct ps2pp_info *model_info; const struct ps2pp_info *model_info;
u8 param[4];
u8 model, buttons;
bool use_ps2pp = false; bool use_ps2pp = false;
int error; int error;
@ -346,7 +378,7 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
buttons = param[1]; buttons = param[1];
if (!model || !buttons) if (!model || !buttons)
return -1; return -ENXIO;
model_info = get_model_info(model); model_info = get_model_info(model);
if (model_info) { if (model_info) {
@ -368,7 +400,8 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
param[0] = 0; param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) && if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { param[0] == 0x06 && param[1] == 0x00 &&
param[2] == 0x14) {
use_ps2pp = true; use_ps2pp = true;
} }
@ -387,7 +420,9 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
} }
} else { } else {
psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model); psmouse_warn(psmouse,
"Detected unknown Logitech mouse model %d\n",
model);
} }
if (set_properties) { if (set_properties) {
@ -395,31 +430,18 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
psmouse->model = model; psmouse->model = model;
if (use_ps2pp) { if (use_ps2pp) {
psmouse->protocol_handler = ps2pp_process_byte; error = ps2pp_setup_protocol(psmouse, model_info);
psmouse->pktsize = 3; if (error)
return error;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
error = device_create_file(&ps2dev->serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
psmouse_err(psmouse,
"failed to create smartscroll sysfs attribute, error: %d\n",
error);
return -1;
}
}
} }
if (buttons >= 3) if (buttons >= 3)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
if (model_info) if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
} }
return use_ps2pp ? 0 : -1; return use_ps2pp ? 0 : -ENXIO;
} }

View File

@ -14,6 +14,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define psmouse_fmt(fmt) fmt #define psmouse_fmt(fmt) fmt
#include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -23,6 +24,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "synaptics.h" #include "synaptics.h"
@ -68,6 +70,10 @@ static bool psmouse_smartscroll = true;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
static bool psmouse_a4tech_2wheels;
module_param_named(a4tech_workaround, psmouse_a4tech_2wheels, bool, 0644);
MODULE_PARM_DESC(a4tech_workaround, "A4Tech second scroll wheel workaround, 1 = enabled, 0 = disabled (default).");
static unsigned int psmouse_resetafter = 5; static unsigned int psmouse_resetafter = 5;
module_param_named(resetafter, psmouse_resetafter, uint, 0644); module_param_named(resetafter, psmouse_resetafter, uint, 0644);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
@ -116,13 +122,30 @@ static DEFINE_MUTEX(psmouse_mutex);
static struct workqueue_struct *kpsmoused_wq; static struct workqueue_struct *kpsmoused_wq;
static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
{ {
input_report_key(dev, BTN_LEFT, buttons & BIT(0)); input_report_key(dev, BTN_LEFT, buttons & BIT(0));
input_report_key(dev, BTN_MIDDLE, buttons & BIT(2)); input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
input_report_key(dev, BTN_RIGHT, buttons & BIT(1)); input_report_key(dev, BTN_RIGHT, buttons & BIT(1));
} }
void psmouse_report_standard_motion(struct input_dev *dev, u8 *packet)
{
int x, y;
x = packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0;
y = packet[2] ? packet[2] - ((packet[0] << 3) & 0x100) : 0;
input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y);
}
void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet)
{
psmouse_report_standard_buttons(dev, packet[0]);
psmouse_report_standard_motion(dev, packet);
}
/* /*
* psmouse_process_byte() analyzes the PS/2 data stream and reports * psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived. * relevant events to the input module once full packet has arrived.
@ -130,7 +153,8 @@ static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
int wheel;
if (psmouse->pktcnt < psmouse->pktsize) if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -140,39 +164,52 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
switch (psmouse->protocol->type) { switch (psmouse->protocol->type) {
case PSMOUSE_IMPS: case PSMOUSE_IMPS:
/* IntelliMouse has scroll wheel */ /* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
break; break;
case PSMOUSE_IMEX: case PSMOUSE_IMEX:
/* Scroll wheel and buttons on IntelliMouse Explorer */ /* Scroll wheel and buttons on IntelliMouse Explorer */
switch (packet[3] & 0xC0) { switch (packet[3] & 0xC0) {
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_WHEEL,
-sign_extend32(packet[3], 5));
break; break;
case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_HWHEEL,
-sign_extend32(packet[3], 5));
break; break;
case 0x00: case 0x00:
case 0xC0: case 0xC0:
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); wheel = sign_extend32(packet[3], 3);
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); /*
* Some A4Tech mice have two scroll wheels, with first
* one reporting +/-1 in the lower nibble, and second
* one reporting +/-2.
*/
if (psmouse_a4tech_2wheels && abs(wheel) > 1)
input_report_rel(dev, REL_HWHEEL, wheel / 2);
else
input_report_rel(dev, REL_WHEEL, -wheel);
input_report_key(dev, BTN_SIDE, BIT(4));
input_report_key(dev, BTN_EXTRA, BIT(5));
break; break;
} }
break; break;
case PSMOUSE_GENPS: case PSMOUSE_GENPS:
/* Report scroll buttons on NetMice */ /* Report scroll buttons on NetMice */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
/* Extra buttons on Genius NewNet 3D */ /* Extra buttons on Genius NewNet 3D */
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_SIDE, BIT(6));
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); input_report_key(dev, BTN_EXTRA, BIT(7));
break; break;
case PSMOUSE_THINKPS: case PSMOUSE_THINKPS:
/* Extra button on ThinkingMouse */ /* Extra button on ThinkingMouse */
input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); input_report_key(dev, BTN_EXTRA, BIT(3));
/* /*
* Without this bit of weirdness moving up gives wildly * Without this bit of weirdness moving up gives wildly
@ -186,8 +223,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
* Cortron PS2 Trackball reports SIDE button in the * Cortron PS2 Trackball reports SIDE button in the
* 4th bit of the first byte. * 4th bit of the first byte.
*/ */
input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); input_report_key(dev, BTN_SIDE, BIT(3));
packet[0] |= 0x08; packet[0] |= BIT(3);
break; break;
default: default:
@ -195,11 +232,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
} }
/* Generic PS/2 Mouse */ /* Generic PS/2 Mouse */
psmouse_report_standard_buttons(dev, packet[0] |= psmouse->extra_buttons;
packet[0] | psmouse->extra_buttons); psmouse_report_standard_packet(dev, packet);
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
input_sync(dev); input_sync(dev);
@ -255,7 +289,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
psmouse_notice(psmouse, psmouse_notice(psmouse,
"issuing reconnect request\n"); "issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio); serio_reconnect(psmouse->ps2dev.serio);
return -1; return -EIO;
} }
} }
psmouse->pktcnt = 0; psmouse->pktcnt = 0;
@ -306,7 +340,7 @@ static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
* for normal processing or gathering them as command response. * for normal processing or gathering them as command response.
*/ */
static irqreturn_t psmouse_interrupt(struct serio *serio, static irqreturn_t psmouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags) u8 data, unsigned int flags)
{ {
struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *psmouse = serio_get_drvdata(serio);
@ -397,41 +431,20 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
{
int i;
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
for (i = 6; i >= 0; i -= 2) {
unsigned char d = (command >> i) & 3;
if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
return -1;
}
return 0;
}
/* /*
* psmouse_reset() resets the mouse into power-on state. * psmouse_reset() resets the mouse into power-on state.
*/ */
int psmouse_reset(struct psmouse *psmouse) int psmouse_reset(struct psmouse *psmouse)
{ {
unsigned char param[2]; u8 param[2];
int error;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT);
return -1; if (error)
return error;
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
return -1; return -EIO;
return 0; return 0;
} }
@ -441,8 +454,8 @@ int psmouse_reset(struct psmouse *psmouse)
*/ */
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{ {
static const unsigned char params[] = { 0, 1, 2, 2, 3 }; static const u8 params[] = { 0, 1, 2, 2, 3 };
unsigned char p; u8 p;
if (resolution == 0 || resolution > 200) if (resolution == 0 || resolution > 200)
resolution = 200; resolution = 200;
@ -457,11 +470,12 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
*/ */
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{ {
static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
unsigned char r; u8 r;
int i = 0; int i = 0;
while (rates[i] > rate) i++; while (rates[i] > rate)
i++;
r = rates[i]; r = rates[i];
ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
psmouse->rate = r; psmouse->rate = r;
@ -533,7 +547,7 @@ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
static int genius_detect(struct psmouse *psmouse, bool set_properties) static int genius_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4]; u8 param[4];
param[0] = 3; param[0] = 3;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
@ -543,7 +557,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -565,7 +579,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
param[0] = 200; param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
@ -576,7 +590,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 3) if (param[0] != 3)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -598,7 +612,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
intellimouse_detect(psmouse, 0); intellimouse_detect(psmouse, 0);
@ -611,7 +625,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 4) if (param[0] != 4)
return -1; return -ENODEV;
/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
param[0] = 200; param[0] = 200;
@ -644,8 +658,8 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
static int thinking_detect(struct psmouse *psmouse, bool set_properties) static int thinking_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
int i; int i;
param[0] = 10; param[0] = 10;
@ -659,7 +673,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 2) if (param[0] != 2)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -687,7 +701,7 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
* We have no way of figuring true number of buttons so let's * We have no way of figuring true number of buttons so let's
* assume that the device has 3. * assume that the device has 3.
*/ */
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
} }
return 0; return 0;
@ -942,20 +956,17 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
{ {
struct input_dev *input_dev = psmouse->dev; struct input_dev *input_dev = psmouse->dev;
memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); bitmap_zero(input_dev->evbit, EV_CNT);
memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); bitmap_zero(input_dev->keybit, KEY_CNT);
memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); bitmap_zero(input_dev->relbit, REL_CNT);
memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); bitmap_zero(input_dev->absbit, ABS_CNT);
memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); bitmap_zero(input_dev->mscbit, MSC_CNT);
__set_bit(EV_KEY, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_LEFT);
__set_bit(EV_REL, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
__set_bit(BTN_LEFT, input_dev->keybit); input_set_capability(input_dev, EV_REL, REL_X);
__set_bit(BTN_RIGHT, input_dev->keybit); input_set_capability(input_dev, EV_REL, REL_Y);
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
__set_bit(INPUT_PROP_POINTER, input_dev->propbit); __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
@ -1225,7 +1236,8 @@ static int psmouse_extensions(struct psmouse *psmouse,
static int psmouse_probe(struct psmouse *psmouse) static int psmouse_probe(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
int error;
/* /*
* First, we check if it's a mouse. It should send 0x00 or 0x03 in * First, we check if it's a mouse. It should send 0x00 or 0x03 in
@ -1234,20 +1246,22 @@ static int psmouse_probe(struct psmouse *psmouse)
* subsequent ID queries, probably due to a firmware bug. * subsequent ID queries, probably due to a firmware bug.
*/ */
param[0] = 0xa5; param[0] = 0xa5;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
return -1; if (error)
return error;
if (param[0] != 0x00 && param[0] != 0x03 && if (param[0] != 0x00 && param[0] != 0x03 &&
param[0] != 0x04 && param[0] != 0xff) param[0] != 0x04 && param[0] != 0xff)
return -1; return -ENODEV;
/* /*
* Then we reset and disable the mouse so that it doesn't generate * Then we reset and disable the mouse so that it doesn't generate
* events. * events.
*/ */
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_warn(psmouse, "Failed to reset mouse on %s\n", if (error)
ps2dev->serio->phys); psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
ps2dev->serio->phys, error);
return 0; return 0;
} }
@ -1288,10 +1302,13 @@ int psmouse_activate(struct psmouse *psmouse)
*/ */
int psmouse_deactivate(struct psmouse *psmouse) int psmouse_deactivate(struct psmouse *psmouse)
{ {
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { int error;
psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
psmouse->ps2dev.serio->phys); error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE);
return -1; if (error) {
psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
psmouse->ps2dev.serio->phys, error);
return error;
} }
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);

View File

@ -131,7 +131,6 @@ struct psmouse {
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay); unsigned long delay);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse); int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
@ -140,6 +139,10 @@ int psmouse_activate(struct psmouse *psmouse);
int psmouse_deactivate(struct psmouse *psmouse); int psmouse_deactivate(struct psmouse *psmouse);
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]); bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
void psmouse_report_standard_buttons(struct input_dev *, u8 buttons);
void psmouse_report_standard_motion(struct input_dev *, u8 *packet);
void psmouse_report_standard_packet(struct input_dev *, u8 *packet);
struct psmouse_attribute { struct psmouse_attribute {
struct device_attribute dattr; struct device_attribute dattr;
void *data; void *data;

View File

@ -710,7 +710,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
unsigned char button_status = 0, lscroll = 0, rscroll = 0; unsigned char button_status = 0, lscroll = 0, rscroll = 0;
unsigned short abs_x, abs_y, fgrs = 0; unsigned short abs_x, abs_y, fgrs = 0;
int rel_x, rel_y;
if (psmouse->pktcnt < 4) if (psmouse->pktcnt < 4)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -840,15 +839,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
/* /*
* Standard PS/2 Mouse * Standard PS/2 Mouse
*/ */
input_report_key(dev, BTN_LEFT, packet[0] & 1); psmouse_report_standard_packet(dev, packet);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
input_report_rel(dev, REL_X, rel_x);
input_report_rel(dev, REL_Y, rel_y);
break; break;
} }

View File

@ -84,7 +84,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
u8 param[1]; u8 param[1];
int error; int error;
error = psmouse_sliced_command(psmouse, mode); error = ps2_sliced_command(&psmouse->ps2dev, mode);
if (error) if (error)
return error; return error;
@ -189,7 +189,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
{ {
int error; int error;
error = psmouse_sliced_command(psmouse, cmd); error = ps2_sliced_command(&psmouse->ps2dev, cmd);
if (error) if (error)
return error; return error;
@ -546,7 +546,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static u8 param = 0xc8; static u8 param = 0xc8;
int error; int error;
error = psmouse_sliced_command(psmouse, SYN_QUE_MODEL); error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL);
if (error) if (error)
return error; return error;
@ -613,7 +613,7 @@ static int synaptics_pt_write(struct serio *serio, u8 c)
u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
int error; int error;
error = psmouse_sliced_command(parent, c); error = ps2_sliced_command(&parent->ps2dev, c);
if (error) if (error)
return error; return error;
@ -1227,32 +1227,39 @@ static void set_abs_position_params(struct input_dev *dev,
input_abs_set_res(dev, y_code, info->y_res); input_abs_set_res(dev, y_code, info->y_res);
} }
static void set_input_params(struct psmouse *psmouse, static int set_input_params(struct psmouse *psmouse,
struct synaptics_data *priv) struct synaptics_data *priv)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct synaptics_device_info *info = &priv->info; struct synaptics_device_info *info = &priv->info;
int i; int i;
int error;
/* Reset default psmouse capabilities */
__clear_bit(EV_REL, dev->evbit);
bitmap_zero(dev->relbit, REL_CNT);
bitmap_zero(dev->keybit, KEY_CNT);
/* Things that apply to both modes */ /* Things that apply to both modes */
__set_bit(INPUT_PROP_POINTER, dev->propbit); __set_bit(INPUT_PROP_POINTER, dev->propbit);
__set_bit(EV_KEY, dev->evbit);
__set_bit(BTN_LEFT, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
if (SYN_CAP_MIDDLE_BUTTON(info->capabilities)) input_set_capability(dev, EV_KEY, BTN_LEFT);
__set_bit(BTN_MIDDLE, dev->keybit);
/* Clickpads report only left button */
if (!SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
input_set_capability(dev, EV_KEY, BTN_RIGHT);
if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
input_set_capability(dev, EV_KEY, BTN_MIDDLE);
}
if (!priv->absolute_mode) { if (!priv->absolute_mode) {
/* Relative mode */ /* Relative mode */
__set_bit(EV_REL, dev->evbit); input_set_capability(dev, EV_REL, REL_X);
__set_bit(REL_X, dev->relbit); input_set_capability(dev, EV_REL, REL_Y);
__set_bit(REL_Y, dev->relbit); return 0;
return;
} }
/* Absolute mode */ /* Absolute mode */
__set_bit(EV_ABS, dev->evbit);
set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y); set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
@ -1264,11 +1271,15 @@ static void set_input_params(struct psmouse *psmouse,
ABS_MT_POSITION_X, ABS_MT_POSITION_Y); ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */ /* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
error = input_mt_init_slots(dev, 2,
INPUT_MT_POINTER | INPUT_MT_TRACK);
if (error)
return error;
/* Image sensors can signal 4 and 5 finger clicks */ /* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP);
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOOL_QUINTTAP);
} else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) { } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
set_abs_position_params(dev, info, set_abs_position_params(dev, info,
ABS_MT_POSITION_X, ABS_MT_POSITION_Y); ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
@ -1276,10 +1287,13 @@ static void set_input_params(struct psmouse *psmouse,
* Profile sensor in CR-48 tracks contacts reasonably well, * Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt. * other non-image sensors with AGM use semi-mt.
*/ */
input_mt_init_slots(dev, 2, error = input_mt_init_slots(dev, 2,
INPUT_MT_POINTER | INPUT_MT_POINTER |
(cr48_profile_sensor ? (cr48_profile_sensor ?
INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); INPUT_MT_TRACK :
INPUT_MT_SEMI_MT));
if (error)
return error;
/* /*
* For semi-mt devices we send ABS_X/Y ourselves instead of * For semi-mt devices we send ABS_X/Y ourselves instead of
@ -1295,37 +1309,32 @@ static void set_input_params(struct psmouse *psmouse,
if (SYN_CAP_PALMDETECT(info->capabilities)) if (SYN_CAP_PALMDETECT(info->capabilities))
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
__set_bit(BTN_TOUCH, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOUCH);
__set_bit(BTN_TOOL_FINGER, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOOL_FINGER);
if (synaptics_has_multifinger(priv)) { if (synaptics_has_multifinger(priv)) {
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOOL_DOUBLETAP);
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); input_set_capability(dev, EV_KEY, BTN_TOOL_TRIPLETAP);
} }
if (SYN_CAP_FOUR_BUTTON(info->capabilities) || if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
SYN_CAP_MIDDLE_BUTTON(info->capabilities)) { SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
__set_bit(BTN_FORWARD, dev->keybit); input_set_capability(dev, EV_KEY, BTN_FORWARD);
__set_bit(BTN_BACK, dev->keybit); input_set_capability(dev, EV_KEY, BTN_BACK);
} }
if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++) for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
__set_bit(BTN_0 + i, dev->keybit); input_set_capability(dev, EV_KEY, BTN_0 + i);
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) { if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10)) !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
/* Clickpads report only left button */
__clear_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
} }
return 0;
} }
static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse, static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
@ -1563,7 +1572,12 @@ static int synaptics_init_ps2(struct psmouse *psmouse,
info->capabilities, info->ext_cap, info->ext_cap_0c, info->capabilities, info->ext_cap, info->ext_cap_0c,
info->ext_cap_10, info->board_id, info->firmware_id); info->ext_cap_10, info->board_id, info->firmware_id);
set_input_params(psmouse, priv); err = set_input_params(psmouse, priv);
if (err) {
psmouse_err(psmouse,
"failed to set up capabilities: %d\n", err);
goto init_fail;
}
/* /*
* Encode touchpad model so that it can be used to set * Encode touchpad model so that it can be used to set

View File

@ -82,6 +82,10 @@ struct synusb {
struct urb *urb; struct urb *urb;
unsigned char *data; unsigned char *data;
/* serialize access to open/suspend */
struct mutex pm_mutex;
bool is_open;
/* input device related data structures */ /* input device related data structures */
struct input_dev *input; struct input_dev *input;
char name[128]; char name[128];
@ -252,6 +256,7 @@ static int synusb_open(struct input_dev *dev)
return retval; return retval;
} }
mutex_lock(&synusb->pm_mutex);
retval = usb_submit_urb(synusb->urb, GFP_KERNEL); retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
if (retval) { if (retval) {
dev_err(&synusb->intf->dev, dev_err(&synusb->intf->dev,
@ -262,8 +267,10 @@ static int synusb_open(struct input_dev *dev)
} }
synusb->intf->needs_remote_wakeup = 1; synusb->intf->needs_remote_wakeup = 1;
synusb->is_open = true;
out: out:
mutex_unlock(&synusb->pm_mutex);
usb_autopm_put_interface(synusb->intf); usb_autopm_put_interface(synusb->intf);
return retval; return retval;
} }
@ -275,8 +282,11 @@ static void synusb_close(struct input_dev *dev)
autopm_error = usb_autopm_get_interface(synusb->intf); autopm_error = usb_autopm_get_interface(synusb->intf);
mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb); usb_kill_urb(synusb->urb);
synusb->intf->needs_remote_wakeup = 0; synusb->intf->needs_remote_wakeup = 0;
synusb->is_open = false;
mutex_unlock(&synusb->pm_mutex);
if (!autopm_error) if (!autopm_error)
usb_autopm_put_interface(synusb->intf); usb_autopm_put_interface(synusb->intf);
@ -315,6 +325,7 @@ static int synusb_probe(struct usb_interface *intf,
synusb->udev = udev; synusb->udev = udev;
synusb->intf = intf; synusb->intf = intf;
synusb->input = input_dev; synusb->input = input_dev;
mutex_init(&synusb->pm_mutex);
synusb->flags = id->driver_info; synusb->flags = id->driver_info;
if (synusb->flags & SYNUSB_COMBO) { if (synusb->flags & SYNUSB_COMBO) {
@ -466,11 +477,10 @@ static void synusb_disconnect(struct usb_interface *intf)
static int synusb_suspend(struct usb_interface *intf, pm_message_t message) static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
{ {
struct synusb *synusb = usb_get_intfdata(intf); struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
mutex_lock(&input_dev->mutex); mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb); usb_kill_urb(synusb->urb);
mutex_unlock(&input_dev->mutex); mutex_unlock(&synusb->pm_mutex);
return 0; return 0;
} }
@ -478,17 +488,16 @@ static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
static int synusb_resume(struct usb_interface *intf) static int synusb_resume(struct usb_interface *intf)
{ {
struct synusb *synusb = usb_get_intfdata(intf); struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
int retval = 0; int retval = 0;
mutex_lock(&input_dev->mutex); mutex_lock(&synusb->pm_mutex);
if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO; retval = -EIO;
} }
mutex_unlock(&input_dev->mutex); mutex_unlock(&synusb->pm_mutex);
return retval; return retval;
} }
@ -496,9 +505,8 @@ static int synusb_resume(struct usb_interface *intf)
static int synusb_pre_reset(struct usb_interface *intf) static int synusb_pre_reset(struct usb_interface *intf)
{ {
struct synusb *synusb = usb_get_intfdata(intf); struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
mutex_lock(&input_dev->mutex); mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb); usb_kill_urb(synusb->urb);
return 0; return 0;
@ -507,15 +515,14 @@ static int synusb_pre_reset(struct usb_interface *intf)
static int synusb_post_reset(struct usb_interface *intf) static int synusb_post_reset(struct usb_interface *intf)
{ {
struct synusb *synusb = usb_get_intfdata(intf); struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
int retval = 0; int retval = 0;
if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO; retval = -EIO;
} }
mutex_unlock(&input_dev->mutex); mutex_unlock(&synusb->pm_mutex);
return retval; return retval;
} }

View File

@ -33,18 +33,15 @@ static const char * const trackpoint_variants[] = {
*/ */
static int trackpoint_power_on_reset(struct ps2dev *ps2dev) static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
{ {
u8 results[2]; u8 param[2] = { TP_POR };
int tries = 0; int err;
/* Issue POR command, and repeat up to once if 0xFC00 received */ err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND));
do { if (err)
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || return err;
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
return -1;
} while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
/* Check for success response -- 0xAA00 */ /* Check for success response -- 0xAA00 */
if (results[0] != 0xAA || results[1] != 0x00) if (param[0] != 0xAA || param[1] != 0x00)
return -ENODEV; return -ENODEV;
return 0; return 0;
@ -55,49 +52,39 @@ static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
*/ */
static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results) static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results)
{ {
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || results[0] = loc;
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
return -1;
}
return 0; return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND));
} }
static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val) static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val)
{ {
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || u8 param[3] = { TP_WRITE_MEM, loc, val };
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
return -1;
}
return 0; return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
} }
static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask) static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask)
{ {
u8 param[3] = { TP_TOGGLE, loc, mask };
/* Bad things will happen if the loc param isn't in this range */ /* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F) if (loc < 0x20 || loc >= 0x2F)
return -1; return -EINVAL;
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
return -1;
}
return 0;
} }
static int trackpoint_update_bit(struct ps2dev *ps2dev, static int trackpoint_update_bit(struct ps2dev *ps2dev,
u8 loc, u8 mask, u8 value) u8 loc, u8 mask, u8 value)
{ {
int retval = 0; int retval;
u8 data; u8 data;
trackpoint_read(ps2dev, loc, &data); retval = trackpoint_read(ps2dev, loc, &data);
if (retval)
return retval;
if (((data & mask) == mask) != !!value) if (((data & mask) == mask) != !!value)
retval = trackpoint_toggle_bit(ps2dev, loc, mask); retval = trackpoint_toggle_bit(ps2dev, loc, mask);
@ -142,9 +129,9 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
return err; return err;
*field = value; *field = value;
trackpoint_write(&psmouse->ps2dev, attr->command, value); err = trackpoint_write(&psmouse->ps2dev, attr->command, value);
return count; return err ?: count;
} }
#define TRACKPOINT_INT_ATTR(_name, _command, _default) \ #define TRACKPOINT_INT_ATTR(_name, _command, _default) \
@ -175,10 +162,11 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
if (*field != value) { if (*field != value) {
*field = value; *field = value;
trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask); err = trackpoint_toggle_bit(&psmouse->ps2dev,
attr->command, attr->mask);
} }
return count; return err ?: count;
} }

View File

@ -530,6 +530,20 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{ } { }
}; };
static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = {
{
/*
* Sony Vaio VGN-CS series require MUX or the touch sensor
* buttons will disturb touchpad operation
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"),
},
},
{ }
};
/* /*
* On some Asus laptops, just running self tests cause problems. * On some Asus laptops, just running self tests cause problems.
*/ */
@ -620,6 +634,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "20046"), DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
}, },
}, },
{
/* Lenovo ThinkPad L460 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"),
},
},
{ {
/* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
.matches = { .matches = {
@ -1163,6 +1184,9 @@ static int __init i8042_platform_init(void)
if (dmi_check_system(i8042_dmi_nomux_table)) if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true; i8042_nomux = true;
if (dmi_check_system(i8042_dmi_forcemux_table))
i8042_nomux = false;
if (dmi_check_system(i8042_dmi_notimeout_table)) if (dmi_check_system(i8042_dmi_notimeout_table))
i8042_notimeout = true; i8042_notimeout = true;

View File

@ -26,31 +26,79 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library"); MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
unsigned int timeout, unsigned int max_attempts)
__releases(&ps2dev->serio->lock) __acquires(&ps2dev->serio->lock)
{
int attempt = 0;
int error;
lockdep_assert_held(&ps2dev->serio->lock);
do {
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
error = serio_write(ps2dev->serio, byte);
if (error)
dev_dbg(&ps2dev->serio->dev,
"failed to write %#02x: %d\n", byte, error);
else
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio);
} while (ps2dev->nak == PS2_RET_NAK && ++attempt < max_attempts);
ps2dev->flags &= ~PS2_FLAG_ACK;
if (!error) {
switch (ps2dev->nak) {
case 0:
break;
case PS2_RET_NAK:
error = -EAGAIN;
break;
case PS2_RET_ERR:
error = -EPROTO;
break;
default:
error = -EIO;
break;
}
}
if (error || attempt > 1)
dev_dbg(&ps2dev->serio->dev,
"%02x - %d (%x), attempt %d\n",
byte, error, ps2dev->nak, attempt);
return error;
}
/* /*
* ps2_sendbyte() sends a byte to the device and waits for acknowledge. * ps2_sendbyte() sends a byte to the device and waits for acknowledge.
* It doesn't handle retransmission, though it could - because if there * It doesn't handle retransmission, the caller is expected to handle
* is a need for retransmissions device has to be replaced anyway. * it when needed.
* *
* ps2_sendbyte() can only be called from a process context. * ps2_sendbyte() can only be called from a process context.
*/ */
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout)
{ {
serio_pause_rx(ps2dev->serio); int retval;
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
if (serio_write(ps2dev->serio, byte) == 0)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags &= ~PS2_FLAG_ACK;
retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1);
dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak);
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
return -ps2dev->nak; return retval;
} }
EXPORT_SYMBOL(ps2_sendbyte); EXPORT_SYMBOL(ps2_sendbyte);
@ -75,7 +123,7 @@ EXPORT_SYMBOL(ps2_end_command);
* and discards them. * and discards them.
*/ */
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
{ {
if (maxbytes > sizeof(ps2dev->cmdbuf)) { if (maxbytes > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1); WARN_ON(1);
@ -102,9 +150,9 @@ EXPORT_SYMBOL(ps2_drain);
* known keyboard IDs. * known keyboard IDs.
*/ */
int ps2_is_keyboard_id(char id_byte) bool ps2_is_keyboard_id(u8 id_byte)
{ {
static const char keyboard_ids[] = { static const u8 keyboard_ids[] = {
0xab, /* Regular keyboards */ 0xab, /* Regular keyboards */
0xac, /* NCD Sun keyboard */ 0xac, /* NCD Sun keyboard */
0x2b, /* Trust keyboard, translated */ 0x2b, /* Trust keyboard, translated */
@ -123,49 +171,50 @@ EXPORT_SYMBOL(ps2_is_keyboard_id);
* completion. * completion.
*/ */
static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) static int ps2_adjust_timeout(struct ps2dev *ps2dev,
unsigned int command, unsigned int timeout)
{ {
switch (command) { switch (command) {
case PS2_CMD_RESET_BAT: case PS2_CMD_RESET_BAT:
/* /*
* Device has sent the first response byte after * Device has sent the first response byte after
* reset command, reset is thus done, so we can * reset command, reset is thus done, so we can
* shorten the timeout. * shorten the timeout.
* The next byte will come soon (keyboard) or not * The next byte will come soon (keyboard) or not
* at all (mouse). * at all (mouse).
*/ */
if (timeout > msecs_to_jiffies(100)) if (timeout > msecs_to_jiffies(100))
timeout = msecs_to_jiffies(100); timeout = msecs_to_jiffies(100);
break; break;
case PS2_CMD_GETID: case PS2_CMD_GETID:
/* /*
* Microsoft Natural Elite keyboard responds to * Microsoft Natural Elite keyboard responds to
* the GET ID command as it were a mouse, with * the GET ID command as it were a mouse, with
* a single byte. Fail the command so atkbd will * a single byte. Fail the command so atkbd will
* use alternative probe to detect it. * use alternative probe to detect it.
*/ */
if (ps2dev->cmdbuf[1] == 0xaa) { if (ps2dev->cmdbuf[1] == 0xaa) {
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0; ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
timeout = 0; timeout = 0;
} }
/* /*
* If device behind the port is not a keyboard there * If device behind the port is not a keyboard there
* won't be 2nd byte of ID response. * won't be 2nd byte of ID response.
*/ */
if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = ps2dev->cmdcnt = 0; ps2dev->flags = ps2dev->cmdcnt = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
timeout = 0; timeout = 0;
} }
break; break;
default: default:
break; break;
} }
return timeout; return timeout;
@ -178,50 +227,60 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context * ps2_command() can only be called from a process context
*/ */
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{ {
int timeout; unsigned int timeout;
int send = (command >> 12) & 0xf; unsigned int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf; unsigned int receive = (command >> 8) & 0xf;
int rc = -1; int rc;
int i; int i;
u8 send_param[16];
if (receive > sizeof(ps2dev->cmdbuf)) { if (receive > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1); WARN_ON(1);
return -1; return -EINVAL;
} }
if (send && !param) { if (send && !param) {
WARN_ON(1); WARN_ON(1);
return -1; return -EINVAL;
} }
memcpy(send_param, param, send);
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive; ps2dev->cmdcnt = receive;
if (receive && param) if (receive && param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i]; ps2dev->cmdbuf[(receive - 1) - i] = param[i];
serio_continue_rx(ps2dev->serio);
/* Signal that we are sending the command byte */
ps2dev->flags |= PS2_FLAG_ACK_CMD;
/* /*
* Some devices (Synaptics) peform the reset before * Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long * ACKing the reset command, and so it can take a long
* time before the ACK arrives. * time before the ACK arrives.
*/ */
if (ps2_sendbyte(ps2dev, command & 0xff, timeout = command == PS2_CMD_RESET_BAT ? 1000 : 200;
command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
serio_pause_rx(ps2dev->serio); rc = ps2_do_sendbyte(ps2dev, command & 0xff, timeout, 2);
if (rc)
goto out_reset_flags; goto out_reset_flags;
}
/* Now we are sending command parameters, if any */
ps2dev->flags &= ~PS2_FLAG_ACK_CMD;
for (i = 0; i < send; i++) { for (i = 0; i < send; i++) {
if (ps2_sendbyte(ps2dev, param[i], 200)) { rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2);
serio_pause_rx(ps2dev->serio); if (rc)
goto out_reset_flags; goto out_reset_flags;
}
} }
serio_continue_rx(ps2dev->serio);
/* /*
* The reset command takes a long time to execute. * The reset command takes a long time to execute.
*/ */
@ -243,8 +302,11 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i]; param[i] = ps2dev->cmdbuf[(receive - 1) - i];
if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) if (ps2dev->cmdcnt &&
(command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) {
rc = -EPROTO;
goto out_reset_flags; goto out_reset_flags;
}
rc = 0; rc = 0;
@ -252,11 +314,21 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0; ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
return rc; dev_dbg(&ps2dev->serio->dev,
"%02x [%*ph] - %x/%08lx [%*ph]\n",
command & 0xff, send, send_param,
ps2dev->nak, ps2dev->flags,
receive, param ?: send_param);
/*
* ps_command() handles resends itself, so do not leak -EAGAIN
* to the callers.
*/
return rc != -EAGAIN ? rc : -EPROTO;
} }
EXPORT_SYMBOL(__ps2_command); EXPORT_SYMBOL(__ps2_command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{ {
int rc; int rc;
@ -268,6 +340,39 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
} }
EXPORT_SYMBOL(ps2_command); EXPORT_SYMBOL(ps2_command);
/*
* ps2_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int ps2_sliced_command(struct ps2dev *ps2dev, u8 command)
{
int i;
int retval;
ps2_begin_command(ps2dev);
retval = __ps2_command(ps2dev, NULL, PS2_CMD_SETSCALE11);
if (retval)
goto out;
for (i = 6; i >= 0; i -= 2) {
u8 d = (command >> i) & 3;
retval = __ps2_command(ps2dev, &d, PS2_CMD_SETRES);
if (retval)
break;
}
out:
dev_dbg(&ps2dev->serio->dev, "%02x - %d\n", command, retval);
ps2_end_command(ps2dev);
return retval;
}
EXPORT_SYMBOL(ps2_sliced_command);
/* /*
* ps2_init() initializes ps2dev structure * ps2_init() initializes ps2dev structure
*/ */
@ -286,42 +391,53 @@ EXPORT_SYMBOL(ps2_init);
* to properly process ACK/NAK of a command from a PS/2 device. * to properly process ACK/NAK of a command from a PS/2 device.
*/ */
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
{ {
switch (data) { switch (data) {
case PS2_RET_ACK: case PS2_RET_ACK:
ps2dev->nak = 0;
break;
case PS2_RET_NAK:
ps2dev->flags |= PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_NAK;
break;
case PS2_RET_ERR:
if (ps2dev->flags & PS2_FLAG_NAK) {
ps2dev->flags &= ~PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_ERR;
break;
}
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0; ps2dev->nak = 0;
break; break;
}
case PS2_RET_NAK: /* Fall through */
ps2dev->flags |= PS2_FLAG_NAK; default:
ps2dev->nak = PS2_RET_NAK;
break;
case PS2_RET_ERR:
if (ps2dev->flags & PS2_FLAG_NAK) {
ps2dev->flags &= ~PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_ERR;
break;
}
/* /*
* Workaround for mice which don't ACK the Get ID command. * Do not signal errors if we get unexpected reply while
* These are valid mouse IDs that we recognize. * waiting for an ACK to the initial (first) command byte:
* the device might not be quiesced yet and continue
* delivering data.
* Note that we reset PS2_FLAG_WAITID flag, so the workaround
* for mice not acknowledging the Get ID command only triggers
* on the 1st byte; if device spews data we really want to see
* a real ACK from it.
*/ */
case 0x00: dev_dbg(&ps2dev->serio->dev, "unexpected %#02x\n", data);
case 0x03: ps2dev->flags &= ~PS2_FLAG_WAITID;
case 0x04: return ps2dev->flags & PS2_FLAG_ACK_CMD;
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0;
break;
}
/* Fall through */
default:
return 0;
} }
if (!ps2dev->nak) { if (!ps2dev->nak) {
ps2dev->flags &= ~PS2_FLAG_NAK; ps2dev->flags &= ~PS2_FLAG_NAK;
if (ps2dev->cmdcnt) if (ps2dev->cmdcnt)
@ -334,7 +450,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
if (data != PS2_RET_ACK) if (data != PS2_RET_ACK)
ps2_handle_response(ps2dev, data); ps2_handle_response(ps2dev, data);
return 1; return true;
} }
EXPORT_SYMBOL(ps2_handle_ack); EXPORT_SYMBOL(ps2_handle_ack);
@ -344,7 +460,7 @@ EXPORT_SYMBOL(ps2_handle_ack);
* waiting for completion of the command. * waiting for completion of the command.
*/ */
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) bool ps2_handle_response(struct ps2dev *ps2dev, u8 data)
{ {
if (ps2dev->cmdcnt) if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
@ -360,7 +476,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
wake_up(&ps2dev->wait); wake_up(&ps2dev->wait);
} }
return 1; return true;
} }
EXPORT_SYMBOL(ps2_handle_response); EXPORT_SYMBOL(ps2_handle_response);

View File

@ -41,6 +41,7 @@
#include <linux/usb/input.h> #include <linux/usb/input.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/mutex.h>
/* USB HID defines */ /* USB HID defines */
#define USB_REQ_GET_REPORT 0x01 #define USB_REQ_GET_REPORT 0x01
@ -76,6 +77,11 @@ struct pegasus {
struct usb_device *usbdev; struct usb_device *usbdev;
struct usb_interface *intf; struct usb_interface *intf;
struct urb *irq; struct urb *irq;
/* serialize access to open/suspend */
struct mutex pm_mutex;
bool is_open;
char name[128]; char name[128];
char phys[64]; char phys[64];
struct work_struct init; struct work_struct init;
@ -216,6 +222,7 @@ static int pegasus_open(struct input_dev *dev)
if (error) if (error)
return error; return error;
mutex_lock(&pegasus->pm_mutex);
pegasus->irq->dev = pegasus->usbdev; pegasus->irq->dev = pegasus->usbdev;
if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) { if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
error = -EIO; error = -EIO;
@ -226,12 +233,15 @@ static int pegasus_open(struct input_dev *dev)
if (error) if (error)
goto err_kill_urb; goto err_kill_urb;
pegasus->is_open = true;
mutex_unlock(&pegasus->pm_mutex);
return 0; return 0;
err_kill_urb: err_kill_urb:
usb_kill_urb(pegasus->irq); usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init); cancel_work_sync(&pegasus->init);
err_autopm_put: err_autopm_put:
mutex_unlock(&pegasus->pm_mutex);
usb_autopm_put_interface(pegasus->intf); usb_autopm_put_interface(pegasus->intf);
return error; return error;
} }
@ -240,8 +250,12 @@ static void pegasus_close(struct input_dev *dev)
{ {
struct pegasus *pegasus = input_get_drvdata(dev); struct pegasus *pegasus = input_get_drvdata(dev);
mutex_lock(&pegasus->pm_mutex);
usb_kill_urb(pegasus->irq); usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init); cancel_work_sync(&pegasus->init);
pegasus->is_open = false;
mutex_unlock(&pegasus->pm_mutex);
usb_autopm_put_interface(pegasus->intf); usb_autopm_put_interface(pegasus->intf);
} }
@ -274,6 +288,8 @@ static int pegasus_probe(struct usb_interface *intf,
goto err_free_mem; goto err_free_mem;
} }
mutex_init(&pegasus->pm_mutex);
pegasus->usbdev = dev; pegasus->usbdev = dev;
pegasus->dev = input_dev; pegasus->dev = input_dev;
pegasus->intf = intf; pegasus->intf = intf;
@ -388,10 +404,10 @@ static int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
{ {
struct pegasus *pegasus = usb_get_intfdata(intf); struct pegasus *pegasus = usb_get_intfdata(intf);
mutex_lock(&pegasus->dev->mutex); mutex_lock(&pegasus->pm_mutex);
usb_kill_urb(pegasus->irq); usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init); cancel_work_sync(&pegasus->init);
mutex_unlock(&pegasus->dev->mutex); mutex_unlock(&pegasus->pm_mutex);
return 0; return 0;
} }
@ -401,10 +417,10 @@ static int pegasus_resume(struct usb_interface *intf)
struct pegasus *pegasus = usb_get_intfdata(intf); struct pegasus *pegasus = usb_get_intfdata(intf);
int retval = 0; int retval = 0;
mutex_lock(&pegasus->dev->mutex); mutex_lock(&pegasus->pm_mutex);
if (pegasus->dev->users && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) if (pegasus->is_open && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
retval = -EIO; retval = -EIO;
mutex_unlock(&pegasus->dev->mutex); mutex_unlock(&pegasus->pm_mutex);
return retval; return retval;
} }
@ -414,14 +430,14 @@ static int pegasus_reset_resume(struct usb_interface *intf)
struct pegasus *pegasus = usb_get_intfdata(intf); struct pegasus *pegasus = usb_get_intfdata(intf);
int retval = 0; int retval = 0;
mutex_lock(&pegasus->dev->mutex); mutex_lock(&pegasus->pm_mutex);
if (pegasus->dev->users) { if (pegasus->is_open) {
retval = pegasus_set_mode(pegasus, PEN_MODE_XY, retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
NOTETAKER_LED_MOUSE); NOTETAKER_LED_MOUSE);
if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
retval = -EIO; retval = -EIO;
} }
mutex_unlock(&pegasus->dev->mutex); mutex_unlock(&pegasus->pm_mutex);
return retval; return retval;
} }

View File

@ -2,7 +2,7 @@
// Samsung S6SY761 Touchscreen device driver // Samsung S6SY761 Touchscreen device driver
// //
// Copyright (c) 2017 Samsung Electronics Co., Ltd. // Copyright (c) 2017 Samsung Electronics Co., Ltd.
// Copyright (c) 2017 Andi Shyti <andi.shyti@samsung.com> // Copyright (c) 2017 Andi Shyti <andi@etezian.org>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <linux/delay.h> #include <linux/delay.h>

View File

@ -602,6 +602,7 @@ static const struct acpi_device_id silead_ts_acpi_match[] = {
{ "GSL3675", 0 }, { "GSL3675", 0 },
{ "GSL3692", 0 }, { "GSL3692", 0 },
{ "MSSL1680", 0 }, { "MSSL1680", 0 },
{ "MSSL0001", 0 },
{ } { }
}; };
MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match); MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match);

View File

@ -2,7 +2,7 @@
// STMicroelectronics FTS Touchscreen device driver // STMicroelectronics FTS Touchscreen device driver
// //
// Copyright (c) 2017 Samsung Electronics Co., Ltd. // Copyright (c) 2017 Samsung Electronics Co., Ltd.
// Copyright (c) 2017 Andi Shyti <andi.shyti@samsung.com> // Copyright (c) 2017 Andi Shyti <andi@etezian.org>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
@ -730,6 +730,7 @@ static int stmfts_probe(struct i2c_client *client,
return err; return err;
pm_runtime_enable(&client->dev); pm_runtime_enable(&client->dev);
device_enable_async_suspend(&client->dev);
return 0; return 0;
} }
@ -805,6 +806,7 @@ static struct i2c_driver stmfts_driver = {
.name = STMFTS_DEV_NAME, .name = STMFTS_DEV_NAME,
.of_match_table = of_match_ptr(stmfts_of_match), .of_match_table = of_match_ptr(stmfts_of_match),
.pm = &stmfts_pm_ops, .pm = &stmfts_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
.probe = stmfts_probe, .probe = stmfts_probe,
.remove = stmfts_remove, .remove = stmfts_remove,

View File

@ -54,6 +54,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/input.h> #include <linux/usb/input.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/mutex.h>
static bool swap_xy; static bool swap_xy;
module_param(swap_xy, bool, 0644); module_param(swap_xy, bool, 0644);
@ -107,6 +108,8 @@ struct usbtouch_usb {
struct usb_interface *interface; struct usb_interface *interface;
struct input_dev *input; struct input_dev *input;
struct usbtouch_device_info *type; struct usbtouch_device_info *type;
struct mutex pm_mutex; /* serialize access to open/suspend */
bool is_open;
char name[128]; char name[128];
char phys[64]; char phys[64];
void *priv; void *priv;
@ -1450,6 +1453,7 @@ static int usbtouch_open(struct input_dev *input)
if (r < 0) if (r < 0)
goto out; goto out;
mutex_lock(&usbtouch->pm_mutex);
if (!usbtouch->type->irq_always) { if (!usbtouch->type->irq_always) {
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) { if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
r = -EIO; r = -EIO;
@ -1458,7 +1462,9 @@ static int usbtouch_open(struct input_dev *input)
} }
usbtouch->interface->needs_remote_wakeup = 1; usbtouch->interface->needs_remote_wakeup = 1;
usbtouch->is_open = true;
out_put: out_put:
mutex_unlock(&usbtouch->pm_mutex);
usb_autopm_put_interface(usbtouch->interface); usb_autopm_put_interface(usbtouch->interface);
out: out:
return r; return r;
@ -1469,8 +1475,12 @@ static void usbtouch_close(struct input_dev *input)
struct usbtouch_usb *usbtouch = input_get_drvdata(input); struct usbtouch_usb *usbtouch = input_get_drvdata(input);
int r; int r;
mutex_lock(&usbtouch->pm_mutex);
if (!usbtouch->type->irq_always) if (!usbtouch->type->irq_always)
usb_kill_urb(usbtouch->irq); usb_kill_urb(usbtouch->irq);
usbtouch->is_open = false;
mutex_unlock(&usbtouch->pm_mutex);
r = usb_autopm_get_interface(usbtouch->interface); r = usb_autopm_get_interface(usbtouch->interface);
usbtouch->interface->needs_remote_wakeup = 0; usbtouch->interface->needs_remote_wakeup = 0;
if (!r) if (!r)
@ -1490,13 +1500,12 @@ static int usbtouch_suspend
static int usbtouch_resume(struct usb_interface *intf) static int usbtouch_resume(struct usb_interface *intf)
{ {
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
struct input_dev *input = usbtouch->input;
int result = 0; int result = 0;
mutex_lock(&input->mutex); mutex_lock(&usbtouch->pm_mutex);
if (input->users || usbtouch->type->irq_always) if (usbtouch->is_open || usbtouch->type->irq_always)
result = usb_submit_urb(usbtouch->irq, GFP_NOIO); result = usb_submit_urb(usbtouch->irq, GFP_NOIO);
mutex_unlock(&input->mutex); mutex_unlock(&usbtouch->pm_mutex);
return result; return result;
} }
@ -1504,7 +1513,6 @@ static int usbtouch_resume(struct usb_interface *intf)
static int usbtouch_reset_resume(struct usb_interface *intf) static int usbtouch_reset_resume(struct usb_interface *intf)
{ {
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
struct input_dev *input = usbtouch->input;
int err = 0; int err = 0;
/* reinit the device */ /* reinit the device */
@ -1519,10 +1527,10 @@ static int usbtouch_reset_resume(struct usb_interface *intf)
} }
/* restart IO if needed */ /* restart IO if needed */
mutex_lock(&input->mutex); mutex_lock(&usbtouch->pm_mutex);
if (input->users) if (usbtouch->is_open)
err = usb_submit_urb(usbtouch->irq, GFP_NOIO); err = usb_submit_urb(usbtouch->irq, GFP_NOIO);
mutex_unlock(&input->mutex); mutex_unlock(&usbtouch->pm_mutex);
return err; return err;
} }

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides constants for gpio keys bindings.
*/
#ifndef _DT_BINDINGS_GPIO_KEYS_H
#define _DT_BINDINGS_GPIO_KEYS_H
#define EV_ACT_ANY 0x00 /* asserted or deasserted */
#define EV_ACT_ASSERTED 0x01 /* asserted */
#define EV_ACT_DEASSERTED 0x02 /* deasserted */
#endif /* _DT_BINDINGS_GPIO_KEYS_H */

View File

@ -13,6 +13,7 @@ struct device;
* @desc: label that will be attached to button's gpio * @desc: label that will be attached to button's gpio
* @type: input event type (%EV_KEY, %EV_SW, %EV_ABS) * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS)
* @wakeup: configure the button as a wake-up source * @wakeup: configure the button as a wake-up source
* @wakeup_event_action: event action to trigger wakeup
* @debounce_interval: debounce ticks interval in msecs * @debounce_interval: debounce ticks interval in msecs
* @can_disable: %true indicates that userspace is allowed to * @can_disable: %true indicates that userspace is allowed to
* disable button via sysfs * disable button via sysfs
@ -26,6 +27,7 @@ struct gpio_keys_button {
const char *desc; const char *desc;
unsigned int type; unsigned int type;
int wakeup; int wakeup;
int wakeup_event_action;
int debounce_interval; int debounce_interval;
bool can_disable; bool can_disable;
int value; int value;

View File

@ -10,7 +10,13 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/wait.h>
#define PS2_CMD_SETSCALE11 0x00e6
#define PS2_CMD_SETRES 0x10e8
#define PS2_CMD_GETID 0x02f2 #define PS2_CMD_GETID 0x02f2
#define PS2_CMD_RESET_BAT 0x02ff #define PS2_CMD_RESET_BAT 0x02ff
@ -20,11 +26,12 @@
#define PS2_RET_NAK 0xfe #define PS2_RET_NAK 0xfe
#define PS2_RET_ERR 0xfc #define PS2_RET_ERR 0xfc
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */ #define PS2_FLAG_ACK BIT(0) /* Waiting for ACK/NAK */
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */ #define PS2_FLAG_CMD BIT(1) /* Waiting for a command to finish */
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */ #define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */ #define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */
#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */ #define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */
#define PS2_FLAG_ACK_CMD BIT(5) /* Waiting to ACK the command (first) byte */
struct ps2dev { struct ps2dev {
struct serio *serio; struct serio *serio;
@ -36,21 +43,22 @@ struct ps2dev {
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned long flags; unsigned long flags;
unsigned char cmdbuf[8]; u8 cmdbuf[8];
unsigned char cmdcnt; u8 cmdcnt;
unsigned char nak; u8 nak;
}; };
void ps2_init(struct ps2dev *ps2dev, struct serio *serio); void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout);
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout);
void ps2_begin_command(struct ps2dev *ps2dev); void ps2_begin_command(struct ps2dev *ps2dev);
void ps2_end_command(struct ps2dev *ps2dev); void ps2_end_command(struct ps2dev *ps2dev);
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_sliced_command(struct ps2dev *ps2dev, u8 command);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data);
bool ps2_handle_response(struct ps2dev *ps2dev, u8 data);
void ps2_cmd_aborted(struct ps2dev *ps2dev); void ps2_cmd_aborted(struct ps2dev *ps2dev);
int ps2_is_keyboard_id(char id); bool ps2_is_keyboard_id(u8 id);
#endif /* _LIBPS2_H */ #endif /* _LIBPS2_H */