* Re: [GIT] HID for 3.12 merge window
From: Linus Torvalds @ 2013-09-06 23:57 UTC (permalink / raw)
To: David Herrmann
Cc: Markus Trippelsdorf, Jiri Kosina, linux-kernel,
open list:HID CORE LAYER, Dmitry Torokhov
In-Reply-To: <CANq1E4Rf4c4zj1mD1aPr93Q2_M65DCEJhuLqcBWa5uH9tcKuWA@mail.gmail.com>
On Fri, Sep 6, 2013 at 2:50 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Fri, Sep 6, 2013 at 10:20 PM, Markus Trippelsdorf
>>
>> commit 61e00655e9cb82e034eb72b95a51072e718d14a7
>> Author: David Herrmann <dh.herrmann@gmail.com>
>> Date: Mon Aug 26 19:14:46 2013 +0200
>>
>> Input: introduce BTN/ABS bits for drums and guitars
>>
>> The commit above breaks my Logitech mouse. The mouse cursor just sits in
>> the middle of the screen and doesn't react to movements. dmesg is
>> normal, but Xorg.0.log says:
>
> Ok, the issue is the kernel assumes ABS_MAX to be a power-of-2 minus 1
> (used as mask). That wasn't really obvious to me. Attached is a patch
> which should fix that. Could you apply it on top of linus/master and
> give it a try?
Gah. I just wasted too much time bisecting down my logitech wireless
keyboard not working to within a few commits of this, and decided to
just try your patch.
And yes, it makes my keyboard work.
Dmitry, should I just apply the patch, or should we revert and use
other bits? Please, this needs to be resolved, I stopped merging when
I noticed this problem..
Linus
^ permalink raw reply
* [PATCH v2] Input: add driver for Neonode zForce based touchscreens
From: Heiko Stübner @ 2013-09-06 23:33 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Henrik Rydberg, linux-kernel, linux-input
This adds a driver for touchscreens using the zforce infrared
technology from Neonode connected via i2c to the host system.
It supports multitouch with up to two fingers and tracking of the
contacts in hardware.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
changes since v1:
- address comments from Dmitry Torokhov
but I kept the access_mutex due to the possible race I described in the mail:
- touch -> interrupt
- isr reads first packet
- user closes device -> stop command sent
- isr reads payload
- address comments from Henrik Rydberg, letting the input system collect
singletouch from the multitouch data using the appropriate functions
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/zforce_ts.c | 826 +++++++++++++++++++++++++++++++
include/linux/platform_data/zforce_ts.h | 26 +
4 files changed, 866 insertions(+)
create mode 100644 drivers/input/touchscreen/zforce_ts.c
create mode 100644 include/linux/platform_data/zforce_ts.h
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3b9758b..ade11b7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -919,4 +919,17 @@ config TOUCHSCREEN_TPS6507X
To compile this driver as a module, choose M here: the
module will be called tps6507x_ts.
+config TOUCHSCREEN_ZFORCE
+ tristate "Neonode zForce infrared touchscreens"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a touchscreen using the zforce
+ infraread technology from Neonode.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zforce_ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index f5216c1..7587883 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
new file mode 100644
index 0000000..f4cfd4d
--- /dev/null
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based in parts on Nook zforce driver
+ *
+ * Copyright (C) 2010 Barnes & Noble, Inc.
+ * Author: Pieter Truter<ptruter@intrinsyc.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/input/mt.h>
+#include <linux/platform_data/zforce_ts.h>
+
+#define WAIT_TIMEOUT msecs_to_jiffies(1000)
+
+#define FRAME_START 0xee
+
+/* Offsets of the different parts of the payload the controller sends */
+#define PAYLOAD_HEADER 0
+#define PAYLOAD_LENGTH 1
+#define PAYLOAD_BODY 2
+
+/* Response offsets */
+#define RESPONSE_ID 0
+#define RESPONSE_DATA 1
+
+/* Commands */
+#define COMMAND_DEACTIVATE 0x00
+#define COMMAND_INITIALIZE 0x01
+#define COMMAND_RESOLUTION 0x02
+#define COMMAND_SETCONFIG 0x03
+#define COMMAND_DATAREQUEST 0x04
+#define COMMAND_SCANFREQ 0x08
+#define COMMAND_STATUS 0X1e
+
+/*
+ * Responses the controller sends as a result of
+ * command requests
+ */
+#define RESPONSE_DEACTIVATE 0x00
+#define RESPONSE_INITIALIZE 0x01
+#define RESPONSE_RESOLUTION 0x02
+#define RESPONSE_SETCONFIG 0x03
+#define RESPONSE_SCANFREQ 0x08
+#define RESPONSE_STATUS 0X1e
+
+/*
+ * Notifications are send by the touch controller without
+ * being requested by the driver and include for example
+ * touch indications
+ */
+#define NOTIFICATION_TOUCH 0x04
+#define NOTIFICATION_BOOTCOMPLETE 0x07
+#define NOTIFICATION_OVERRUN 0x25
+#define NOTIFICATION_PROXIMITY 0x26
+#define NOTIFICATION_INVALID_COMMAND 0xfe
+
+#define ZFORCE_REPORT_POINTS 2
+#define ZFORCE_MAX_AREA 0xff
+
+#define STATE_DOWN 0
+#define STATE_MOVE 1
+#define STATE_UP 2
+
+#define SETCONFIG_DUALTOUCH (1 << 0)
+
+struct zforce_point {
+ int coord_x;
+ int coord_y;
+ int state;
+ int id;
+ int area_major;
+ int area_minor;
+ int orientation;
+ int pressure;
+ int prblty;
+};
+
+/*
+ * @client the i2c_client
+ * @input the input device
+ * @suspending in the process of going to suspend (don't emit wakeup
+ * events for commands executed to suspend the device)
+ * @suspended device suspended
+ * @access_mutex serialize i2c-access, to keep multipart reads together
+ * @command_done completion to wait for the command result
+ * @command_mutex serialize commands send to the ic
+ * @command_waiting the id of the command that that is currently waiting
+ * for a result
+ * @command_result returned result of the command
+ */
+struct zforce_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ const struct zforce_ts_platdata *pdata;
+ char phys[32];
+
+ bool suspending;
+ bool suspended;
+ bool boot_complete;
+
+ /* Firmware version information */
+ u16 version_major;
+ u16 version_minor;
+ u16 version_build;
+ u16 version_rev;
+
+ struct mutex access_mutex;
+
+ struct completion command_done;
+ struct mutex command_mutex;
+ int command_waiting;
+ int command_result;
+};
+
+static int zforce_command(struct zforce_ts *ts, u8 cmd)
+{
+ struct i2c_client *client = ts->client;
+ char buf[3];
+ int ret;
+
+ dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+ buf[0] = FRAME_START;
+ buf[1] = 1; /* data size, command only */
+ buf[2] = cmd;
+
+ mutex_lock(&ts->access_mutex);
+ ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
+ mutex_unlock(&ts->access_mutex);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = mutex_trylock(&ts->command_mutex);
+ if (!ret) {
+ dev_err(&client->dev, "already waiting for a command\n");
+ return -EBUSY;
+ }
+
+ dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
+ buf[1], buf[2]);
+
+ ts->command_waiting = buf[2];
+
+ mutex_lock(&ts->access_mutex);
+ ret = i2c_master_send(client, buf, len);
+ mutex_unlock(&ts->access_mutex);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
+
+ if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {
+ ret = -ETIME;
+ goto unlock;
+ }
+
+ ret = ts->command_result;
+
+unlock:
+ mutex_unlock(&ts->command_mutex);
+ return ret;
+}
+
+static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
+{
+ struct i2c_client *client = ts->client;
+ char buf[3];
+ int ret;
+
+ dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+ buf[0] = FRAME_START;
+ buf[1] = 1; /* data size, command only */
+ buf[2] = cmd;
+
+ ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y)
+{
+ struct i2c_client *client = ts->client;
+ char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,
+ (x & 0xff), ((x >> 8) & 0xff),
+ (y & 0xff), ((y >> 8) & 0xff) };
+
+ dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
+ u16 stylus)
+{
+ struct i2c_client *client = ts->client;
+ char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,
+ (idle & 0xff), ((idle >> 8) & 0xff),
+ (finger & 0xff), ((finger >> 8) & 0xff),
+ (stylus & 0xff), ((stylus >> 8) & 0xff) };
+
+ dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
+ idle, finger, stylus);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_setconfig(struct zforce_ts *ts, char b1)
+{
+ struct i2c_client *client = ts->client;
+ char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,
+ b1, 0, 0, 0 };
+
+ dev_dbg(&client->dev, "set config to (%d)\n", b1);
+
+ return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_start(struct zforce_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ int ret;
+
+ dev_dbg(&client->dev, "starting device\n");
+
+ ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+ if (ret) {
+ dev_err(&client->dev, "Unable to initialize, %d\n", ret);
+ return ret;
+ }
+
+ ret = zforce_resolution(ts, pdata->x_max, pdata->y_max);
+ if (ret) {
+ dev_err(&client->dev, "Unable to set resolution, %d\n", ret);
+ goto error;
+ }
+
+ ret = zforce_scan_frequency(ts, 10, 50, 50);
+ if (ret) {
+ dev_err(&client->dev, "Unable to set scan frequency, %d\n",
+ ret);
+ goto error;
+ }
+
+ if (zforce_setconfig(ts, SETCONFIG_DUALTOUCH)) {
+ dev_err(&client->dev, "Unable to set config\n");
+ goto error;
+ }
+
+ /* start sending touch events */
+ ret = zforce_command(ts, COMMAND_DATAREQUEST);
+ if (ret) {
+ dev_err(&client->dev, "Unable to request data\n");
+ goto error;
+ }
+
+ /* Per NN, initial cal. take max. of 200msec.
+ * Allow time to complete this calibration
+ */
+ msleep(200);
+
+ return 0;
+
+error:
+ zforce_command_wait(ts, COMMAND_DEACTIVATE);
+ return ret;
+}
+
+static int zforce_stop(struct zforce_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ dev_dbg(&client->dev, "stopping device\n");
+
+ /* deactivates touch sensing and puts the device into sleep */
+ ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);
+ if (ret != 0) {
+ dev_err(&client->dev, "could not deactivate device, %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
+{
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ struct zforce_point point;
+ int count, i, num = 0;
+
+ count = payload[0];
+ if (count > ZFORCE_REPORT_POINTS) {
+ dev_warn(&client->dev, "to many coordinates %d, expected max %d\n",
+ count, ZFORCE_REPORT_POINTS);
+ count = ZFORCE_REPORT_POINTS;
+ }
+
+ for (i = 0; i < count; i++) {
+ point.coord_x =
+ payload[9 * i + 2] << 8 | payload[9 * i + 1];
+ point.coord_y =
+ payload[9 * i + 4] << 8 | payload[9 * i + 3];
+
+ if (point.coord_x > pdata->x_max ||
+ point.coord_y > pdata->y_max) {
+ dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+ point.coord_x, point.coord_y);
+ point.coord_x = point.coord_y = 0;
+ }
+
+ point.state = payload[9 * i + 5] & 0x03;
+ point.id = (payload[9 * i + 5] & 0xfc) >> 2;
+
+ /* determine touch major, minor and orientation */
+ point.area_major = max(payload[9 * i + 6],
+ payload[9 * i + 7]);
+ point.area_minor = min(payload[9 * i + 6],
+ payload[9 * i + 7]);
+ point.orientation = payload[9 * i + 6] > payload[9 * i + 7];
+
+ point.pressure = payload[9 * i + 8];
+ point.prblty = payload[9 * i + 9];
+
+ dev_dbg(&client->dev, "point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
+ i, count, point.state, point.id,
+ point.pressure, point.prblty,
+ point.coord_x, point.coord_y,
+ point.area_major, point.area_minor,
+ point.orientation);
+
+ /* the zforce id starts with "1", so needs to be decreased */
+ input_mt_slot(ts->input, point.id - 1);
+
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
+ point.state != STATE_UP);
+
+ if (point.state != STATE_UP) {
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ point.coord_x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ point.coord_y);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ point.area_major);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+ point.area_minor);
+ input_report_abs(ts->input, ABS_MT_ORIENTATION,
+ point.orientation);
+ num++;
+ }
+ }
+
+ input_mt_sync_frame(ts->input);
+
+ input_mt_report_finger_count(ts->input, num);
+
+ input_sync(ts->input);
+
+ return 0;
+}
+
+static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ mutex_lock(&ts->access_mutex);
+
+ /* read 2 byte message header */
+ ret = i2c_master_recv(client, buf, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "error reading header: %d\n", ret);
+ goto unlock;
+ }
+
+ if (buf[PAYLOAD_HEADER] != FRAME_START) {
+ dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) {
+ dev_err(&client->dev, "invalid payload length: %d\n",
+ buf[PAYLOAD_LENGTH]);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* read the message */
+ ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
+ if (ret < 0) {
+ dev_err(&client->dev, "error reading payload: %d\n", ret);
+ goto unlock;
+ }
+
+ dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
+ buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
+
+unlock:
+ mutex_unlock(&ts->access_mutex);
+ return ret;
+}
+
+static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
+{
+ struct i2c_client *client = ts->client;
+
+ if (ts->command_waiting == cmd) {
+ dev_dbg(&client->dev, "completing command 0x%x\n", cmd);
+ ts->command_result = result;
+ complete(&ts->command_done);
+ } else {
+ dev_dbg(&client->dev, "command %d not for us\n", cmd);
+ }
+}
+
+static irqreturn_t zforce_interrupt(int irq, void *dev_id)
+{
+ struct zforce_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ int ret;
+ u8 payload_buffer[512];
+ u8 *payload;
+
+ /*
+ * When suspended, emit a wakeup signal if necessary and return.
+ * Due to the level-interrupt we will get re-triggered later.
+ */
+ if (ts->suspended) {
+ if (device_may_wakeup(&client->dev))
+ pm_wakeup_event(&client->dev, 500);
+ msleep(20);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(&client->dev, "handling interrupt\n");
+
+ /* Don't emit wakeup events from commands run by zforce_suspend */
+ if (!ts->suspending && device_may_wakeup(&client->dev))
+ pm_stay_awake(&client->dev);
+
+ while (!gpio_get_value(pdata->gpio_int)) {
+ ret = zforce_read_packet(ts, payload_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not read packet, ret: %d\n",
+ ret);
+ break;
+ }
+
+ payload = &payload_buffer[PAYLOAD_BODY];
+
+ switch (payload[RESPONSE_ID]) {
+ case NOTIFICATION_TOUCH:
+ /* Always report touch-events received while
+ * suspending, when being a wakeup source
+ */
+ if (ts->suspending && device_may_wakeup(&client->dev))
+ pm_wakeup_event(&client->dev, 500);
+ zforce_touch_event(ts, &payload[RESPONSE_DATA]);
+ break;
+ case NOTIFICATION_BOOTCOMPLETE:
+ ts->boot_complete = payload[RESPONSE_DATA];
+ zforce_complete(ts, payload[RESPONSE_ID], 0);
+ break;
+ case RESPONSE_INITIALIZE:
+ case RESPONSE_DEACTIVATE:
+ case RESPONSE_SETCONFIG:
+ case RESPONSE_RESOLUTION:
+ case RESPONSE_SCANFREQ:
+ zforce_complete(ts, payload[RESPONSE_ID],
+ payload[RESPONSE_DATA]);
+ break;
+ case RESPONSE_STATUS:
+ /* Version Payload Results
+ * [2:major] [2:minor] [2:build] [2:rev]
+ */
+ ts->version_major = (payload[RESPONSE_DATA + 1] << 8) |
+ payload[RESPONSE_DATA];
+ ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) |
+ payload[RESPONSE_DATA + 2];
+ ts->version_build = (payload[RESPONSE_DATA + 5] << 8) |
+ payload[RESPONSE_DATA + 4];
+ ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) |
+ payload[RESPONSE_DATA + 6];
+ dev_dbg(&ts->client->dev, "Firmware Version %04x:%04x %04x:%04x\n",
+ ts->version_major, ts->version_minor,
+ ts->version_build, ts->version_rev);
+
+ zforce_complete(ts, payload[RESPONSE_ID], 0);
+ break;
+ case NOTIFICATION_INVALID_COMMAND:
+ dev_err(&ts->client->dev, "invalid command: 0x%x\n",
+ payload[RESPONSE_DATA]);
+ break;
+ default:
+ dev_err(&ts->client->dev, "unrecognized response id: 0x%x\n",
+ payload[RESPONSE_ID]);
+ break;
+ }
+ }
+
+ if (!ts->suspending && device_may_wakeup(&client->dev))
+ pm_relax(&client->dev);
+
+ dev_dbg(&client->dev, "finished interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static int zforce_input_open(struct input_dev *dev)
+{
+ struct zforce_ts *ts = input_get_drvdata(dev);
+ int ret;
+
+ ret = zforce_start(ts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void zforce_input_close(struct input_dev *dev)
+{
+ struct zforce_ts *ts = input_get_drvdata(dev);
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = zforce_stop(ts);
+ if (ret)
+ dev_warn(&client->dev, "stopping zforce failed\n");
+
+ return;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int zforce_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct zforce_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+ ts->suspending = true;
+
+ /* when configured as wakeup source, device should always wake system
+ * therefore start device if necessary
+ */
+ if (device_may_wakeup(&client->dev)) {
+ dev_dbg(&client->dev, "suspend while being a wakeup source\n");
+
+ /* need to start device if not open, to be wakeup source */
+ if (!input->users) {
+ ret = zforce_start(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ enable_irq_wake(client->irq);
+ } else if (input->users) {
+ dev_dbg(&client->dev, "suspend without being a wakeup source\n");
+
+ ret = zforce_stop(ts);
+ if (ret)
+ goto unlock;
+
+ disable_irq(client->irq);
+ }
+
+ ts->suspended = true;
+
+unlock:
+ ts->suspending = false;
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int zforce_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct zforce_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ ts->suspended = false;
+
+ if (device_may_wakeup(&client->dev)) {
+ dev_dbg(&client->dev, "resume from being a wakeup source\n");
+
+ disable_irq_wake(client->irq);
+
+ /* need to stop device if it was not open on suspend */
+ if (!input->users) {
+ ret = zforce_stop(ts);
+ if (ret)
+ goto unlock;
+ }
+ } else if (input->users) {
+ dev_dbg(&client->dev, "resume without being a wakeup source\n");
+
+ enable_irq(client->irq);
+
+ ret = zforce_start(ts);
+ if (ret < 0)
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
+
+static void zforce_reset(void *data)
+{
+ struct zforce_ts *ts = data;
+
+ gpio_set_value(ts->pdata->gpio_rst, 0);
+}
+
+static int zforce_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ struct zforce_ts *ts;
+ struct input_dev *input_dev;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN,
+ "zforce_ts_int");
+ if (ret) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_int, ret);
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+ GPIOF_OUT_INIT_LOW, "zforce_ts_rst");
+ if (ret) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_rst, ret);
+ return ret;
+ }
+
+ ret = devm_add_action(&client->dev, zforce_reset, ts);
+ if (ret) {
+ dev_err(&client->dev, "failed to register reset action, %d\n",
+ ret);
+ return ret;
+ }
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev) {
+ dev_err(&client->dev, "could not allocate input device\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&ts->access_mutex);
+ mutex_init(&ts->command_mutex);
+
+ ts->pdata = pdata;
+ ts->client = client;
+ ts->input = input_dev;
+
+ input_dev->name = "Neonode zForce touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_dev->open = zforce_input_open;
+ input_dev->close = zforce_input_close;
+
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_ABS, input_dev->evbit);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ ZFORCE_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+ ZFORCE_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT);
+
+ input_set_drvdata(ts->input, ts);
+
+ init_completion(&ts->command_done);
+
+ /* The zforce pulls the interrupt low when it has data ready.
+ * After it is triggered the isr thread runs until all the available
+ * packets have been read and the interrupt is high again.
+ * Therefore we can trigger the interrupt anytime it is low and do
+ * not need to limit it to the interrupt edge.
+ */
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ zforce_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ input_dev->name, ts);
+ if (ret) {
+ dev_err(&client->dev, "irq %d request failed\n", client->irq);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ /* let the controller boot */
+ gpio_set_value(pdata->gpio_rst, 1);
+
+ ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
+ if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
+ dev_warn(&client->dev, "bootcomplete timed out\n");
+
+ /* need to start device to get version information */
+ ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+ if (ret) {
+ dev_err(&client->dev, "unable to initialize, %d\n", ret);
+ return ret;
+ }
+
+ /* this gets the firmware version among other informations */
+ ret = zforce_command_wait(ts, COMMAND_STATUS);
+ if (ret < 0) {
+ dev_err(&client->dev, "couldn't get status, %d\n", ret);
+ zforce_stop(ts);
+ return ret;
+ }
+
+ /* stop device and put it into sleep until it is opened */
+ ret = zforce_stop(ts);
+ if (ret < 0)
+ return ret;
+
+ device_set_wakeup_capable(&client->dev, true);
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ dev_err(&client->dev, "could not register input device, %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct i2c_device_id zforce_idtable[] = {
+ { "zforce-ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, zforce_idtable);
+
+static struct i2c_driver zforce_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "zforce-ts",
+ .pm = &zforce_pm_ops,
+ },
+ .probe = zforce_probe,
+ .id_table = zforce_idtable,
+};
+
+module_i2c_driver(zforce_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("zForce TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/zforce_ts.h b/include/linux/platform_data/zforce_ts.h
new file mode 100644
index 0000000..0472ab2
--- /dev/null
+++ b/include/linux/platform_data/zforce_ts.h
@@ -0,0 +1,26 @@
+/* drivers/input/touchscreen/zforce.c
+ *
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_INPUT_ZFORCE_TS_H
+#define _LINUX_INPUT_ZFORCE_TS_H
+
+struct zforce_ts_platdata {
+ int gpio_int;
+ int gpio_rst;
+
+ unsigned int x_max;
+ unsigned int y_max;
+};
+
+#endif /* _LINUX_INPUT_ZFORCE_TS_H */
--
1.7.10.4
^ permalink raw reply related
* Re: [GIT] HID for 3.12 merge window
From: Dmitry Torokhov @ 2013-09-06 23:10 UTC (permalink / raw)
To: David Herrmann
Cc: Markus Trippelsdorf, Jiri Kosina, Linus Torvalds, linux-kernel,
open list:HID CORE LAYER
In-Reply-To: <CANq1E4Q4mxKVJJEPNSb9kvUoMXc3KoAnxjnKd0Vx+hoGaeN=kg@mail.gmail.com>
On Sat, Sep 07, 2013 at 12:51:27AM +0200, David Herrmann wrote:
> Hi
>
> On Fri, Sep 6, 2013 at 11:59 PM, Markus Trippelsdorf
> <markus@trippelsdorf.de> wrote:
> > On 2013.09.06 at 23:50 +0200, David Herrmann wrote:
> >> Hi
> >>
> >> On Fri, Sep 6, 2013 at 10:20 PM, Markus Trippelsdorf
> >> <markus@trippelsdorf.de> wrote:
> >> > On 2013.09.06 at 14:00 +0200, Jiri Kosina wrote:
> >> >>
> >> >> David Herrmann (12):
> >> > ...
> >> >> HID: wiimote: add support for Guitar-Hero drums
> >> >
> >> > commit 61e00655e9cb82e034eb72b95a51072e718d14a7
> >> > Author: David Herrmann <dh.herrmann@gmail.com>
> >> > Date: Mon Aug 26 19:14:46 2013 +0200
> >> >
> >> > Input: introduce BTN/ABS bits for drums and guitars
> >> >
> >> > The commit above breaks my Logitech mouse. The mouse cursor just sits in
> >> > the middle of the screen and doesn't react to movements. dmesg is
> >> > normal, but Xorg.0.log says:
> >>
> >> Ok, the issue is the kernel assumes ABS_MAX to be a power-of-2 minus 1
> >> (used as mask). That wasn't really obvious to me. Attached is a patch
> >> which should fix that. Could you apply it on top of linus/master and
> >> give it a try?
> >
> > Your patch fixes the issue. Thanks.
>
> Thanks a lot for reporting+testing!
>
> I am still not sure how to solve the EVIOCSABS thingy. Problem is,
> it's defined as:
> #define EVIOCSABS(_abs) ...0xc0 + (_abs)...
> But if (_abs > 0x3f) this will be bigger than 0xff. Unfortunately, the
> upper part of the ioctl is defined as 'E' which is 0x45 in hex and
> thus sets the LSB. That means we cannot extend the _IOC_TYPE field to
> the upper region (which would cause endian-issues, anyway). I guess
> we're screwed here and need to revert that...
>
> Dmitry, any comment on this? Or am I missing something?
We have gaps below ABS_MT constants, I think for 3.12 you could move
your whammy there and revert ABS_MAX change, but we need to plan for
expanding it in the future.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [GIT] HID for 3.12 merge window
From: David Herrmann @ 2013-09-06 22:51 UTC (permalink / raw)
To: Markus Trippelsdorf
Cc: Jiri Kosina, Linus Torvalds, linux-kernel,
open list:HID CORE LAYER, Dmitry Torokhov
In-Reply-To: <20130906215910.GA356@x4>
Hi
On Fri, Sep 6, 2013 at 11:59 PM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2013.09.06 at 23:50 +0200, David Herrmann wrote:
>> Hi
>>
>> On Fri, Sep 6, 2013 at 10:20 PM, Markus Trippelsdorf
>> <markus@trippelsdorf.de> wrote:
>> > On 2013.09.06 at 14:00 +0200, Jiri Kosina wrote:
>> >>
>> >> David Herrmann (12):
>> > ...
>> >> HID: wiimote: add support for Guitar-Hero drums
>> >
>> > commit 61e00655e9cb82e034eb72b95a51072e718d14a7
>> > Author: David Herrmann <dh.herrmann@gmail.com>
>> > Date: Mon Aug 26 19:14:46 2013 +0200
>> >
>> > Input: introduce BTN/ABS bits for drums and guitars
>> >
>> > The commit above breaks my Logitech mouse. The mouse cursor just sits in
>> > the middle of the screen and doesn't react to movements. dmesg is
>> > normal, but Xorg.0.log says:
>>
>> Ok, the issue is the kernel assumes ABS_MAX to be a power-of-2 minus 1
>> (used as mask). That wasn't really obvious to me. Attached is a patch
>> which should fix that. Could you apply it on top of linus/master and
>> give it a try?
>
> Your patch fixes the issue. Thanks.
Thanks a lot for reporting+testing!
I am still not sure how to solve the EVIOCSABS thingy. Problem is,
it's defined as:
#define EVIOCSABS(_abs) ...0xc0 + (_abs)...
But if (_abs > 0x3f) this will be bigger than 0xff. Unfortunately, the
upper part of the ioctl is defined as 'E' which is 0x45 in hex and
thus sets the LSB. That means we cannot extend the _IOC_TYPE field to
the upper region (which would cause endian-issues, anyway). I guess
we're screwed here and need to revert that...
Dmitry, any comment on this? Or am I missing something?
Regards
David
^ permalink raw reply
* Re: [GIT] HID for 3.12 merge window
From: Markus Trippelsdorf @ 2013-09-06 21:59 UTC (permalink / raw)
To: David Herrmann
Cc: Jiri Kosina, Linus Torvalds, linux-kernel,
open list:HID CORE LAYER, Dmitry Torokhov
In-Reply-To: <CANq1E4Rf4c4zj1mD1aPr93Q2_M65DCEJhuLqcBWa5uH9tcKuWA@mail.gmail.com>
On 2013.09.06 at 23:50 +0200, David Herrmann wrote:
> Hi
>
> On Fri, Sep 6, 2013 at 10:20 PM, Markus Trippelsdorf
> <markus@trippelsdorf.de> wrote:
> > On 2013.09.06 at 14:00 +0200, Jiri Kosina wrote:
> >>
> >> David Herrmann (12):
> > ...
> >> HID: wiimote: add support for Guitar-Hero drums
> >
> > commit 61e00655e9cb82e034eb72b95a51072e718d14a7
> > Author: David Herrmann <dh.herrmann@gmail.com>
> > Date: Mon Aug 26 19:14:46 2013 +0200
> >
> > Input: introduce BTN/ABS bits for drums and guitars
> >
> > The commit above breaks my Logitech mouse. The mouse cursor just sits in
> > the middle of the screen and doesn't react to movements. dmesg is
> > normal, but Xorg.0.log says:
>
> Ok, the issue is the kernel assumes ABS_MAX to be a power-of-2 minus 1
> (used as mask). That wasn't really obvious to me. Attached is a patch
> which should fix that. Could you apply it on top of linus/master and
> give it a try?
Your patch fixes the issue. Thanks.
--
Markus
^ permalink raw reply
* Re: [GIT] HID for 3.12 merge window
From: David Herrmann @ 2013-09-06 21:50 UTC (permalink / raw)
To: Markus Trippelsdorf
Cc: Jiri Kosina, Linus Torvalds, linux-kernel,
open list:HID CORE LAYER, Dmitry Torokhov
In-Reply-To: <20130906202022.GA353@x4>
[-- Attachment #1: Type: text/plain, Size: 2862 bytes --]
Hi
On Fri, Sep 6, 2013 at 10:20 PM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2013.09.06 at 14:00 +0200, Jiri Kosina wrote:
>>
>> David Herrmann (12):
> ...
>> HID: wiimote: add support for Guitar-Hero drums
>
> commit 61e00655e9cb82e034eb72b95a51072e718d14a7
> Author: David Herrmann <dh.herrmann@gmail.com>
> Date: Mon Aug 26 19:14:46 2013 +0200
>
> Input: introduce BTN/ABS bits for drums and guitars
>
> The commit above breaks my Logitech mouse. The mouse cursor just sits in
> the middle of the screen and doesn't react to movements. dmesg is
> normal, but Xorg.0.log says:
Ok, the issue is the kernel assumes ABS_MAX to be a power-of-2 minus 1
(used as mask). That wasn't really obvious to me. Attached is a patch
which should fix that. Could you apply it on top of linus/master and
give it a try?
@Dmitry: The IOC_NR part of the definition of EVIOCSABS() is now
bigger than 1-byte. I need to check how that affects the 'E' part. Any
idea what to do here?
Thanks
David
Patch is also attached as I doubt that inlining it works in that
stupid web-client:
>From 653fe4d46ad368cdbf9b56a559a8468bd6f5cb3c Mon Sep 17 00:00:00 2001
From: David Herrmann <dh.herrmann@gmail.com>
Date: Fri, 6 Sep 2013 23:46:08 +0200
Subject: [PATCH] Input: evdev: don't assume ABS_MAX to be a power-of-2 minus 1
ABS_MAX is no longer a full mask. Hence, don't use it directly to get any
parameter for ioctls. Furthermore, the parameter-region and
ioctl-definition overlap, so even bumping ABS_MAX to 0x7f wouldn't help.
Reported-by: Markus Trippelsdorf <markus@trippelsdorf.de>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/input/evdev.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index d2b34fb..82e0073 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -939,12 +939,13 @@ static long evdev_do_ioctl(struct file *file,
unsigned int cmd,
_IOC_NR(cmd) & EV_MAX, size,
p, compat_mode);
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+ if (_IOC_NR(cmd) >= _IOC_NR(EVIOCGABS(0)) &&
+ _IOC_NR(cmd) <= _IOC_NR(EVIOCGABS(ABS_MAX))) {
if (!dev->absinfo)
return -EINVAL;
- t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) - _IOC_NR(EVIOCGABS(0));
abs = dev->absinfo[t];
if (copy_to_user(p, &abs, min_t(size_t,
@@ -957,12 +958,13 @@ static long evdev_do_ioctl(struct file *file,
unsigned int cmd,
if (_IOC_DIR(cmd) == _IOC_WRITE) {
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+ if (_IOC_NR(cmd) >= _IOC_NR(EVIOCSABS(0)) &&
+ _IOC_NR(cmd) <= _IOC_NR(EVIOCSABS(ABS_MAX))) {
if (!dev->absinfo)
return -EINVAL;
- t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) - _IOC_NR(EVIOCSABS(0));
if (copy_from_user(&abs, p, min_t(size_t,
size, sizeof(struct input_absinfo))))
--
1.8.4
[-- Attachment #2: 0001-Input-evdev-don-t-assume-ABS_MAX-to-be-a-power-of-2-.patch --]
[-- Type: application/octet-stream, Size: 1820 bytes --]
From 653fe4d46ad368cdbf9b56a559a8468bd6f5cb3c Mon Sep 17 00:00:00 2001
From: David Herrmann <dh.herrmann@gmail.com>
Date: Fri, 6 Sep 2013 23:46:08 +0200
Subject: [PATCH] Input: evdev: don't assume ABS_MAX to be a power-of-2 minus 1
ABS_MAX is no longer a full mask. Hence, don't use it directly to get any
parameter for ioctls. Furthermore, the parameter-region and
ioctl-definition overlap, so even bumping ABS_MAX to 0x7f wouldn't help.
Reported-by: Markus Trippelsdorf <markus@trippelsdorf.de>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/input/evdev.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index d2b34fb..82e0073 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -939,12 +939,13 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
_IOC_NR(cmd) & EV_MAX, size,
p, compat_mode);
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+ if (_IOC_NR(cmd) >= _IOC_NR(EVIOCGABS(0)) &&
+ _IOC_NR(cmd) <= _IOC_NR(EVIOCGABS(ABS_MAX))) {
if (!dev->absinfo)
return -EINVAL;
- t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) - _IOC_NR(EVIOCGABS(0));
abs = dev->absinfo[t];
if (copy_to_user(p, &abs, min_t(size_t,
@@ -957,12 +958,13 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
if (_IOC_DIR(cmd) == _IOC_WRITE) {
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+ if (_IOC_NR(cmd) >= _IOC_NR(EVIOCSABS(0)) &&
+ _IOC_NR(cmd) <= _IOC_NR(EVIOCSABS(ABS_MAX))) {
if (!dev->absinfo)
return -EINVAL;
- t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) - _IOC_NR(EVIOCSABS(0));
if (copy_from_user(&abs, p, min_t(size_t,
size, sizeof(struct input_absinfo))))
--
1.8.4
^ permalink raw reply related
* Re: [GIT] HID for 3.12 merge window
From: Markus Trippelsdorf @ 2013-09-06 20:20 UTC (permalink / raw)
To: Jiri Kosina
Cc: Linus Torvalds, linux-kernel, linux-input, David Herrmann,
Dmitry Torokhov
In-Reply-To: <alpine.LNX.2.00.1309061202050.26934@pobox.suse.cz>
On 2013.09.06 at 14:00 +0200, Jiri Kosina wrote:
>
> David Herrmann (12):
...
> HID: wiimote: add support for Guitar-Hero drums
commit 61e00655e9cb82e034eb72b95a51072e718d14a7
Author: David Herrmann <dh.herrmann@gmail.com>
Date: Mon Aug 26 19:14:46 2013 +0200
Input: introduce BTN/ABS bits for drums and guitars
The commit above breaks my Logitech mouse. The mouse cursor just sits in
the middle of the screen and doesn't react to movements. dmesg is
normal, but Xorg.0.log says:
[ 5.717] (II) config/udev: Adding input device Logitech Unifying Device. Wireless PID:4026 (/dev/input/event0)
[ 5.717] (**) Logitech Unifying Device. Wireless PID:4026: Applying InputClass "evdev pointer catchall"
[ 5.717] (**) Logitech Unifying Device. Wireless PID:4026: Applying InputClass "evdev keyboard catchall"
[ 5.717] (II) Using input driver 'evdev' for 'Logitech Unifying Device. Wireless PID:4026'
[ 5.717] (**) Logitech Unifying Device. Wireless PID:4026: always reports core events
[ 5.717] (**) evdev: Logitech Unifying Device. Wireless PID:4026: Device: "/dev/input/event0"
[ 5.717] (EE) evdev: Logitech Unifying Device. Wireless PID:4026: ioctl EVIOCGABSi(32) failed: Invalid argument
[ 5.763] (EE) PreInit returned 8 for "Logitech Unifying Device. Wireless PID:4026"
[ 5.763] (II) UnloadModule: "evdev"
[ 5.763] (II) config/udev: Adding input device Logitech Unifying Device. Wireless PID:4026 (/dev/input/mouse0)
[ 5.763] (II) No input driver specified, ignoring this device.
[ 5.763] (II) This device may have been added with another device file.
I've double-checked the bisection by reverting the commit on top of
current tree and this fixes the issue...
>From dmesg:
[ 1.551437] XFS (sda): Mounting Filesystem
[ 1.555823] tsc: Refined TSC clocksource calibration: 3210.826 MHz
[ 1.729233] usb 4-2: new full-speed USB device number 2 using ohci-pci
[ 1.857691] XFS (sda): Ending clean mount
[ 1.915581] logitech-djreceiver 0003:046D:C52B.0003: hiddev0,hidraw0: USB HID v1.11 Device [Logitech USB Receiver] on usb-0000:00:12.1-2/input2
[ 1.920263] input: Logitech Unifying Device. Wireless PID:4026 as /devices/pci0000:00/0000:00:12.1/usb4/4-2/4-2:1.2/0003:046D:C52B.0003/input/input0
[ 1.920479] logitech-djdevice 0003:046D:C52B.0004: input,hidraw1: USB HID v1.11 Keyboard [Logitech Unifying Device. Wireless PID:4026] on usb-0000:00:12.1-2:1
[ 2.042641] usb 3-1: new low-speed USB device number 2 using ohci-pci
[ 2.204791] input: HID 046a:0011 as /devices/pci0000:00/0000:00:12.0/usb3/3-1/3-1:1.0/input/input1
[ 2.204975] hid-generic 0003:046A:0011.0005: input,hidraw2: USB HID v1.10 Keyboard [HID 046a:0011] on usb-0000:00:12.0-1/input0
[ 2.341424] udevd[98]: starting eudev version 1.0
[ 2.556172] Switched to clocksource tsc
[ 2.791794] ATL1E 0000:02:00.0 eth0: NIC Link is Up <1000 Mbps Full Duplex>
[ 3.945433] Adding 3071996k swap on /var/cache/swapfile.img. Priority:-1 extents:1 across:3071996k
--
Markus
^ permalink raw reply
* [PATCH] Input: wacom - add support for three new Intuos Pro devices
From: Ping Cheng @ 2013-09-06 18:50 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, Ping Cheng
Signed-off-by: Ping Cheng <pingc@wacom.com>
---
drivers/input/tablet/wacom_sys.c | 10 ++++++++--
drivers/input/tablet/wacom_wac.c | 37 ++++++++++++++++++++++++++++++-------
drivers/input/tablet/wacom_wac.h | 3 +++
3 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 79b69ea..cec3f9a 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -719,7 +719,7 @@ static int wacom_led_control(struct wacom *wacom)
return -ENOMEM;
if (wacom->wacom_wac.features.type >= INTUOS5S &&
- wacom->wacom_wac.features.type <= INTUOS5L) {
+ wacom->wacom_wac.features.type <= INTUOSPL) {
/*
* Touch Ring and crop mark LED luminance may take on
* one of four values:
@@ -981,6 +981,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOS5S:
case INTUOS5:
case INTUOS5L:
+ case INTUOSPS:
+ case INTUOSPM:
+ case INTUOSPL:
wacom->led.select[0] = 0;
wacom->led.select[1] = 0;
wacom->led.llv = 32;
@@ -1024,6 +1027,9 @@ static void wacom_destroy_leds(struct wacom *wacom)
case INTUOS5S:
case INTUOS5:
case INTUOS5L:
+ case INTUOSPS:
+ case INTUOSPM:
+ case INTUOSPL:
sysfs_remove_group(&wacom->intf->dev.kobj,
&intuos5_led_attr_group);
break;
@@ -1298,7 +1304,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
* HID descriptor. If this is the touch interface (wMaxPacketSize
* of WACOM_PKGLEN_BBTOUCH3), override the table values.
*/
- if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
+ if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) {
features->device_type = BTN_TOOL_FINGER;
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 541197b..d15dfdb 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -619,14 +619,14 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
} else {
input_report_abs(input, ABS_MISC, 0);
}
- } else if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
+ } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
int i;
/* Touch ring mode switch has no capacitive sensor */
input_report_key(input, BTN_0, (data[3] & 0x01));
/*
- * ExpressKeys on Intuos5 have a capacitive sensor in
+ * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
* addition to the mechanical switch. Switch data is
* stored in data[4], capacitive data in data[5].
*/
@@ -714,7 +714,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
features->type == INTUOS4 ||
features->type == INTUOS4S ||
features->type == INTUOS5 ||
- features->type == INTUOS5S)) {
+ features->type == INTUOS5S ||
+ features->type == INTUOSPM ||
+ features->type == INTUOSPS)) {
return 0;
}
@@ -767,8 +769,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
/* I4 mouse */
- if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
- (features->type >= INTUOS5S && features->type <= INTUOS5L)) {
+ if (features->type >= INTUOS4S && features->type <= INTUOSPL) {
input_report_key(input, BTN_LEFT, data[6] & 0x01);
input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
input_report_key(input, BTN_RIGHT, data[6] & 0x04);
@@ -795,7 +796,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
}
}
} else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
- features->type == INTUOS4L || features->type == INTUOS5L) &&
+ features->type == INTUOS4L || features->type == INTUOS5L ||
+ features->type == INTUOSPL) &&
wacom->tool[idx] == BTN_TOOL_LENS) {
/* Lens cursor packets */
input_report_key(input, BTN_LEFT, data[8] & 0x01);
@@ -1335,6 +1337,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case INTUOS5S:
case INTUOS5:
case INTUOS5L:
+ case INTUOSPS:
+ case INTUOSPM:
+ case INTUOSPL:
if (len == WACOM_PKGLEN_BBTOUCH3)
sync = wacom_bpt3_touch(wacom_wac);
else
@@ -1418,7 +1423,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
/* these device have multiple inputs */
if (features->type >= WIRELESS ||
- (features->type >= INTUOS5S && features->type <= INTUOS5L) ||
+ (features->type >= INTUOS5S && features->type <= INTUOSPL) ||
(features->oVid && features->oPid))
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
@@ -1625,6 +1630,8 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case INTUOS5:
case INTUOS5L:
+ case INTUOSPM:
+ case INTUOSPL:
if (features->device_type == BTN_TOOL_PEN) {
__set_bit(BTN_7, input_dev->keybit);
__set_bit(BTN_8, input_dev->keybit);
@@ -1632,6 +1639,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
/* fall through */
case INTUOS5S:
+ case INTUOSPS:
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (features->device_type == BTN_TOOL_PEN) {
@@ -1950,6 +1958,18 @@ static const struct wacom_features wacom_features_0x29 =
static const struct wacom_features wacom_features_0x2A =
{ "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047,
63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x314 =
+ { "Wacom Intuos Pro S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047,
+ 63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ .touch_max = 16 };
+static const struct wacom_features wacom_features_0x315 =
+ { "Wacom Intuos Pro M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047,
+ 63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ .touch_max = 16 };
+static const struct wacom_features wacom_features_0x317 =
+ { "Wacom Intuos Pro L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047,
+ 63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ .touch_max = 16 };
static const struct wacom_features wacom_features_0xF4 =
{ "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047,
63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@@ -2241,6 +2261,9 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x101) },
{ USB_DEVICE_WACOM(0x10D) },
{ USB_DEVICE_WACOM(0x304) },
+ { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) },
+ { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) },
+ { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x47) },
{ USB_DEVICE_WACOM(0xF4) },
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index dfc9e08..d6dec58 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -76,6 +76,9 @@ enum {
INTUOS5S,
INTUOS5,
INTUOS5L,
+ INTUOSPS,
+ INTUOSPM,
+ INTUOSPL,
WACOM_21UX2,
WACOM_22HD,
DTK,
--
1.8.1.2
^ permalink raw reply related
* [PATCH 4/4] Input: rb532_button - Remove redundant dev_set_drvdata
From: Sachin Kamat @ 2013-09-06 18:08 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, sachin.kamat
In-Reply-To: <1378490888-5083-1-git-send-email-sachin.kamat@linaro.org>
Driver core sets the data to NULL upon release or probe
failure. Hence explicit setting is not necessary.
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
drivers/input/misc/rb532_button.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
index fb4f8ac..83fff38 100644
--- a/drivers/input/misc/rb532_button.c
+++ b/drivers/input/misc/rb532_button.c
@@ -87,7 +87,6 @@ static int rb532_button_remove(struct platform_device *pdev)
input_unregister_polled_device(poll_dev);
input_free_polled_device(poll_dev);
- dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 3/4] Input: htcpen - Remove redundant dev_set_drvdata
From: Sachin Kamat @ 2013-09-06 18:08 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, sachin.kamat, Pau Oliva Fora
In-Reply-To: <1378490888-5083-1-git-send-email-sachin.kamat@linaro.org>
Driver core sets the data to NULL upon release or probe
failure. Hence explicit setting is not necessary.
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
Cc: Pau Oliva Fora <pof@eslack.org>
---
drivers/input/touchscreen/htcpen.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c
index 6650085..92e2243 100644
--- a/drivers/input/touchscreen/htcpen.c
+++ b/drivers/input/touchscreen/htcpen.c
@@ -186,8 +186,6 @@ static int htcpen_isa_remove(struct device *dev, unsigned int id)
release_region(HTCPEN_PORT_INIT, 1);
release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
- dev_set_drvdata(dev, NULL);
-
return 0;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 2/4] Input: cyttsp4_core - Remove redundant dev_set_drvdata
From: Sachin Kamat @ 2013-09-06 18:08 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, sachin.kamat, Ferruh Yigit
In-Reply-To: <1378490888-5083-1-git-send-email-sachin.kamat@linaro.org>
Driver core sets the data to NULL upon release or probe
failure. Hence explicit setting is not necessary.
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
Cc: Ferruh Yigit <fery@cypress.com>
---
drivers/input/touchscreen/cyttsp4_core.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
index d038575..42d830e 100644
--- a/drivers/input/touchscreen/cyttsp4_core.c
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -2113,7 +2113,6 @@ error_startup:
error_request_irq:
if (cd->cpdata->init)
cd->cpdata->init(cd->cpdata, 0, dev);
- dev_set_drvdata(dev, NULL);
error_free_xfer:
kfree(cd->xfer_buf);
error_free_cd:
@@ -2151,7 +2150,6 @@ int cyttsp4_remove(struct cyttsp4 *cd)
free_irq(cd->irq, cd);
if (cd->cpdata->init)
cd->cpdata->init(cd->cpdata, 0, dev);
- dev_set_drvdata(dev, NULL);
cyttsp4_free_si_ptrs(cd);
kfree(cd);
return 0;
--
1.7.4.1
^ permalink raw reply related
* [PATCH 1/4] Input: cobalt_btns - Remove redundant dev_set_drvdata
From: Sachin Kamat @ 2013-09-06 18:08 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, sachin.kamat, Yoichi Yuasa
Driver core sets the data to NULL upon release or probe
failure. Hence explicit setting is not necessary.
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
Cc: Yoichi Yuasa <yuasa@linux-mips.org>
---
drivers/input/misc/cobalt_btns.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
index 4f77f87..b5d71d2 100644
--- a/drivers/input/misc/cobalt_btns.c
+++ b/drivers/input/misc/cobalt_btns.c
@@ -131,7 +131,6 @@ static int cobalt_buttons_probe(struct platform_device *pdev)
err_free_mem:
input_free_polled_device(poll_dev);
kfree(bdev);
- dev_set_drvdata(&pdev->dev, NULL);
return error;
}
@@ -144,7 +143,6 @@ static int cobalt_buttons_remove(struct platform_device *pdev)
input_free_polled_device(bdev->poll_dev);
iounmap(bdev->reg);
kfree(bdev);
- dev_set_drvdata(dev, NULL);
return 0;
}
--
1.7.4.1
^ permalink raw reply related
* Re: [PATCH v3] Input: evdev - add EVIOCREVOKE ioctl
From: Kristian Høgsberg @ 2013-09-06 18:04 UTC (permalink / raw)
To: David Herrmann; +Cc: open list:HID CORE LAYER, Dmitry Torokhov
In-Reply-To: <CANq1E4S9WJaFTqru0-UtcLi-Aij2v43b3VT5jkPRUp6n5VHrzw@mail.gmail.com>
On Fri, Sep 6, 2013 at 10:12 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi Dmitry
>
> On Sun, Sep 1, 2013 at 4:07 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
>> If we have multiple sessions on a system, we normally don't want
>> background sessions to read input events. Otherwise, it could capture
>> passwords and more entered by the user on the foreground session. This is
>> a real world problem as the recent XMir development showed:
>> http://mjg59.dreamwidth.org/27327.html
>>
>> We currently rely on sessions to release input devices when being
>> deactivated. This relies on trust across sessions. But that's not given on
>> usual systems. We therefore need a way to control which processes have
>> access to input devices.
>>
>> With VTs the kernel simply routed them through the active /dev/ttyX. This
>> is not possible with evdev devices, though. Moreover, we want to avoid
>> routing input-devices through some dispatcher-daemon in userspace (which
>> would add some latency).
>>
>> This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
>> device-access irrecoverably for that *single* open-file. Hence, once you
>> call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
>> rather useless now (but still valid compared to close()!). This allows us
>> to pass fds directly to session-processes from a trusted source. The
>> source keeps a dup()ed fd and revokes access once the session-process is
>> no longer active.
>> Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
>> restriction now as there is no way to revive the fd again. Hence, a user
>> is free to call EVIOCREVOKE themself to kill the fd.
>>
>> Additionally, this ioctl allows multi-layer access-control (again compared
>> to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
>> layer can simply request a new open-file from the layer above and pass it
>> to the layer below. Now each layer can call EVIOCREVOKE on the fds to
>> revoke access for all layers below, at the expense of one fd per layer.
>>
>> There's already ongoing experimental user-space work which demonstrates
>> how it can be used:
>> http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
>
> I've been running this logind-feature for some time on my machine now.
> I haven't seen any problems. Could you let us know what your plans are
> for this patch?
> 3.12 would be very nice so we could continue pushing the user-space parts.
> There are even existing programs (like weston-launch) which could
> already make great use of that with just a <10 line patch.
I think we have good use cases for this - with revoke we can give
input fds to a non-trusted, non-root display-severs and standalone
KMS+evdev applications and reliably revoke them when their session is
deactivated (vt switched away typically). EVIOCREVOKE is usable in
logind and standalone setuid helpers such as weston-launch,.
Everything other priviledge operation a display server needs can be
done in logind or weston-launch, this ioctl is the final piece that
lets us run X without root priviledges.
Reviewed-by: Kristian Høgsberg <krh@bitplanet.net>
>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>> ---
>> Changes from v2:
>> - Handle client->revoked like evdev->exist to avoid special-casing "revoked"
>>
>> drivers/input/evdev.c | 33 +++++++++++++++++++++++++++++----
>> include/uapi/linux/input.h | 1 +
>> 2 files changed, 30 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
>> index d2b34fb..cf6bdff 100644
>> --- a/drivers/input/evdev.c
>> +++ b/drivers/input/evdev.c
>> @@ -48,6 +48,7 @@ struct evdev_client {
>> struct evdev *evdev;
>> struct list_head node;
>> int clkid;
>> + bool revoked;
>> unsigned int bufsize;
>> struct input_event buffer[];
>> };
>> @@ -164,6 +165,9 @@ static void evdev_pass_values(struct evdev_client *client,
>> struct input_event event;
>> bool wakeup = false;
>>
>> + if (client->revoked)
>> + return;
>> +
>> event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
>> mono : real);
>>
>> @@ -429,7 +433,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
>> if (retval)
>> return retval;
>>
>> - if (!evdev->exist) {
>> + if (!evdev->exist || client->revoked) {
>> retval = -ENODEV;
>> goto out;
>> }
>> @@ -511,7 +515,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
>> if (!(file->f_flags & O_NONBLOCK)) {
>> error = wait_event_interruptible(evdev->wait,
>> client->packet_head != client->tail ||
>> - !evdev->exist);
>> + !evdev->exist || client->revoked);
>> if (error)
>> return error;
>> }
>> @@ -529,7 +533,11 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
>>
>> poll_wait(file, &evdev->wait, wait);
>>
>> - mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
>> + if (evdev->exist && !client->revoked)
>> + mask = POLLOUT | POLLWRNORM;
>> + else
>> + mask = POLLHUP | POLLERR;
>> +
>> if (client->packet_head != client->tail)
>> mask |= POLLIN | POLLRDNORM;
>>
>> @@ -795,6 +803,17 @@ static int evdev_handle_mt_request(struct input_dev *dev,
>> return 0;
>> }
>>
>> +static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
>> + struct file *file)
>> +{
>> + client->revoked = true;
>> + evdev_ungrab(evdev, client);
>> + input_flush_device(&evdev->handle, file);
>> + wake_up_interruptible(&evdev->wait);
>> +
>> + return 0;
>> +}
>> +
>> static long evdev_do_ioctl(struct file *file, unsigned int cmd,
>> void __user *p, int compat_mode)
>> {
>> @@ -857,6 +876,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
>> else
>> return evdev_ungrab(evdev, client);
>>
>> + case EVIOCREVOKE:
>> + if (p)
>> + return -EINVAL;
>> + else
>> + return evdev_revoke(evdev, client, file);
>> +
>> case EVIOCSCLOCKID:
>> if (copy_from_user(&i, p, sizeof(unsigned int)))
>> return -EFAULT;
>> @@ -1002,7 +1027,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
>> if (retval)
>> return retval;
>>
>> - if (!evdev->exist) {
>> + if (!evdev->exist || client->revoked) {
>> retval = -ENODEV;
>> goto out;
>> }
>> diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
>> index 2fb6fae..d61c61c 100644
>> --- a/include/uapi/linux/input.h
>> +++ b/include/uapi/linux/input.h
>> @@ -152,6 +152,7 @@ struct input_keymap_entry {
>> #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
>>
>> #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
>> +#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */
>>
>> #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */
>>
>> --
>> 1.8.4
>>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3] Input: evdev - add EVIOCREVOKE ioctl
From: David Herrmann @ 2013-09-06 17:12 UTC (permalink / raw)
To: open list:HID CORE LAYER
Cc: Dmitry Torokhov, David Herrmann, Kristian Høgsberg
In-Reply-To: <1378044450-8327-1-git-send-email-dh.herrmann@gmail.com>
Hi Dmitry
On Sun, Sep 1, 2013 at 4:07 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> If we have multiple sessions on a system, we normally don't want
> background sessions to read input events. Otherwise, it could capture
> passwords and more entered by the user on the foreground session. This is
> a real world problem as the recent XMir development showed:
> http://mjg59.dreamwidth.org/27327.html
>
> We currently rely on sessions to release input devices when being
> deactivated. This relies on trust across sessions. But that's not given on
> usual systems. We therefore need a way to control which processes have
> access to input devices.
>
> With VTs the kernel simply routed them through the active /dev/ttyX. This
> is not possible with evdev devices, though. Moreover, we want to avoid
> routing input-devices through some dispatcher-daemon in userspace (which
> would add some latency).
>
> This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
> device-access irrecoverably for that *single* open-file. Hence, once you
> call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
> rather useless now (but still valid compared to close()!). This allows us
> to pass fds directly to session-processes from a trusted source. The
> source keeps a dup()ed fd and revokes access once the session-process is
> no longer active.
> Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
> restriction now as there is no way to revive the fd again. Hence, a user
> is free to call EVIOCREVOKE themself to kill the fd.
>
> Additionally, this ioctl allows multi-layer access-control (again compared
> to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
> layer can simply request a new open-file from the layer above and pass it
> to the layer below. Now each layer can call EVIOCREVOKE on the fds to
> revoke access for all layers below, at the expense of one fd per layer.
>
> There's already ongoing experimental user-space work which demonstrates
> how it can be used:
> http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
I've been running this logind-feature for some time on my machine now.
I haven't seen any problems. Could you let us know what your plans are
for this patch?
3.12 would be very nice so we could continue pushing the user-space parts.
There are even existing programs (like weston-launch) which could
already make great use of that with just a <10 line patch.
Thanks
David
> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
> ---
> Changes from v2:
> - Handle client->revoked like evdev->exist to avoid special-casing "revoked"
>
> drivers/input/evdev.c | 33 +++++++++++++++++++++++++++++----
> include/uapi/linux/input.h | 1 +
> 2 files changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
> index d2b34fb..cf6bdff 100644
> --- a/drivers/input/evdev.c
> +++ b/drivers/input/evdev.c
> @@ -48,6 +48,7 @@ struct evdev_client {
> struct evdev *evdev;
> struct list_head node;
> int clkid;
> + bool revoked;
> unsigned int bufsize;
> struct input_event buffer[];
> };
> @@ -164,6 +165,9 @@ static void evdev_pass_values(struct evdev_client *client,
> struct input_event event;
> bool wakeup = false;
>
> + if (client->revoked)
> + return;
> +
> event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
> mono : real);
>
> @@ -429,7 +433,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
> if (retval)
> return retval;
>
> - if (!evdev->exist) {
> + if (!evdev->exist || client->revoked) {
> retval = -ENODEV;
> goto out;
> }
> @@ -511,7 +515,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
> if (!(file->f_flags & O_NONBLOCK)) {
> error = wait_event_interruptible(evdev->wait,
> client->packet_head != client->tail ||
> - !evdev->exist);
> + !evdev->exist || client->revoked);
> if (error)
> return error;
> }
> @@ -529,7 +533,11 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
>
> poll_wait(file, &evdev->wait, wait);
>
> - mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
> + if (evdev->exist && !client->revoked)
> + mask = POLLOUT | POLLWRNORM;
> + else
> + mask = POLLHUP | POLLERR;
> +
> if (client->packet_head != client->tail)
> mask |= POLLIN | POLLRDNORM;
>
> @@ -795,6 +803,17 @@ static int evdev_handle_mt_request(struct input_dev *dev,
> return 0;
> }
>
> +static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
> + struct file *file)
> +{
> + client->revoked = true;
> + evdev_ungrab(evdev, client);
> + input_flush_device(&evdev->handle, file);
> + wake_up_interruptible(&evdev->wait);
> +
> + return 0;
> +}
> +
> static long evdev_do_ioctl(struct file *file, unsigned int cmd,
> void __user *p, int compat_mode)
> {
> @@ -857,6 +876,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
> else
> return evdev_ungrab(evdev, client);
>
> + case EVIOCREVOKE:
> + if (p)
> + return -EINVAL;
> + else
> + return evdev_revoke(evdev, client, file);
> +
> case EVIOCSCLOCKID:
> if (copy_from_user(&i, p, sizeof(unsigned int)))
> return -EFAULT;
> @@ -1002,7 +1027,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
> if (retval)
> return retval;
>
> - if (!evdev->exist) {
> + if (!evdev->exist || client->revoked) {
> retval = -ENODEV;
> goto out;
> }
> diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
> index 2fb6fae..d61c61c 100644
> --- a/include/uapi/linux/input.h
> +++ b/include/uapi/linux/input.h
> @@ -152,6 +152,7 @@ struct input_keymap_entry {
> #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
>
> #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
> +#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */
>
> #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */
>
> --
> 1.8.4
>
^ permalink raw reply
* Re: [RFC] surface-input
From: David Herrmann @ 2013-09-06 13:36 UTC (permalink / raw)
To: Florian Echtler; +Cc: linux-input
In-Reply-To: <52289209.2070707@butterbrot.org>
Hi
On Thu, Sep 5, 2013 at 4:15 PM, Florian Echtler <floe@butterbrot.org> wrote:
> Hello everyone,
>
> as mentioned earlier, I'm currently writing a multitouch input driver
> for the Pixelsense (formerly Surface 2.0). It's now at a point where I'd
> consider it mostly done, but a) I have very limited experience with
> kernel drivers and b) there are some additional questions I have, so I'm
> attaching the current state of my source code and would like to ask for
> your comments.
>
> Open question: it looks like just calling
>
> input_mt_init_slots(poll_dev->input, MAX_CONTACTS, INPUT_MT_DIRECT);
>
> in the initialization routine isn't enough. hid-multitouch doesn't seem
> to use any other init commands, though, or did I overlook something?
>
> Are there any other caveats of the input-mt library which I should be
> aware of?
>
> Thanks for your input, and best regards, Florian
Please inline the code as Benjamin noted. Otherwise, it's cumbersome
to comment on specific lines (please see "git help format-patch" and
"git help send-email").
I did review the core setup/lifetime/destruction code and it looks
good. But you need to fix several error-paths. If a core function
returns "int", you should check it for "r < 0" and then forward the
code. Don't overwrite it with EINVAL.
Furthermore, correctly free your memory. Things like this leak memory
(surface might be non-NULL):
if (!surface || !poll_dev)
return -ENOMEM;
I can do a more complete review for v2 if you send it inline. But
looks good overall.
Thanks
David
^ permalink raw reply
* Re: [RFC] surface-input
From: Benjamin Tissoires @ 2013-09-06 13:25 UTC (permalink / raw)
To: Florian Echtler; +Cc: linux-input, Henrik Rydberg, Dmitry Torokhov
In-Reply-To: <52289209.2070707@butterbrot.org>
Hi Florian,
On 05/09/13 16:15, Florian Echtler wrote:
> Hello everyone,
>
> as mentioned earlier, I'm currently writing a multitouch input driver
> for the Pixelsense (formerly Surface 2.0). It's now at a point where I'd
> consider it mostly done, but a) I have very limited experience with
> kernel drivers and b) there are some additional questions I have, so I'm
> attaching the current state of my source code and would like to ask for
> your comments.
Thanks for sharing this with us.
I will not do a proper review right now (just coming back from some days off, so I am quickly emptying my mail box).
Some generic comments:
- please always inline the code in the message, it is *much* easier to review and comment it
- please directly use a patch format: if the code is good, Dmitry can take it directly through his tree
- add the following people for further submissions:
* Henrik - multitouch maintainer in the kernel, and I'm sure he will be happy to see this stuff
* Dmitry - input maintainer, the driver will go through his tree, so it's better to let him know as soon as possible of the different discussions.
- please stick to the kernel formatting guidelines (without orders: do not use C99-style ("// ..."), do not mix tabs and spaces, stick to 80 columns, etc..). The whole documentation is in Documentation/CodingStyle, and use the script scripts/checkpatch.pl to validate most of these.
- I don't think a separate ".h" will be accepted as the declarations will not be used outside of the driver. Just merge the header on top of you .c file.
>
> Open question: it looks like just calling
>
> input_mt_init_slots(poll_dev->input, MAX_CONTACTS, INPUT_MT_DIRECT);
>
> in the initialization routine isn't enough. hid-multitouch doesn't seem
> to use any other init commands, though, or did I overlook something?
>
> Are there any other caveats of the input-mt library which I should be
> aware of?
You need to also set up the different axes (ABS_MT_POSITION_X and others) before calling input_mt_init_slots(). See setup_events_to_report() in bcm5974.c for an example.
>
> Thanks for your input, and best regards, Florian
>
Few comments inlined:
> /*
> Surface2.0/SUR40/PixelSense input driver v0.0.1
>
> This program is free software; you can redistribute it and/or
> modify it under the terms of the GNU General Public License as
> published by the Free Software Foundation; either version 2 of
> the License, or (at your option) any later version.
>
> Copyright (C) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
>
> Derived from the USB Skeleton driver 1.1,
> Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
>
> Derived from the Apple USB BCM5974 multitouch driver,
> Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
> */
>
>[snipped]
>
> /*
> * this function is called when a whole contact has been processed,
> * so that it can assign it to a slot and store the data there
> */
> static void report_blob(struct surface_blob *blob, struct input_dev *input)
> {
> int wide, major, minor;
>
> int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
> if (slotnum < 0 || slotnum >= MAX_CONTACTS)
> return;
>
> /* FIXME: is this needed for the Pixelsense? */
I doubt. This was required in hid-multitouch because we sometimes have "blobs" without valuable information. So we drop them.
Here it looks like each blob contains information, so it should be fine without this.
> /*if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
> struct input_mt *mt = input->mt;
> struct input_mt_slot *slot = &mt->slots[slotnum];
> if (input_mt_is_active(slot) &&
> input_mt_is_used(mt, slot))
> return;
> }*/
>
> input_mt_slot(input, slotnum);
> input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
looks like you never release the touch... :(
See below.
> wide = (blob->bb_size_x > blob->bb_size_y);
> major = max(blob->bb_size_x, blob->bb_size_y);
> minor = min(blob->bb_size_x, blob->bb_size_y);
>
> input_event(input, EV_ABS, ABS_MT_POSITION_X, blob->pos_x);
> input_event(input, EV_ABS, ABS_MT_POSITION_Y, blob->pos_y);
> input_event(input, EV_ABS, ABS_MT_TOOL_X, blob->ctr_x);
> input_event(input, EV_ABS, ABS_MT_TOOL_Y, blob->ctr_y);
> input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
> input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
> input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
> }
>
>
[snipped]
>
> /* check candidate USB interface */
> static int surface_probe(struct usb_interface *interface,
> const struct usb_device_id *id)
> {
> struct usb_device *usbdev = interface_to_usbdev(interface);
> struct usb_surface *surface;
> struct usb_host_interface *iface_desc;
> struct usb_endpoint_descriptor *endpoint;
> struct input_polled_dev* poll_dev;
>
> /* check if we really have the right interface */
> iface_desc = &interface->altsetting[0];
> if (iface_desc->desc.bInterfaceClass != 0xFF)
> return -ENODEV;
>
> /* allocate memory for our device state and initialize it */
> surface = kzalloc(sizeof(struct usb_surface), GFP_KERNEL);
> poll_dev = input_allocate_polled_device();
> surface->input = poll_dev;
> if (!surface || !poll_dev)
> return -ENOMEM;
>
> poll_dev->private = surface;
> poll_dev->poll_interval = POLL_INTERVAL;
> poll_dev->open = surface_open;
> poll_dev->poll = surface_poll;
> poll_dev->close = surface_close;
>
> poll_dev->input->name = "Samsung SUR40";
> usb_to_input_id(usbdev,&(poll_dev->input->id));
> usb_make_path(usbdev, surface->phys, sizeof(surface->phys));
> strlcat(surface->phys, "/input0", sizeof(surface->phys));
> poll_dev->input->phys = surface->phys;
>
> input_mt_init_slots(poll_dev->input, MAX_CONTACTS, INPUT_MT_DIRECT);
If the device always reports actual touches and discards the release information, you may need to use the flag INPUT_MT_DROP_UNUSED.
>
> surface->usbdev = usbdev;
> surface->interface = interface;
>
> /* use endpoint #4 (0x86) */
> endpoint = &iface_desc->endpoint[4].desc;
> if (endpoint->bEndpointAddress == TOUCH_ENDPOINT) {
> /* we found a bulk in endpoint */
> surface->bulk_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
> surface->bulk_in_endpointAddr = endpoint->bEndpointAddress;
> surface->bulk_in_buffer = kmalloc(2*surface->bulk_in_size, GFP_KERNEL);
> if (!surface->bulk_in_buffer) {
> pr_err("Unable to allocate input buffer.");
> surface_delete(surface);
> return -ENOMEM;
> }
> }
>
> if (!(surface->bulk_in_endpointAddr)) {
> pr_err("Unable to find bulk-in endpoint.");
> surface_delete(surface);
> return -ENODEV;
> }
>
> /* we can register the device now, as it is ready */
> usb_set_intfdata(interface, surface);
>
> if (input_register_polled_device(poll_dev)) {
> pr_err("Unable to register polled input device.");
> surface_delete(surface);
> return -ENODEV;
> }
>
> dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
>
> return 0;
> }
>
> [snipped]
>
> /* usb specific object needed to register this driver with the usb subsystem */
> static struct usb_driver surface_driver = {
> .name = DRIVER_SHORT,
> .probe = surface_probe,
> .disconnect = surface_disconnect,
> /*.suspend = surface_suspend,
> .resume = surface_resume,*/
if they are commented, they are not required, so just drop it :)
> .id_table = surface_table,
> /*.supports_autosuspend = 1,*/
ditto
> };
>
> module_usb_driver(surface_driver);
>
> MODULE_AUTHOR(DRIVER_AUTHOR);
> MODULE_DESCRIPTION(DRIVER_DESC);
> MODULE_LICENSE("GPL");
Cheers,
Benjamin
^ permalink raw reply
* [RFC] surface-input
From: Florian Echtler @ 2013-09-05 14:15 UTC (permalink / raw)
To: linux-input
[-- Attachment #1.1: Type: text/plain, Size: 831 bytes --]
Hello everyone,
as mentioned earlier, I'm currently writing a multitouch input driver
for the Pixelsense (formerly Surface 2.0). It's now at a point where I'd
consider it mostly done, but a) I have very limited experience with
kernel drivers and b) there are some additional questions I have, so I'm
attaching the current state of my source code and would like to ask for
your comments.
Open question: it looks like just calling
input_mt_init_slots(poll_dev->input, MAX_CONTACTS, INPUT_MT_DIRECT);
in the initialization routine isn't enough. hid-multitouch doesn't seem
to use any other init commands, though, or did I overlook something?
Are there any other caveats of the input-mt library which I should be
aware of?
Thanks for your input, and best regards, Florian
--
SENT FROM MY DEC VT50 TERMINAL
[-- Attachment #1.2: Makefile --]
[-- Type: text/plain, Size: 1058 bytes --]
# Assumptions:
# This makefile assumes that kernel modules are installed in
# /lib/modules/'uname -r'/.
# Should the assumption be incorrect, please use:
# make MODULE_ROOT="/path/to/modules/for/your/kernel"
# to override assumed path
MODULE=surface-input
srcs := $(MODULE).c
obj-m := $(srcs:%.c=%.o)
MODULE_ROOT:= /lib/modules/$(shell uname -r)
BUILDDIR := $(MODULE_ROOT)/build
MODDIR := $(MODULE_ROOT)/updates
PWD := $(shell pwd)
override install_targets := $(srcs:%.c=install_%)
default:
$(MAKE) -C $(BUILDDIR) SUBDIRS=$(PWD) "obj-m=$(obj-m)" modules
.PHONY: default install all clean help $(install_targets)
install:
install -m 644 $(MODULE).ko $(MODDIR)
depmod -a
modprobe -r $(MODULE)
modprobe $(MODULE)
debug:
@echo
@echo "srcs="$(srcs)
@echo "obj-m="$(obj-m)
@echo "MODULE_ROOT="$(MODULE_ROOT)
@echo "PWD="$(PWD)
@echo "install targets: " $(install_targets)
@echo
clean:
-rm -rf *.o *.ko $(MODULE)*.mod.c Module.symvers .$(MODULE)* .tmp_versions modules.order
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: surface-data.h --]
[-- Type: text/x-chdr; name="surface-data.h", Size: 2533 bytes --]
/*
Surface2.0/SUR40/PixelSense input driver v0.0.1
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
Copyright (C) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
Derived from the USB Skeleton driver 1.1,
Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
Derived from the Apple USB BCM5974 multitouch driver,
Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
*/
#ifndef _SURFACE_DATA_H_
#define _SURFACE_DATA_H_
#define ID_MICROSOFT 0x045e
#define ID_SURFACE 0x0775
#define VIDEO_RES_X 960
#define VIDEO_RES_Y 540
#define VIDEO_BUFFER_SIZE VIDEO_RES_X * VIDEO_RES_Y
// read 512 bytes from endpoint 0x86 -> get header + blobs
struct surface_header {
uint16_t type; // always 0x0001
uint16_t count; // count of blobs (if == 0: continue prev. packet ID)
uint32_t packet_id;
uint32_t timestamp; // milliseconds (increases by 16 or 17 each frame)
uint32_t unknown; // "epoch?" always 02/03 00 00 00
};
struct surface_blob {
uint16_t blob_id;
uint8_t action; // 0x02 = enter/exit, 0x03 = update (?)
uint8_t unknown; // always 0x01 or 0x02 (no idea what this is?)
uint16_t bb_pos_x; // upper left corner of bounding box
uint16_t bb_pos_y;
uint16_t bb_size_x; // size of bounding box
uint16_t bb_size_y;
uint16_t pos_x; // finger tip position
uint16_t pos_y;
uint16_t ctr_x; // centroid position
uint16_t ctr_y;
uint16_t axis_x; // somehow related to major/minor axis, mostly:
uint16_t axis_y; // axis_x == bb_size_y && axis_y == bb_size_x
float angle; // orientation in radians relative to x axis
uint32_t area; // size in pixels/pressure (?)
uint8_t padding[32];
};
// read 512 bytes from endpoint 0x82 -> get header below
// continue reading 16k blocks until header.size bytes read
struct surface_image {
uint32_t magic; // "SUBF"
uint32_t packet_id;
uint32_t size; // always 0x0007e900 = 960x540
uint32_t timestamp; // milliseconds (increases by 16 or 17 each frame)
uint32_t unknown; // "epoch?" always 02/03 00 00 00
};
// read 8 bytes using control message 0xc0,0xb1,0x00,0x00
struct surface_sensors {
uint16_t temp;
uint16_t acc_x;
uint16_t acc_y;
uint16_t acc_z;
};
#endif // _SURFACE_DATA_H_
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.4: surface-input.c --]
[-- Type: text/x-csrc; name="surface-input.c", Size: 11285 bytes --]
/*
Surface2.0/SUR40/PixelSense input driver v0.0.1
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
Copyright (C) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
Derived from the USB Skeleton driver 1.1,
Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
Derived from the Apple USB BCM5974 multitouch driver,
Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/printk.h>
#include <linux/input-polldev.h>
#include <linux/input/mt.h>
#include <linux/usb/input.h>
#include "surface-data.h"
/* version information */
#define DRIVER_VERSION "0.0.1"
#define DRIVER_SHORT "surface-input"
#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>"
#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver"
/* vendor and device IDs */
#define ID_MICROSOFT 0x045e
#define ID_SURFACE 0x0775
/* touch data endpoint */
#define TOUCH_ENDPOINT 0x86
/* polling interval (ms) */
#define POLL_INTERVAL 10
/* maximum number of contacts */
#define MAX_CONTACTS 64
/* device ID table */
static const struct usb_device_id surface_table[] = {
{USB_DEVICE(ID_MICROSOFT, ID_SURFACE)}, /* Microsoft Surface 2.0 */
{} /* terminating null entry */
};
/* control commands */
#define SURFACE_GET_VERSION 0xb0 /* 12 bytes string */
#define SURFACE_UNKNOWN1 0xb3 /* 5 bytes */
#define SURFACE_UNKNOWN2 0xc1 /* 24 bytes */
#define SURFACE_GET_STATE 0xc5 /* 4 bytes state (?) */
#define SURFACE_GET_SENSORS 0xb1 /* 8 bytes sensors */
/* FIXME: previous version used USB_RECIP_ENDPOINT, may have corrupted eeprom? */
#define surface_command(dev, command, index, buffer, size) \
usb_control_msg (dev->usbdev, usb_rcvctrlpipe (dev->usbdev, 0), command, \
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x00, index, buffer, size, 1000)
MODULE_DEVICE_TABLE(usb, surface_table);
/* structure to hold all of our device specific stuff */
struct usb_surface {
struct usb_device *usbdev; /* save the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
struct input_polled_dev *input; /* struct for polled input device */
unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the maximum bulk packet size */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
char phys[64];
};
/* initialization routine, called from surface_open */
static int surface_init(struct usb_surface *dev)
{
int result;
__u8 buffer[24];
/* stupidly replay the original MS driver init sequence */
result = surface_command(dev, SURFACE_GET_VERSION, 0x00, buffer, 12 );
if (result < 0)
return result;
result = surface_command(dev, SURFACE_GET_VERSION, 0x01, buffer, 12 );
if (result < 0)
return result;
result = surface_command(dev, SURFACE_GET_VERSION, 0x02, buffer, 12 );
if (result < 0)
return result;
result = surface_command(dev, SURFACE_UNKNOWN2, 0x00, buffer, 24 );
if (result < 0)
return result;
result = surface_command(dev, SURFACE_UNKNOWN1, 0x00, buffer, 5 );
if (result < 0)
return result;
result = surface_command(dev, SURFACE_GET_VERSION, 0x03, buffer, 12 );
/* discard the result buffer - no known data inside except
some version strings, maybe extract these sometime.. */
return result;
}
/*
* callback routines from input_polled_dev
*/
/* enable the device, polling will now start */
void surface_open(struct input_polled_dev *polldev) {
struct usb_surface *surface = polldev->private;
pr_info("surface_open");
surface_init(surface);
}
/* disable device, polling has stopped */
void surface_close(struct input_polled_dev *polldev) {
/* no known way to stop the device, except to stop polling */
pr_info("surface_close");
}
/*
* this function is called when a whole contact has been processed,
* so that it can assign it to a slot and store the data there
*/
static void report_blob(struct surface_blob *blob, struct input_dev *input)
{
int wide, major, minor;
int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
if (slotnum < 0 || slotnum >= MAX_CONTACTS)
return;
/* FIXME: is this needed for the Pixelsense? */
/*if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
struct input_mt *mt = input->mt;
struct input_mt_slot *slot = &mt->slots[slotnum];
if (input_mt_is_active(slot) &&
input_mt_is_used(mt, slot))
return;
}*/
input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
wide = (blob->bb_size_x > blob->bb_size_y);
major = max(blob->bb_size_x, blob->bb_size_y);
minor = min(blob->bb_size_x, blob->bb_size_y);
input_event(input, EV_ABS, ABS_MT_POSITION_X, blob->pos_x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, blob->pos_y);
input_event(input, EV_ABS, ABS_MT_TOOL_X, blob->ctr_x);
input_event(input, EV_ABS, ABS_MT_TOOL_Y, blob->ctr_y);
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
}
/* core function: poll for new input data */
void surface_poll(struct input_polled_dev *polldev) {
struct usb_surface *surface = polldev->private;
struct input_dev *input = polldev->input;
int result, bulk_read, need_blobs, packet_blobs, i;
uint32_t packet_id;
struct surface_header* header = (struct surface_header*)(surface->bulk_in_buffer);
struct surface_blob* inblob = (struct surface_blob*)(surface->bulk_in_buffer+sizeof(struct surface_header));
need_blobs = -1;
pr_info("surface_poll\n");
do {
/* perform a blocking bulk read to get data from the device */
result = usb_bulk_msg (surface->usbdev,
usb_rcvbulkpipe (surface->usbdev, surface->bulk_in_endpointAddr),
surface->bulk_in_buffer, 512, &bulk_read, 1000);
pr_info("got %d bytes\n",bulk_read);
if (result < 0) {
pr_err("error in usb_bulk_read");
return;
}
result = bulk_read - sizeof(struct surface_header);
if (result % sizeof(struct surface_blob) != 0) {
pr_err("transfer size mismatch");
return;
}
/* first packet? */
if (need_blobs == -1) {
need_blobs = header->count;
pr_info("expecting %d blobs\n",need_blobs);
packet_id = header->packet_id;
}
/* sanity check. when video data is also being retrieved, the packet ID
will usually increase in the middle of a series instead of at the end. */
if (packet_id != header->packet_id)
pr_warn("packet ID mismatch\n");
packet_blobs = result / sizeof(struct surface_blob);
pr_info("got %d blobs\n",packet_blobs);
/* packets always contain at least 4 blobs, even if they are empty */
if (packet_blobs > need_blobs)
packet_blobs = need_blobs;
for (i = 0; i < packet_blobs; i++) {
need_blobs--;
pr_info("got a blob\n");
report_blob( &(inblob[i]), input );
}
} while (need_blobs > 0);
input_mt_sync_frame(input);
input_sync(input);
}
/*
* power management routines
*/
/* PM operations are nops as this driver does IO only during open() */
/*static int surface_suspend(struct usb_interface *intf, pm_message_t message)
{
return 0;
}
static int surface_resume(struct usb_interface *intf)
{
return 0;
}*/
/*
* housekeeping routines
*/
/* clean up all allocated buffers/structs */
static inline void surface_delete(struct usb_surface *surface)
{
input_free_polled_device(surface->input);
kfree(surface->bulk_in_buffer);
kfree(surface);
}
/* check candidate USB interface */
static int surface_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *usbdev = interface_to_usbdev(interface);
struct usb_surface *surface;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct input_polled_dev* poll_dev;
/* check if we really have the right interface */
iface_desc = &interface->altsetting[0];
if (iface_desc->desc.bInterfaceClass != 0xFF)
return -ENODEV;
/* allocate memory for our device state and initialize it */
surface = kzalloc(sizeof(struct usb_surface), GFP_KERNEL);
poll_dev = input_allocate_polled_device();
surface->input = poll_dev;
if (!surface || !poll_dev)
return -ENOMEM;
poll_dev->private = surface;
poll_dev->poll_interval = POLL_INTERVAL;
poll_dev->open = surface_open;
poll_dev->poll = surface_poll;
poll_dev->close = surface_close;
poll_dev->input->name = "Samsung SUR40";
usb_to_input_id(usbdev,&(poll_dev->input->id));
usb_make_path(usbdev, surface->phys, sizeof(surface->phys));
strlcat(surface->phys, "/input0", sizeof(surface->phys));
poll_dev->input->phys = surface->phys;
input_mt_init_slots(poll_dev->input, MAX_CONTACTS, INPUT_MT_DIRECT);
surface->usbdev = usbdev;
surface->interface = interface;
/* use endpoint #4 (0x86) */
endpoint = &iface_desc->endpoint[4].desc;
if (endpoint->bEndpointAddress == TOUCH_ENDPOINT) {
/* we found a bulk in endpoint */
surface->bulk_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
surface->bulk_in_endpointAddr = endpoint->bEndpointAddress;
surface->bulk_in_buffer = kmalloc(2*surface->bulk_in_size, GFP_KERNEL);
if (!surface->bulk_in_buffer) {
pr_err("Unable to allocate input buffer.");
surface_delete(surface);
return -ENOMEM;
}
}
if (!(surface->bulk_in_endpointAddr)) {
pr_err("Unable to find bulk-in endpoint.");
surface_delete(surface);
return -ENODEV;
}
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, surface);
if (input_register_polled_device(poll_dev)) {
pr_err("Unable to register polled input device.");
surface_delete(surface);
return -ENODEV;
}
dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
return 0;
}
/* unregister device & clean up */
static void surface_disconnect(struct usb_interface *interface)
{
struct usb_surface *surface = usb_get_intfdata(interface);
input_unregister_polled_device(surface->input);
usb_set_intfdata(interface, NULL);
surface_delete(surface);
dev_info(&interface->dev, "%s now disconnected\n",DRIVER_DESC);
}
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver surface_driver = {
.name = DRIVER_SHORT,
.probe = surface_probe,
.disconnect = surface_disconnect,
/*.suspend = surface_suspend,
.resume = surface_resume,*/
.id_table = surface_table,
/*.supports_autosuspend = 1,*/
};
module_usb_driver(surface_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* [PATCH 2/2] Input: ALPS Touchpad- Improve the performance of alps v5-protocol's touchpad
From: yunkang.tang @ 2013-09-05 5:50 UTC (permalink / raw)
To: linux-input@vger.kernel.org
Cc: dmitry.torokhov@gmail.com, yunkang.tang@cn.alps.com
<Change list>
- Add the macro definition for v5 device.
--- linux-3.11/drivers/input/mouse/alps.h.orig 2013-09-04 19:51:33.837135870 +0800
+++ linux-3.11/drivers/input/mouse/alps.h 2013-09-05 21:01:57.074314890 +0800
@@ -18,6 +18,10 @@
#define ALPS_PROTO_V4 4
#define ALPS_PROTO_V5 5
+#define DOLPHIN_COUNT_PER_ELECTRODE 64
+#define DOLPHIN_PROFILE_XOFFSET 8 /* [DOLPHIN] The number of the x-electrode offset value */
+#define DOLPHIN_PROFILE_YOFFSET 1 /* [DOLPHIN] The number of the y-electrode offset value */
+
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
@@ -69,7 +73,7 @@ struct alps_nibble_commands {
* @y: Y position for ST.
* @z: Z position for ST.
* @first_mp: Packet is the first of a multi-packet report.
- * @is_mp: Packet is part of a multi-packet report.
+ * @is_mp: Packet is the last of a multi-packet report.
* @left: Left touchpad button is active.
* @right: Right touchpad button is active.
* @middle: Middle touchpad button is active.
@@ -145,7 +149,7 @@ struct alps_data {
int (*hw_init)(struct psmouse *psmouse);
void (*process_packet)(struct psmouse *psmouse);
- void (*decode_fields)(struct alps_fields *f, unsigned char *p);
+ void (*decode_fields)(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse);
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
int prev_fin;
-Tommy
^ permalink raw reply
* [PATCH 1/2] Input: ALPS Touchpad- Improve the performance of alps v5-protocol's touchpad
From: yunkang.tang @ 2013-09-05 5:50 UTC (permalink / raw)
To: linux-input@vger.kernel.org
Cc: dmitry.torokhov@gmail.com, yunkang.tang@cn.alps.com
<Change list>
- Calculate the device's dimension in alps_identify().
- Change the logic of packet decoding.
- Add the new data process logic.
- Change the dev2's name from "PS/2 Mouse" to "ALPS Touchpad".
--- linux-3.11/drivers/input/mouse/alps.c.orig 2013-09-04 19:51:23.585085033 +0800
+++ linux-3.11/drivers/input/mouse/alps.c 2013-09-05 11:58:02.000000000 +0800
@@ -257,6 +257,74 @@ static void alps_process_packet_v1_v2(st
}
/*
+ * Process bitmap data for V5 protocols. Return value is null.
+ *
+ * The bitmaps don't have enough data to track fingers, so this function
+ * only generates points representing a bounding box of at most two contacts.
+ * These two points are returned in x1, y1, x2, and y2.
+ */
+static void alps_process_bitmap_dolphin(struct alps_data *priv, struct alps_fields *fields,
+ int *x1, int *y1, int *x2, int *y2)
+{
+ struct alps_palm_bitmap {
+ int start_bit;
+ int end_bit;
+ };
+
+ int i;
+ int box_middle_x, box_middle_y;
+ unsigned int x_map, y_map;
+ struct alps_palm_bitmap x_bitmap = {0}, y_bitmap = {0};
+
+ x_map = fields->x_map;
+ y_map = fields->y_map;
+
+ if (!x_map || !y_map)
+ return;
+
+ *x1 = *y1 = *x2 = *y2 = 0;
+
+ if (fields->fingers > 1) {
+ for (i = 0; i < 32; i++) {
+ if (x_map & (1 << i)) {
+ x_bitmap.end_bit = priv->x_bits - i;
+ break;
+ }
+ }
+
+ for (i = 31; i >= 0; i--) {
+ if (x_map & (1 << i)) {
+ x_bitmap.start_bit = priv->x_bits - i;
+ break;
+ }
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (y_map & (1 << i)) {
+ y_bitmap.start_bit = i;
+ break;
+ }
+ }
+
+ for (i = 31; i >= 0; i--) {
+ if (y_map & (1 << i)) {
+ y_bitmap.end_bit = i;
+ break;
+ }
+ }
+
+ box_middle_x = (priv->x_max * (x_bitmap.start_bit + x_bitmap.end_bit)) /
+ (2 * (priv->x_bits - 1));
+ box_middle_y = (priv->y_max * (y_bitmap.start_bit + y_bitmap.end_bit)) /
+ (2 * (priv->y_bits - 1));
+ *x1 = fields->x;
+ *y1 = fields->y;
+ *x2 = 2 * box_middle_x - *x1;
+ *y2 = 2 * box_middle_y - *y1;
+ }
+}
+
+/*
* Process bitmap data from v3 and v4 protocols. Returns the number of
* fingers detected. A return value of 0 means at least one of the
* bitmaps was empty.
@@ -461,7 +529,7 @@ static void alps_decode_buttons_v3(struc
f->ts_middle = !!(p[3] & 0x40);
}
-static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p)
+static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
{
f->first_mp = !!(p[4] & 0x40);
f->is_mp = !!(p[0] & 0x40);
@@ -482,35 +550,65 @@ static void alps_decode_pinnacle(struct
alps_decode_buttons_v3(f, p);
}
-static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p)
+static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
{
- alps_decode_pinnacle(f, p);
+ alps_decode_pinnacle(f, p, psmouse);
f->x_map |= (p[5] & 0x10) << 11;
f->y_map |= (p[5] & 0x20) << 6;
}
-static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p)
+static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
{
+ unsigned int palm_high = 0, palm_low = 0, palm_mask = 0;
+ int i, remain_xbits;
+ struct alps_data *priv = psmouse->private;
+
f->first_mp = !!(p[0] & 0x02);
f->is_mp = !!(p[0] & 0x20);
- f->fingers = ((p[0] & 0x6) >> 1 |
+ if (!f->is_mp) {
+ f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
+ f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
+ f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
+ alps_decode_buttons_v3(f, p);
+ } else {
+ f->fingers = ((p[0] & 0x6) >> 1 |
(p[0] & 0x10) >> 2);
- f->x_map = ((p[2] & 0x60) >> 5) |
- ((p[4] & 0x7f) << 2) |
- ((p[5] & 0x7f) << 9) |
- ((p[3] & 0x07) << 16) |
- ((p[3] & 0x70) << 15) |
- ((p[0] & 0x01) << 22);
- f->y_map = (p[1] & 0x7f) |
- ((p[2] & 0x1f) << 7);
-
- f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
- f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
- f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
- alps_decode_buttons_v3(f, p);
+ palm_low = (p[1] & 0x7f) |
+ ((p[2] & 0x7f) << 7) |
+ ((p[4] & 0x7f) << 14) |
+ ((p[5] & 0x7f) << 21) |
+ ((p[3] & 0x07) << 28) | ((p[3] & 0x10) << 27);
+ palm_high = ((p[3] & 0x60) >> 5) | ((p[0] & 0x01) << 2);
+
+ for (i = 0; i < priv->y_bits; i++) {
+ palm_mask |= 1 << i;
+ }
+ /* Y-profile is stored in P(0) to p(n), n = y_bits; */
+ f->y_map = palm_low & palm_mask;
+
+ /* X-profile is stored in p(n) to p(n+x_bits). */
+ palm_mask = 0;
+ for (i = priv->y_bits; i < 32 && i < priv->y_bits + priv->x_bits; i++) {
+ palm_mask |= 1 << i;
+ }
+ f->x_map = ((palm_low & (palm_mask)) >> priv->y_bits);
+
+ /*
+ * In some cases, palm_low's 32bit is not enough to save all X&Y-profiles,
+ * we need to use palm_high.
+ */
+ remain_xbits = priv->x_bits + priv->y_bits - 32;
+ if (remain_xbits > 0) {
+ palm_mask = 0;
+ for (i = 0; i < remain_xbits; i++) {
+ palm_mask |= 1 << i;
+ }
+ f->x_map |= ((palm_high & palm_mask) << (32- priv->y_bits));
+ }
+ }
}
static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
@@ -523,7 +621,7 @@ static void alps_process_touchpad_packet
int fingers = 0, bmap_fingers;
struct alps_fields f;
- priv->decode_fields(&f, packet);
+ priv->decode_fields(&f, packet, psmouse);
/*
* There's no single feature of touchpad position and bitmap packets
@@ -552,7 +650,7 @@ static void alps_process_touchpad_packet
fingers = bmap_fingers;
/* Now process position packet */
- priv->decode_fields(&f, priv->multi_data);
+ priv->decode_fields(&f, priv->multi_data, psmouse);
} else {
priv->multi_packet = 0;
}
@@ -742,6 +840,111 @@ static void alps_process_packet_v4(struc
input_sync(dev);
}
+
+static void alps_process_touchpad_packet_v5(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ struct input_dev *dev = psmouse->dev;
+ int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ int fingers = 0;
+ struct alps_fields f;
+
+ priv->decode_fields(&f, packet, psmouse);
+
+ /*
+ * There's no single feature of touchpad position and bitmap packets
+ * that can be used to distinguish between them. We rely on the fact
+ * that a bitmap packet should always follow a position packet with
+ * bit 6 of packet[4] set.
+ */
+ if (priv->multi_packet) {
+ /*
+ * Sometimes a position packet will indicate a multi-packet
+ * sequence, but then what follows is another position
+ * packet. Check for this, and when it happens process the
+ * position packet as usual.
+ */
+ if (f.is_mp) {
+ fingers = f.fingers;
+ priv->decode_fields(&f, priv->multi_data, psmouse);
+ alps_process_bitmap_dolphin(priv, &f, &x1, &y1, &x2, &y2);
+ } else {
+ priv->multi_packet = 0;
+ }
+ }
+
+ /*
+ * Bit 6 of byte 0 is not usually set in position packets. The only
+ * times it seems to be set is in situations where the data is
+ * suspect anyway, e.g. a palm resting flat on the touchpad. Given
+ * this combined with the fact that this bit is useful for filtering
+ * out misidentified bitmap packets, we reject anything with this
+ * bit set.
+ */
+ if (f.is_mp)
+ return;
+
+ if (!priv->multi_packet && f.first_mp) {
+ priv->multi_packet = 1;
+ memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+ return;
+ }
+
+ priv->multi_packet = 0;
+
+ /*
+ * Sometimes the hardware sends a single packet with z = 0
+ * in the middle of a stream. Real releases generate packets
+ * with x, y, and z all zero, so these seem to be flukes.
+ * Ignore them.
+ */
+ if (f.x && f.y && !f.z)
+ return;
+
+ /*
+ * If we don't have MT data or the bitmaps were empty, we have
+ * to rely on ST data.
+ */
+ if (!fingers) {
+ x1 = f.x;
+ y1 = f.y;
+ fingers = f.z > 0 ? 1 : 0;
+ }
+
+ if (f.z >= 64)
+ input_report_key(dev, BTN_TOUCH, 1);
+ else
+ input_report_key(dev, BTN_TOUCH, 0);
+
+ alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+
+ input_mt_report_finger_count(dev, fingers);
+
+ input_report_key(dev, BTN_LEFT, f.left);
+ input_report_key(dev, BTN_RIGHT, f.right);
+ input_report_key(dev, BTN_MIDDLE, f.middle);
+
+ if (f.z > 0) {
+ input_report_abs(dev, ABS_X, f.x);
+ input_report_abs(dev, ABS_Y, f.y);
+ }
+ input_report_abs(dev, ABS_PRESSURE, f.z);
+
+ input_sync(dev);
+}
+
+static void alps_process_packet_v5(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+
+ /* Ignore stick point data */
+ if (packet[0] == 0xD8)
+ return;
+
+ alps_process_touchpad_packet_v5(psmouse);
+}
+
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
@@ -1519,6 +1722,40 @@ error:
return -1;
}
+static int alps_dolphin_get_device_area(struct psmouse *psmouse, struct alps_data *priv)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4] = {0};
+ int num_x_electrode, num_y_electrode;
+
+ if (alps_enter_command_mode(psmouse))
+ return -1;
+
+ if (ps2_command(ps2dev, NULL, 0x00EC) ||
+ ps2_command(ps2dev, NULL, 0x00F0) ||
+ ps2_command(ps2dev, NULL, 0x00F0) ||
+ ps2_command(ps2dev, NULL, 0x00F3) ||
+ ps2_command(ps2dev, NULL, 0x000A) ||
+ ps2_command(ps2dev, NULL, 0x00F3) ||
+ ps2_command(ps2dev, NULL, 0x000A))
+ return -1;
+
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+ return -1;
+
+ num_x_electrode = DOLPHIN_PROFILE_XOFFSET + ( param[2] & 0x0F );
+ num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ( (param[2] >> 4) & 0x0F );
+ priv->x_bits = num_x_electrode;
+ priv->y_bits = num_y_electrode;
+ priv->x_max = (num_x_electrode - 1 ) * DOLPHIN_COUNT_PER_ELECTRODE;
+ priv->y_max = (num_y_electrode - 1 ) * DOLPHIN_COUNT_PER_ELECTRODE;
+
+ if (alps_exit_command_mode(psmouse))
+ return -1;
+
+ return 0;
+}
+
static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
@@ -1571,7 +1808,7 @@ static void alps_set_defaults(struct alp
break;
case ALPS_PROTO_V5:
priv->hw_init = alps_hw_init_dolphin_v1;
- priv->process_packet = alps_process_packet_v3;
+ priv->process_packet = alps_process_packet_v5;
priv->decode_fields = alps_decode_dolphin;
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
@@ -1645,11 +1882,13 @@ static int alps_identify(struct psmouse
if (alps_match_table(psmouse, priv, e7, ec) == 0) {
return 0;
} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
- ec[0] == 0x73 && ec[1] == 0x01) {
+ ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
priv->proto_version = ALPS_PROTO_V5;
alps_set_defaults(priv);
-
- return 0;
+ if (alps_dolphin_get_device_area(psmouse, priv))
+ return -EIO;
+ else
+ return 0;
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
priv->proto_version = ALPS_PROTO_V3;
alps_set_defaults(priv);
@@ -1792,7 +2031,7 @@ int alps_init(struct psmouse *psmouse)
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
dev2->name = (priv->flags & ALPS_DUALPOINT) ?
- "DualPoint Stick" : "PS/2 Mouse";
+ "DualPoint Stick" : "ALPS Touchpad";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_ALPS;
-Tommy
^ permalink raw reply
* [PATCH 0/2] Input: ALPS Touchpad- Improve the performance of alps v5-protocol's touchpad
From: yunkang.tang @ 2013-09-05 5:50 UTC (permalink / raw)
To: linux-input@vger.kernel.org
Cc: dmitry.torokhov@gmail.com, yunkang.tang@cn.alps.com
Hi all,
These patches improve the performance of alps v5-protocol's touchpad (Dolphin).
Target device info:
Device ID = 0x73, 0x03, 0x50
Firmware ID = 0x73, 0x01/0x02, 0xXX
-Tommy
^ permalink raw reply
* Re: [PATCH 12/14] HID: sensor-hub: validate feature report details
From: Kees Cook @ 2013-09-04 18:26 UTC (permalink / raw)
To: Jiri Kosina; +Cc: Mika Westerberg, linux-input, srinivas pandruvada
In-Reply-To: <alpine.LNX.2.00.1309042014140.3684@pobox.suse.cz>
On Wed, Sep 4, 2013 at 11:14 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Wed, 4 Sep 2013, Kees Cook wrote:
>
>> Should this one have been part of the batch you applied? It doesn't
>> use hid_validate_report().
>
> It's there [1], I just somehow forgot to send out information mail, sorry
> for that.
>
> [1] https://git.kernel.org/cgit/linux/kernel/git/jikos/hid.git/commit/?h=for-3.12/upstream&id=9e8910257397372633e74b333ef891f20c800ee4
Ah-ha! Okay, thanks. :)
-Kees
--
Kees Cook
Chrome OS Security
^ permalink raw reply
* Re: [PATCH 12/14] HID: sensor-hub: validate feature report details
From: Jiri Kosina @ 2013-09-04 18:14 UTC (permalink / raw)
To: Kees Cook; +Cc: Mika Westerberg, linux-input, srinivas pandruvada
In-Reply-To: <CAGXu5jJ4zFJ78=85T6QZDVXZ9GEM_vgbEP4hERjRwQP3RXYkjQ@mail.gmail.com>
On Wed, 4 Sep 2013, Kees Cook wrote:
> Should this one have been part of the batch you applied? It doesn't
> use hid_validate_report().
It's there [1], I just somehow forgot to send out information mail, sorry
for that.
[1] https://git.kernel.org/cgit/linux/kernel/git/jikos/hid.git/commit/?h=for-3.12/upstream&id=9e8910257397372633e74b333ef891f20c800ee4
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* [PATCH 1/7] HID: provide a helper for validating hid reports
From: Kees Cook @ 2013-09-04 16:37 UTC (permalink / raw)
To: linux-input
Cc: Benjamin Tissoires, Jiri Kosina, Henrik Rydberg, Kees Cook,
stable
In-Reply-To: <1378312645-27736-1-git-send-email-keescook@chromium.org>
Many drivers need to validate the characteristics of their HID report
during initialization to avoid misusing the reports. This adds a common
helper to perform validation of the report exisitng, the field existing,
and the expected number of values within the field.
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: stable@kernel.org
---
v2:
- suggestions from Benjamin Tissoires:
- check id too, just to be double-safe.
- updated to check a specific field, moving the for loop to callers.
---
drivers/hid/hid-core.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/hid.h | 4 ++++
2 files changed, 62 insertions(+)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 21e3b9d..dc23284 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -759,6 +759,64 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size)
}
EXPORT_SYMBOL_GPL(hid_parse_report);
+static const char * const hid_report_names[] = {
+ "HID_INPUT_REPORT",
+ "HID_OUTPUT_REPORT",
+ "HID_FEATURE_REPORT",
+};
+/**
+ * hid_validate_values - validate existing device report's value indexes
+ *
+ * @device: hid device
+ * @type: which report type to examine
+ * @id: which report ID to examine (0 for first)
+ * @field_index: which report field to examine
+ * @report_counts: expected number of values
+ *
+ * Validate the number of values in a given field of a given report, after
+ * parsing.
+ */
+struct hid_report *hid_validate_values(struct hid_device *hid,
+ unsigned int type, unsigned int id,
+ unsigned int field_index,
+ unsigned int report_counts)
+{
+ struct hid_report *report;
+
+ if (type > HID_FEATURE_REPORT) {
+ hid_err(hid, "invalid HID report type %u\n", type);
+ return NULL;
+ }
+
+ if (id >= HID_MAX_IDS) {
+ hid_err(hid, "invalid HID report id %u\n", id);
+ return NULL;
+ }
+
+ /*
+ * Explicitly not using hid_get_report() here since it depends on
+ * ->numbered being checked, which may not always be the case when
+ * drivers go to access report values.
+ */
+ report = hid->report_enum[type].report_id_hash[id];
+ if (!report) {
+ hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
+ return NULL;
+ }
+ if (report->maxfield <= field_index) {
+ hid_err(hid, "not enough fields in %s %u\n",
+ hid_report_names[type], id);
+ return NULL;
+ }
+ if (report->field[field_index]->report_count < report_counts) {
+ hid_err(hid, "not enough values in %s %u field %u\n",
+ hid_report_names[type], id, field_index);
+ return NULL;
+ }
+ return report;
+}
+EXPORT_SYMBOL_GPL(hid_validate_values);
+
/**
* hid_open_report - open a driver-specific device report
*
diff --git a/include/linux/hid.h b/include/linux/hid.h
index ff545cc..6e18550 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -749,6 +749,10 @@ void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
+struct hid_report *hid_validate_values(struct hid_device *hid,
+ unsigned int type, unsigned int id,
+ unsigned int field_index,
+ unsigned int report_counts);
int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 5/7] HID: LG: validate HID output report details
From: Kees Cook @ 2013-09-04 16:37 UTC (permalink / raw)
To: linux-input
Cc: Benjamin Tissoires, Jiri Kosina, Henrik Rydberg, Kees Cook,
stable
In-Reply-To: <1378312645-27736-1-git-send-email-keescook@chromium.org>
A HID device could send a malicious output report that would cause the
lg, lg3, and lg4 HID drivers to write beyond the output report allocation
during an event, causing a heap overflow:
[ 325.245240] usb 1-1: New USB device found, idVendor=046d, idProduct=c287
...
[ 414.518960] BUG kmalloc-4096 (Not tainted): Redzone overwritten
Additionally, while lg2 did correctly validate the report details, it was
cleaned up and shortened.
CVE-2013-2893
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: stable@kernel.org
---
drivers/hid/hid-lg2ff.c | 19 +++----------------
drivers/hid/hid-lg3ff.c | 29 ++++++-----------------------
drivers/hid/hid-lg4ff.c | 20 +-------------------
drivers/hid/hid-lgff.c | 17 ++---------------
4 files changed, 12 insertions(+), 73 deletions(-)
diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
index b3cd150..1a42eaa 100644
--- a/drivers/hid/hid-lg2ff.c
+++ b/drivers/hid/hid-lg2ff.c
@@ -64,26 +64,13 @@ int lg2ff_init(struct hid_device *hid)
struct hid_report *report;
struct hid_input *hidinput = list_entry(hid->inputs.next,
struct hid_input, list);
- struct list_head *report_list =
- &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int error;
- if (list_empty(report_list)) {
- hid_err(hid, "no output report found\n");
+ /* Check that the report looks ok */
+ report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
+ if (!report)
return -ENODEV;
- }
-
- report = list_entry(report_list->next, struct hid_report, list);
-
- if (report->maxfield < 1) {
- hid_err(hid, "output report is empty\n");
- return -ENODEV;
- }
- if (report->field[0]->report_count < 7) {
- hid_err(hid, "not enough values in the field\n");
- return -ENODEV;
- }
lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
if (!lg2ff)
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c
index e52f181..8c2da18 100644
--- a/drivers/hid/hid-lg3ff.c
+++ b/drivers/hid/hid-lg3ff.c
@@ -66,10 +66,11 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data,
int x, y;
/*
- * Maxusage should always be 63 (maximum fields)
- * likely a better way to ensure this data is clean
+ * Available values in the field should always be 63, but we only use up to
+ * 35. Instead, clear the entire area, however big it is.
*/
- memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage);
+ memset(report->field[0]->value, 0,
+ sizeof(__s32) * report->field[0]->report_count);
switch (effect->type) {
case FF_CONSTANT:
@@ -129,32 +130,14 @@ static const signed short ff3_joystick_ac[] = {
int lg3ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
const signed short *ff_bits = ff3_joystick_ac;
int error;
int i;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- hid_err(hid, "NULL output report\n");
- return -1;
- }
-
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
+ return -ENODEV;
/* Assume single fixed device G940 */
for (i = 0; ff_bits[i] >= 0; i++)
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 0ddae2a..8782fe1 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -484,34 +484,16 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
int lg4ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
struct usb_device_descriptor *udesc;
int error, i, j;
__u16 bcdDevice, rev_maj, rev_min;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- hid_err(hid, "NULL output report\n");
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -1;
- }
-
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
/* Check what wheel has been connected */
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index d7ea8c8..e1394af 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -128,27 +128,14 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
int lgff_init(struct hid_device* hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
- struct hid_report *report;
- struct hid_field *field;
const signed short *ff_bits = ff_joystick;
int error;
int i;
- /* Find the report to use */
- if (list_empty(report_list)) {
- hid_err(hid, "No output report found\n");
- return -1;
- }
-
/* Check that the report looks ok */
- report = list_entry(report_list->next, struct hid_report, list);
- field = report->field[0];
- if (!field) {
- hid_err(hid, "NULL field\n");
- return -1;
- }
+ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
+ return -ENODEV;
for (i = 0; i < ARRAY_SIZE(devices); i++) {
if (dev->id.vendor == devices[i].idVendor &&
--
1.7.9.5
^ permalink raw reply related
* [PATCH 7/7] HID: logitech-dj: validate output report details
From: Kees Cook @ 2013-09-04 16:37 UTC (permalink / raw)
To: linux-input
Cc: Benjamin Tissoires, Jiri Kosina, Henrik Rydberg, Kees Cook,
stable
In-Reply-To: <1378312645-27736-1-git-send-email-keescook@chromium.org>
A HID device could send a malicious output report that would cause the
logitech-dj HID driver to leak kernel memory contents to the device, or
trigger a NULL dereference during initialization:
[ 304.424553] usb 1-1: New USB device found, idVendor=046d, idProduct=c52b
...
[ 304.780467] BUG: unable to handle kernel NULL pointer dereference at 0000000000000028
[ 304.781409] IP: [<ffffffff815d50aa>] logi_dj_recv_send_report.isra.11+0x1a/0x90
CVE-2013-2895
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: stable@kernel.org
---
drivers/hid/hid-logitech-dj.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index cd33084..404f2d0 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -461,7 +461,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
struct hid_report *report;
struct hid_report_enum *output_report_enum;
u8 *data = (u8 *)(&dj_report->device_index);
- int i;
+ unsigned int i, length;
output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
@@ -471,7 +471,9 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
return -ENODEV;
}
- for (i = 0; i < report->field[0]->report_count; i++)
+ length = min_t(size_t, sizeof(*dj_report) - 1,
+ report->field[0]->report_count);
+ for (i = 0; i < length; i++)
report->field[0]->value[i] = data[i];
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
@@ -783,6 +785,12 @@ static int logi_dj_probe(struct hid_device *hdev,
goto hid_parse_fail;
}
+ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT,
+ 0, 3)) {
+ retval = -ENODEV;
+ goto hid_parse_fail;
+ }
+
/* Starts the usb device and connects to upper interfaces hiddev and
* hidraw */
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox