All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
@ 2013-11-06 14:26 Florian Echtler
  2013-11-08 21:37 ` Henrik Rydberg
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Florian Echtler @ 2013-11-06 14:26 UTC (permalink / raw
  To: linux-input, benjamin.tissoires, rydberg, dmitry.torokhov,
	dh.herrmann
  Cc: Florian Echtler

From: "Florian Echtler" <floe@butterbrot.org>

This patch adds support for the built-in multitouch sensor in the Samsung
SUR40 touchscreen device, also known as Microsoft Surface 2.0 or Microsoft
Pixelsense. Support for raw video output from the sensor as well as the 
accelerometer will be added in a later patch.

Signed-off-by: Florian Echtler <floe@butterbrot.org>
---
 drivers/input/touchscreen/Kconfig  |   10 +
 drivers/input/touchscreen/Makefile |    1 +
 drivers/input/touchscreen/sur40.c  |  467 ++++++++++++++++++++++++++++++++++++
 3 files changed, 478 insertions(+)
 create mode 100644 drivers/input/touchscreen/sur40.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 515cfe7..99aaf10 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -876,6 +876,16 @@ config TOUCHSCREEN_STMPE
 	  To compile this driver as a module, choose M here: the
 	  module will be called stmpe-ts.
 
+config TOUCHSCREEN_SUR40
+	tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
+	depends on USB
+	help
+	  Say Y here if you want support for the Samsung SUR40 touchscreen
+	  (also known as Microsoft Surface 2.0 or Microsoft PixelSense).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sur40.
+
 config TOUCHSCREEN_TPS6507X
 	tristate "TPS6507x based touchscreens"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6bfbeab..b63a25d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUR40)		+= sur40.o
 obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)	+= ti_am335x_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
new file mode 100644
index 0000000..48787fd
--- /dev/null
+++ b/drivers/input/touchscreen/sur40.c
@@ -0,0 +1,467 @@
+/*
+	Surface2.0/SUR40/PixelSense input driver
+
+	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)
+
+	and from the Apple USB BCM5974 multitouch driver,
+	Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
+
+	and from the generic hid-multitouch driver,
+	Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+*/
+
+#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 <linux/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>
+
+/* read 512 bytes from endpoint 0x86 -> get header + blobs */
+struct sur40_header {
+
+	__le16 type;       /* always 0x0001 */
+	__le16 count;      /* count of blobs (if 0: continue prev. packet) */
+
+	__le32 packet_id;  /* unique ID for all packets in one frame */
+
+	__le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */
+	__le32 unknown;    /* "epoch?" always 02/03 00 00 00 */
+
+} __packed;
+
+struct sur40_blob {
+
+	__le16 blob_id;
+
+	__u8 action;       /* 0x02 = enter/exit, 0x03 = update (?) */
+	__u8 unknown;      /* always 0x01 or 0x02 (no idea what this is?) */
+
+	__le16 bb_pos_x;   /* upper left corner of bounding box */
+	__le16 bb_pos_y;
+
+	__le16 bb_size_x;  /* size of bounding box */
+	__le16 bb_size_y;
+
+	__le16 pos_x;      /* finger tip position */
+	__le16 pos_y;
+
+	__le16 ctr_x;      /* centroid position */
+	__le16 ctr_y;
+
+	__le16 axis_x;     /* somehow related to major/minor axis, mostly: */
+	__le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */
+
+	__le32 angle;      /* orientation in radians relative to x axis -
+	                      actually an IEEE754 float, don't use in kernel */
+
+	__le32 area;       /* size in pixels/pressure (?) */
+
+	__u8 padding[32];
+
+} __packed;
+
+/* combined header/blob data */
+struct sur40_data {
+	struct sur40_header header;
+	struct sur40_blob   blobs[];
+} __packed;
+
+
+/* version information */
+#define DRIVER_SHORT   "sur40"
+#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_SUR40     0x0775
+
+/* sensor resolution */
+#define SENSOR_RES_X 1920
+#define SENSOR_RES_Y 1080
+
+/* touch data endpoint */
+#define TOUCH_ENDPOINT 0x86
+
+/* polling interval (ms) */
+#define POLL_INTERVAL 10
+
+/* maximum number of contacts FIXME: this is a guess? */
+#define MAX_CONTACTS 64
+
+/* device ID table */
+static const struct usb_device_id sur40_table[] = {
+	{USB_DEVICE(ID_MICROSOFT, ID_SUR40)},  /* Samsung SUR40 */
+	{}                                     /* terminating null entry */
+};
+
+/* control commands */
+#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */
+#define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */
+#define SUR40_UNKNOWN2    0xc1 /* 24 bytes           */
+
+#define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
+#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+#define sur40_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, sur40_table);
+
+/* structure to hold all of our device specific stuff */
+struct sur40_state {
+
+	struct usb_device *usbdev;      /* save the usb device pointer */
+	struct device *dev;             /* save the generic device pointer */
+	struct input_polled_dev *input; /* struct for polled input device */
+
+	struct sur40_data *bulk_in_buffer; /* the buffer to receive data */
+	size_t bulk_in_size;            /* the maximum bulk packet size */
+	__u8 bulk_in_epaddr;            /* address of the bulk in endpoint */
+
+	char phys[64];                  /* buffer for phys name */
+};
+
+/* initialization routine, called from sur40_open */
+static int sur40_init(struct sur40_state *dev)
+{
+	int result;
+	__u8 buffer[24];
+
+	/* stupidly replay the original MS driver init sequence */
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_UNKNOWN2,    0x00, buffer, 24);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_UNKNOWN1,    0x00, buffer,  5);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_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 sur40_open(struct input_polled_dev *polldev)
+{
+	struct sur40_state *sur40 = polldev->private;
+	dev_dbg(sur40->dev, "open\n");
+	sur40_init(sur40);
+}
+
+/* disable device, polling has stopped */
+void sur40_close(struct input_polled_dev *polldev)
+{
+	/* no known way to stop the device, except to stop polling */
+	struct sur40_state *sur40 = polldev->private;
+	dev_dbg(sur40->dev, "close\n");
+}
+
+/*
+ * 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 sur40_blob *blob, struct input_dev *input)
+{
+	int wide, major, minor;
+
+	int bb_size_x = le16_to_cpu(blob->bb_size_x);
+	int bb_size_y = le16_to_cpu(blob->bb_size_y);
+
+	int pos_x = le16_to_cpu(blob->pos_x);
+	int pos_y = le16_to_cpu(blob->pos_y);
+
+	int ctr_x = le16_to_cpu(blob->ctr_x);
+	int ctr_y = le16_to_cpu(blob->ctr_y);
+
+	int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
+	if (slotnum < 0 || slotnum >= MAX_CONTACTS)
+		return;
+
+	input_mt_slot(input, slotnum);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
+	wide = (bb_size_x > bb_size_y);
+	major = max(bb_size_x, bb_size_y);
+	minor = min(bb_size_x, bb_size_y);
+
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, pos_x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, pos_y);
+	input_event(input, EV_ABS, ABS_MT_TOOL_X, ctr_x);
+	input_event(input, EV_ABS, ABS_MT_TOOL_Y, ctr_y);
+
+	/* TODO: use a better orientation measure */
+	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 sur40_poll(struct input_polled_dev *polldev)
+{
+
+	struct sur40_state *sur40 = polldev->private;
+	struct input_dev *input = polldev->input;
+	int result, bulk_read, need_blobs, packet_blobs, i;
+	uint32_t packet_id;
+
+	struct sur40_header *header = &sur40->bulk_in_buffer->header;
+	struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
+
+	need_blobs = -1;
+
+	dev_dbg(sur40->dev, "poll\n");
+
+	do {
+
+		/* perform a blocking bulk read to get data from the device */
+		result = usb_bulk_msg(sur40->usbdev,
+			usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),
+			sur40->bulk_in_buffer, sur40->bulk_in_size,
+			&bulk_read, 1000);
+
+		dev_dbg(sur40->dev, "received %d bytes\n", bulk_read);
+
+		if (result < 0) {
+			dev_err(sur40->dev, "error in usb_bulk_read\n");
+			return;
+		}
+
+		result = bulk_read - sizeof(struct sur40_header);
+
+		if (result % sizeof(struct sur40_blob) != 0) {
+			dev_err(sur40->dev, "transfer size mismatch\n");
+			return;
+		}
+
+		/* first packet? */
+		if (need_blobs == -1) {
+			need_blobs = le16_to_cpu(header->count);
+			dev_dbg(sur40->dev, "need %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)
+			dev_warn(sur40->dev, "packet ID mismatch\n");
+
+		packet_blobs = result / sizeof(struct sur40_blob);
+		dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
+
+		/* packets always contain at least 4 blobs, even if empty */
+		if (packet_blobs > need_blobs)
+			packet_blobs = need_blobs;
+
+		for (i = 0; i < packet_blobs; i++) {
+			need_blobs--;
+			dev_dbg(sur40->dev, "processing blob\n");
+			report_blob(&(inblob[i]), input);
+		}
+
+	} while (need_blobs > 0);
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+}
+
+/*
+ * housekeeping routines
+*/
+
+/* initialize input device parameters */
+static void sur40_input_setup(struct input_dev *input_dev)
+{
+	set_bit(EV_KEY, input_dev->evbit);
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+		0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+		0, SENSOR_RES_Y, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_TOOL_X,
+		0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
+		0, SENSOR_RES_Y, 0, 0);
+
+	/* max value unknown, but major/minor axis
+	 * can never be larger than screen */
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+		0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+		0, SENSOR_RES_Y, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	input_mt_init_slots(input_dev, MAX_CONTACTS,
+		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+}
+
+/* clean up all allocated buffers/structs */
+static inline void sur40_delete(struct sur40_state *sur40)
+{
+	input_free_polled_device(sur40->input);
+	kfree(sur40->bulk_in_buffer);
+	kfree(sur40);
+}
+
+/* check candidate USB interface */
+static int sur40_probe(struct usb_interface *interface,
+	const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(interface);
+	struct sur40_state *sur40;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct input_polled_dev *poll_dev;
+	int result;
+
+	/* check if we really have the right interface */
+	iface_desc = &interface->altsetting[0];
+	if (iface_desc->desc.bInterfaceClass != 0xFF)
+		return -ENODEV;
+
+	/* use endpoint #4 (0x86) */
+	endpoint = &iface_desc->endpoint[4].desc;
+	if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
+		return -ENODEV;
+
+	/* allocate memory for our device state and initialize it */
+	sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
+	if (!sur40)
+		return -ENOMEM;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		result = -ENOMEM;
+		goto err_free_dev;
+	}
+
+	/* setup polled input device control struct */
+	poll_dev->private = sur40;
+	poll_dev->poll_interval = POLL_INTERVAL;
+	poll_dev->open = sur40_open;
+	poll_dev->poll = sur40_poll;
+	poll_dev->close = sur40_close;
+
+	/* setup regular input device struct */
+	sur40_input_setup(poll_dev->input);
+
+	poll_dev->input->name = "Samsung SUR40";
+	usb_to_input_id(usbdev, &(poll_dev->input->id));
+	usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
+	strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
+	poll_dev->input->phys = sur40->phys;
+
+	sur40->usbdev = usbdev;
+	sur40->dev = &usbdev->dev;
+	sur40->input = poll_dev;
+
+	/* use the bulk-in endpoint tested above */
+	sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
+	sur40->bulk_in_epaddr = endpoint->bEndpointAddress;
+	sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
+	if (!sur40->bulk_in_buffer) {
+		dev_err(&interface->dev, "Unable to allocate input buffer.");
+		result = -ENOMEM;
+		goto err_free_polldev;
+	}
+
+	result = input_register_polled_device(poll_dev);
+	if (result) {
+		dev_err(&interface->dev,
+			"Unable to register polled input device.");
+		goto err_free_buffer;
+	}
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, sur40);
+	dev_dbg(&interface->dev, "%s now attached\n", DRIVER_DESC);
+
+	return 0;
+
+err_free_buffer:
+	kfree(sur40->bulk_in_buffer);
+err_free_polldev:
+	input_free_polled_device(sur40->input);
+err_free_dev:
+	kfree(sur40);
+
+	return result;
+}
+
+/* unregister device & clean up */
+static void sur40_disconnect(struct usb_interface *interface)
+{
+	struct sur40_state *sur40 = usb_get_intfdata(interface);
+
+	input_unregister_polled_device(sur40->input);
+
+	usb_set_intfdata(interface, NULL);
+
+	sur40_delete(sur40);
+
+	dev_dbg(&interface->dev, "%s now disconnected\n", DRIVER_DESC);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver sur40_driver = {
+	.name = DRIVER_SHORT,
+	.probe = sur40_probe,
+	.disconnect = sur40_disconnect,
+	.id_table = sur40_table,
+};
+
+module_usb_driver(sur40_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-06 14:26 [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense) Florian Echtler
@ 2013-11-08 21:37 ` Henrik Rydberg
  2013-11-11  6:56 ` David Herrmann
  2013-11-11  7:31 ` Dmitry Torokhov
  2 siblings, 0 replies; 10+ messages in thread
From: Henrik Rydberg @ 2013-11-08 21:37 UTC (permalink / raw
  To: Florian Echtler
  Cc: linux-input, benjamin.tissoires, dmitry.torokhov, dh.herrmann

Hi Florian,

> This patch adds support for the built-in multitouch sensor in the Samsung
> SUR40 touchscreen device, also known as Microsoft Surface 2.0 or Microsoft
> Pixelsense. Support for raw video output from the sensor as well as the 
> accelerometer will be added in a later patch.
> 
> Signed-off-by: Florian Echtler <floe@butterbrot.org>
> ---
>  drivers/input/touchscreen/Kconfig  |   10 +
>  drivers/input/touchscreen/Makefile |    1 +
>  drivers/input/touchscreen/sur40.c  |  467 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 478 insertions(+)
>  create mode 100644 drivers/input/touchscreen/sur40.c

The driver looks pretty good now, thanks for making the changes. There
might be one or two white space problems, but those can be fixed while
applying. Dmitry, I guess you want to take this through your tree, so

Reviewed-by: Henrik Rydberg <rydberg@euromail.se>

Thanks,
Henrik

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-06 14:26 [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense) Florian Echtler
  2013-11-08 21:37 ` Henrik Rydberg
@ 2013-11-11  6:56 ` David Herrmann
  2013-11-11  7:31 ` Dmitry Torokhov
  2 siblings, 0 replies; 10+ messages in thread
From: David Herrmann @ 2013-11-11  6:56 UTC (permalink / raw
  To: Florian Echtler
  Cc: open list:HID CORE LAYER, Benjamin Tissoires, Henrik Rydberg,
	Dmitry Torokhov

Hi Florian

On Wed, Nov 6, 2013 at 3:26 PM, Florian Echtler <floe@butterbrot.org> wrote:
> From: "Florian Echtler" <floe@butterbrot.org>
>
> This patch adds support for the built-in multitouch sensor in the Samsung
> SUR40 touchscreen device, also known as Microsoft Surface 2.0 or Microsoft
> Pixelsense. Support for raw video output from the sensor as well as the
> accelerometer will be added in a later patch.
>
> Signed-off-by: Florian Echtler <floe@butterbrot.org>
> ---
>  drivers/input/touchscreen/Kconfig  |   10 +
>  drivers/input/touchscreen/Makefile |    1 +
>  drivers/input/touchscreen/sur40.c  |  467 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 478 insertions(+)
>  create mode 100644 drivers/input/touchscreen/sur40.c
>
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 515cfe7..99aaf10 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -876,6 +876,16 @@ config TOUCHSCREEN_STMPE
>           To compile this driver as a module, choose M here: the
>           module will be called stmpe-ts.
>
> +config TOUCHSCREEN_SUR40
> +       tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
> +       depends on USB
> +       help
> +         Say Y here if you want support for the Samsung SUR40 touchscreen
> +         (also known as Microsoft Surface 2.0 or Microsoft PixelSense).
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called sur40.
> +
>  config TOUCHSCREEN_TPS6507X
>         tristate "TPS6507x based touchscreens"
>         depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6bfbeab..b63a25d 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR)      += pixcir_i2c_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ST1232)       += st1232.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)                += stmpe-ts.o
> +obj-$(CONFIG_TOUCHSCREEN_SUR40)                += sur40.o
>  obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)        += ti_am335x_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_TNETV107X)    += tnetv107x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
> diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
> new file mode 100644
> index 0000000..48787fd
> --- /dev/null
> +++ b/drivers/input/touchscreen/sur40.c
> @@ -0,0 +1,467 @@
> +/*
> +       Surface2.0/SUR40/PixelSense input driver
> +
> +       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)
> +
> +       and from the Apple USB BCM5974 multitouch driver,
> +       Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
> +
> +       and from the generic hid-multitouch driver,
> +       Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
> +*/
> +
> +#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 <linux/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>
> +
> +/* read 512 bytes from endpoint 0x86 -> get header + blobs */
> +struct sur40_header {
> +
> +       __le16 type;       /* always 0x0001 */
> +       __le16 count;      /* count of blobs (if 0: continue prev. packet) */
> +
> +       __le32 packet_id;  /* unique ID for all packets in one frame */
> +
> +       __le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */
> +       __le32 unknown;    /* "epoch?" always 02/03 00 00 00 */
> +
> +} __packed;
> +
> +struct sur40_blob {
> +
> +       __le16 blob_id;
> +
> +       __u8 action;       /* 0x02 = enter/exit, 0x03 = update (?) */
> +       __u8 unknown;      /* always 0x01 or 0x02 (no idea what this is?) */
> +
> +       __le16 bb_pos_x;   /* upper left corner of bounding box */
> +       __le16 bb_pos_y;
> +
> +       __le16 bb_size_x;  /* size of bounding box */
> +       __le16 bb_size_y;
> +
> +       __le16 pos_x;      /* finger tip position */
> +       __le16 pos_y;
> +
> +       __le16 ctr_x;      /* centroid position */
> +       __le16 ctr_y;
> +
> +       __le16 axis_x;     /* somehow related to major/minor axis, mostly: */
> +       __le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */
> +
> +       __le32 angle;      /* orientation in radians relative to x axis -
> +                             actually an IEEE754 float, don't use in kernel */
> +
> +       __le32 area;       /* size in pixels/pressure (?) */
> +
> +       __u8 padding[32];
> +
> +} __packed;
> +
> +/* combined header/blob data */
> +struct sur40_data {
> +       struct sur40_header header;
> +       struct sur40_blob   blobs[];
> +} __packed;
> +
> +
> +/* version information */
> +#define DRIVER_SHORT   "sur40"
> +#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_SUR40     0x0775
> +
> +/* sensor resolution */
> +#define SENSOR_RES_X 1920
> +#define SENSOR_RES_Y 1080
> +
> +/* touch data endpoint */
> +#define TOUCH_ENDPOINT 0x86
> +
> +/* polling interval (ms) */
> +#define POLL_INTERVAL 10
> +
> +/* maximum number of contacts FIXME: this is a guess? */
> +#define MAX_CONTACTS 64
> +
> +/* device ID table */
> +static const struct usb_device_id sur40_table[] = {
> +       {USB_DEVICE(ID_MICROSOFT, ID_SUR40)},  /* Samsung SUR40 */
> +       {}                                     /* terminating null entry */
> +};
> +
> +/* control commands */
> +#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */
> +#define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */
> +#define SUR40_UNKNOWN2    0xc1 /* 24 bytes           */
> +
> +#define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
> +#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
> +
> +/*
> + * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
> + * here by mistake which is very likely to have corrupted the firmware EEPROM
> + * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
> + * Should you ever run into a similar problem, the background story to this
> + * incident and instructions on how to fix the corrupted EEPROM are available
> + * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
> +*/
> +#define sur40_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, sur40_table);
> +
> +/* structure to hold all of our device specific stuff */
> +struct sur40_state {
> +
> +       struct usb_device *usbdev;      /* save the usb device pointer */
> +       struct device *dev;             /* save the generic device pointer */
> +       struct input_polled_dev *input; /* struct for polled input device */
> +
> +       struct sur40_data *bulk_in_buffer; /* the buffer to receive data */
> +       size_t bulk_in_size;            /* the maximum bulk packet size */
> +       __u8 bulk_in_epaddr;            /* address of the bulk in endpoint */
> +
> +       char phys[64];                  /* buffer for phys name */
> +};
> +
> +/* initialization routine, called from sur40_open */
> +static int sur40_init(struct sur40_state *dev)
> +{
> +       int result;
> +       __u8 buffer[24];
> +
> +       /* stupidly replay the original MS driver init sequence */
> +       result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
> +       if (result < 0)
> +               return result;
> +
> +       result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);
> +       if (result < 0)
> +               return result;
> +
> +       result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);
> +       if (result < 0)
> +               return result;
> +
> +       result = sur40_command(dev, SUR40_UNKNOWN2,    0x00, buffer, 24);
> +       if (result < 0)
> +               return result;
> +
> +       result = sur40_command(dev, SUR40_UNKNOWN1,    0x00, buffer,  5);
> +       if (result < 0)
> +               return result;
> +
> +       result = sur40_command(dev, SUR40_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 sur40_open(struct input_polled_dev *polldev)
> +{
> +       struct sur40_state *sur40 = polldev->private;
> +       dev_dbg(sur40->dev, "open\n");
> +       sur40_init(sur40);

Just to be clear, sur40_open() may be called multiple times, and thus
same is true for sur40_init(). Did you test whether that works? If
not, you can drop the _open() and _close() callbacks and just call
sur40_init() in _probe(). If it works, the callbacks are definitely
better, obviously.

> +}
> +
> +/* disable device, polling has stopped */
> +void sur40_close(struct input_polled_dev *polldev)
> +{
> +       /* no known way to stop the device, except to stop polling */
> +       struct sur40_state *sur40 = polldev->private;
> +       dev_dbg(sur40->dev, "close\n");
> +}
> +
> +/*
> + * 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 sur40_blob *blob, struct input_dev *input)
> +{
> +       int wide, major, minor;
> +
> +       int bb_size_x = le16_to_cpu(blob->bb_size_x);
> +       int bb_size_y = le16_to_cpu(blob->bb_size_y);
> +
> +       int pos_x = le16_to_cpu(blob->pos_x);
> +       int pos_y = le16_to_cpu(blob->pos_y);
> +
> +       int ctr_x = le16_to_cpu(blob->ctr_x);
> +       int ctr_y = le16_to_cpu(blob->ctr_y);
> +
> +       int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
> +       if (slotnum < 0 || slotnum >= MAX_CONTACTS)
> +               return;
> +
> +       input_mt_slot(input, slotnum);
> +       input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
> +       wide = (bb_size_x > bb_size_y);
> +       major = max(bb_size_x, bb_size_y);
> +       minor = min(bb_size_x, bb_size_y);
> +
> +       input_event(input, EV_ABS, ABS_MT_POSITION_X, pos_x);
> +       input_event(input, EV_ABS, ABS_MT_POSITION_Y, pos_y);
> +       input_event(input, EV_ABS, ABS_MT_TOOL_X, ctr_x);
> +       input_event(input, EV_ABS, ABS_MT_TOOL_Y, ctr_y);
> +
> +       /* TODO: use a better orientation measure */
> +       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 sur40_poll(struct input_polled_dev *polldev)
> +{
> +
> +       struct sur40_state *sur40 = polldev->private;
> +       struct input_dev *input = polldev->input;
> +       int result, bulk_read, need_blobs, packet_blobs, i;
> +       uint32_t packet_id;
> +
> +       struct sur40_header *header = &sur40->bulk_in_buffer->header;
> +       struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
> +
> +       need_blobs = -1;
> +
> +       dev_dbg(sur40->dev, "poll\n");
> +
> +       do {
> +
> +               /* perform a blocking bulk read to get data from the device */
> +               result = usb_bulk_msg(sur40->usbdev,
> +                       usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),
> +                       sur40->bulk_in_buffer, sur40->bulk_in_size,
> +                       &bulk_read, 1000);
> +
> +               dev_dbg(sur40->dev, "received %d bytes\n", bulk_read);
> +
> +               if (result < 0) {
> +                       dev_err(sur40->dev, "error in usb_bulk_read\n");
> +                       return;
> +               }
> +
> +               result = bulk_read - sizeof(struct sur40_header);
> +
> +               if (result % sizeof(struct sur40_blob) != 0) {
> +                       dev_err(sur40->dev, "transfer size mismatch\n");
> +                       return;
> +               }
> +
> +               /* first packet? */
> +               if (need_blobs == -1) {
> +                       need_blobs = le16_to_cpu(header->count);
> +                       dev_dbg(sur40->dev, "need %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)
> +                       dev_warn(sur40->dev, "packet ID mismatch\n");
> +
> +               packet_blobs = result / sizeof(struct sur40_blob);
> +               dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
> +
> +               /* packets always contain at least 4 blobs, even if empty */
> +               if (packet_blobs > need_blobs)
> +                       packet_blobs = need_blobs;
> +
> +               for (i = 0; i < packet_blobs; i++) {
> +                       need_blobs--;
> +                       dev_dbg(sur40->dev, "processing blob\n");
> +                       report_blob(&(inblob[i]), input);
> +               }
> +
> +       } while (need_blobs > 0);
> +
> +       input_mt_sync_frame(input);
> +       input_sync(input);
> +}
> +
> +/*
> + * housekeeping routines
> +*/
> +
> +/* initialize input device parameters */
> +static void sur40_input_setup(struct input_dev *input_dev)
> +{
> +       set_bit(EV_KEY, input_dev->evbit);
> +       set_bit(EV_SYN, input_dev->evbit);
> +       set_bit(EV_ABS, input_dev->evbit);
> +
> +       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> +               0, SENSOR_RES_X, 0, 0);
> +       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> +               0, SENSOR_RES_Y, 0, 0);
> +
> +       input_set_abs_params(input_dev, ABS_MT_TOOL_X,
> +               0, SENSOR_RES_X, 0, 0);
> +       input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
> +               0, SENSOR_RES_Y, 0, 0);
> +
> +       /* max value unknown, but major/minor axis
> +        * can never be larger than screen */
> +       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
> +               0, SENSOR_RES_X, 0, 0);
> +       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
> +               0, SENSOR_RES_Y, 0, 0);
> +
> +       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
> +
> +       input_mt_init_slots(input_dev, MAX_CONTACTS,
> +               INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +}
> +
> +/* clean up all allocated buffers/structs */
> +static inline void sur40_delete(struct sur40_state *sur40)
> +{
> +       input_free_polled_device(sur40->input);
> +       kfree(sur40->bulk_in_buffer);
> +       kfree(sur40);
> +}
> +
> +/* check candidate USB interface */
> +static int sur40_probe(struct usb_interface *interface,
> +       const struct usb_device_id *id)
> +{
> +       struct usb_device *usbdev = interface_to_usbdev(interface);
> +       struct sur40_state *sur40;
> +       struct usb_host_interface *iface_desc;
> +       struct usb_endpoint_descriptor *endpoint;
> +       struct input_polled_dev *poll_dev;
> +       int result;
> +
> +       /* check if we really have the right interface */
> +       iface_desc = &interface->altsetting[0];
> +       if (iface_desc->desc.bInterfaceClass != 0xFF)
> +               return -ENODEV;
> +
> +       /* use endpoint #4 (0x86) */
> +       endpoint = &iface_desc->endpoint[4].desc;

Don't you have to test whether iface_desc->desc->bNumEndpoints is >4?
Or does usb allocate enough endpoint descriptors, anyway?

Other than that, the driver looks really good.
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>

Thanks
David

> +       if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
> +               return -ENODEV;
> +
> +       /* allocate memory for our device state and initialize it */
> +       sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
> +       if (!sur40)
> +               return -ENOMEM;
> +
> +       poll_dev = input_allocate_polled_device();
> +       if (!poll_dev) {
> +               result = -ENOMEM;
> +               goto err_free_dev;
> +       }
> +
> +       /* setup polled input device control struct */
> +       poll_dev->private = sur40;
> +       poll_dev->poll_interval = POLL_INTERVAL;
> +       poll_dev->open = sur40_open;
> +       poll_dev->poll = sur40_poll;
> +       poll_dev->close = sur40_close;
> +
> +       /* setup regular input device struct */
> +       sur40_input_setup(poll_dev->input);
> +
> +       poll_dev->input->name = "Samsung SUR40";
> +       usb_to_input_id(usbdev, &(poll_dev->input->id));
> +       usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
> +       strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
> +       poll_dev->input->phys = sur40->phys;
> +
> +       sur40->usbdev = usbdev;
> +       sur40->dev = &usbdev->dev;
> +       sur40->input = poll_dev;
> +
> +       /* use the bulk-in endpoint tested above */
> +       sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
> +       sur40->bulk_in_epaddr = endpoint->bEndpointAddress;
> +       sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
> +       if (!sur40->bulk_in_buffer) {
> +               dev_err(&interface->dev, "Unable to allocate input buffer.");
> +               result = -ENOMEM;
> +               goto err_free_polldev;
> +       }
> +
> +       result = input_register_polled_device(poll_dev);
> +       if (result) {
> +               dev_err(&interface->dev,
> +                       "Unable to register polled input device.");
> +               goto err_free_buffer;
> +       }
> +
> +       /* we can register the device now, as it is ready */
> +       usb_set_intfdata(interface, sur40);
> +       dev_dbg(&interface->dev, "%s now attached\n", DRIVER_DESC);
> +
> +       return 0;
> +
> +err_free_buffer:
> +       kfree(sur40->bulk_in_buffer);
> +err_free_polldev:
> +       input_free_polled_device(sur40->input);
> +err_free_dev:
> +       kfree(sur40);
> +
> +       return result;
> +}
> +
> +/* unregister device & clean up */
> +static void sur40_disconnect(struct usb_interface *interface)
> +{
> +       struct sur40_state *sur40 = usb_get_intfdata(interface);
> +
> +       input_unregister_polled_device(sur40->input);
> +
> +       usb_set_intfdata(interface, NULL);
> +
> +       sur40_delete(sur40);
> +
> +       dev_dbg(&interface->dev, "%s now disconnected\n", DRIVER_DESC);
> +}
> +
> +/* usb specific object needed to register this driver with the usb subsystem */
> +static struct usb_driver sur40_driver = {
> +       .name = DRIVER_SHORT,
> +       .probe = sur40_probe,
> +       .disconnect = sur40_disconnect,
> +       .id_table = sur40_table,
> +};
> +
> +module_usb_driver(sur40_driver);
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");
> --
> 1.7.9.5
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-06 14:26 [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense) Florian Echtler
  2013-11-08 21:37 ` Henrik Rydberg
  2013-11-11  6:56 ` David Herrmann
@ 2013-11-11  7:31 ` Dmitry Torokhov
  2013-11-11 10:08   ` Florian Echtler
  2 siblings, 1 reply; 10+ messages in thread
From: Dmitry Torokhov @ 2013-11-11  7:31 UTC (permalink / raw
  To: Florian Echtler; +Cc: linux-input, benjamin.tissoires, rydberg, dh.herrmann

Hi Florian,

On Wed, Nov 06, 2013 at 03:26:51PM +0100, Florian Echtler wrote:
> +
> +	/* max value unknown, but major/minor axis
> +	 * can never be larger than screen */
> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
> +		0, SENSOR_RES_X, 0, 0);
> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
> +		0, SENSOR_RES_Y, 0, 0);

If the range is unknown do we really want to specify min/max?

Also, does the patch below mess up or device or it still works?

Thanks.

-- 
Dmitry

Input: sur40 - misc changes

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/touchscreen/sur40.c |  227 ++++++++++++++++++-------------------
 1 file changed, 113 insertions(+), 114 deletions(-)

diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 48787fd..cfd1b7e 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -1,22 +1,22 @@
 /*
-	Surface2.0/SUR40/PixelSense input driver
-
-	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)
-
-	and from the Apple USB BCM5974 multitouch driver,
-	Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
-
-	and from the generic hid-multitouch driver,
-	Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
-*/
+ * Surface2.0/SUR40/PixelSense input driver
+ *
+ * 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)
+ *
+ * and from the Apple USB BCM5974 multitouch driver,
+ * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
+ *
+ * and from the generic hid-multitouch driver,
+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ *
+ * 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.
+ */
 
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -49,8 +49,8 @@ struct sur40_blob {
 
 	__le16 blob_id;
 
-	__u8 action;       /* 0x02 = enter/exit, 0x03 = update (?) */
-	__u8 unknown;      /* always 0x01 or 0x02 (no idea what this is?) */
+	u8 action;         /* 0x02 = enter/exit, 0x03 = update (?) */
+	u8 unknown;        /* always 0x01 or 0x02 (no idea what this is?) */
 
 	__le16 bb_pos_x;   /* upper left corner of bounding box */
 	__le16 bb_pos_y;
@@ -72,7 +72,7 @@ struct sur40_blob {
 
 	__le32 area;       /* size in pixels/pressure (?) */
 
-	__u8 padding[32];
+	u8 padding[32];
 
 } __packed;
 
@@ -105,12 +105,6 @@ struct sur40_data {
 /* maximum number of contacts FIXME: this is a guess? */
 #define MAX_CONTACTS 64
 
-/* device ID table */
-static const struct usb_device_id sur40_table[] = {
-	{USB_DEVICE(ID_MICROSOFT, ID_SUR40)},  /* Samsung SUR40 */
-	{}                                     /* terminating null entry */
-};
-
 /* control commands */
 #define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */
 #define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */
@@ -127,32 +121,34 @@ static const struct usb_device_id sur40_table[] = {
  * incident and instructions on how to fix the corrupted EEPROM are available
  * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
 */
-#define sur40_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, sur40_table);
 
-/* structure to hold all of our device specific stuff */
 struct sur40_state {
 
-	struct usb_device *usbdev;      /* save the usb device pointer */
-	struct device *dev;             /* save the generic device pointer */
-	struct input_polled_dev *input; /* struct for polled input device */
+	struct usb_device *usbdev;
+	struct device *dev;
+	struct input_polled_dev *input;
 
-	struct sur40_data *bulk_in_buffer; /* the buffer to receive data */
-	size_t bulk_in_size;            /* the maximum bulk packet size */
-	__u8 bulk_in_epaddr;            /* address of the bulk in endpoint */
+	struct sur40_data *bulk_in_buffer;
+	size_t bulk_in_size;
+	u8 bulk_in_epaddr;
 
-	char phys[64];                  /* buffer for phys name */
+	char phys[64];
 };
 
-/* initialization routine, called from sur40_open */
+static int sur40_command(struct sur40_state *dev,
+			 u8 command, u16 index, void *buffer, u16 size)
+{
+	return 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);
+}
+
+/* Initialization routine, called from sur40_open */
 static int sur40_init(struct sur40_state *dev)
 {
 	int result;
-	__u8 buffer[24];
+	u8 buffer[24];
 
 	/* stupidly replay the original MS driver init sequence */
 	result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
@@ -177,37 +173,44 @@ static int sur40_init(struct sur40_state *dev)
 
 	result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);
 
-	/* discard the result buffer - no known data inside except
-	   some version strings, maybe extract these sometime..    */
+	/*
+	 * Discard the result buffer - no known data inside except
+	 * some version strings, maybe extract these sometime...
+	 */
 
 	return result;
 }
 
 /*
- * callback routines from input_polled_dev
-*/
+ * Callback routines from input_polled_dev
+ */
 
-/* enable the device, polling will now start */
-void sur40_open(struct input_polled_dev *polldev)
+/* Enable the device, polling will now start. */
+static void sur40_open(struct input_polled_dev *polldev)
 {
 	struct sur40_state *sur40 = polldev->private;
+
 	dev_dbg(sur40->dev, "open\n");
 	sur40_init(sur40);
 }
 
-/* disable device, polling has stopped */
-void sur40_close(struct input_polled_dev *polldev)
+/* Disable device, polling has stopped. */
+static void sur40_close(struct input_polled_dev *polldev)
 {
-	/* no known way to stop the device, except to stop polling */
 	struct sur40_state *sur40 = polldev->private;
+
 	dev_dbg(sur40->dev, "close\n");
+	/*
+	 * There is no known way to stop the device, so we simply
+	 * stop polling.
+	 */
 }
 
 /*
- * 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
+ * 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 sur40_blob *blob, struct input_dev *input)
+static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
 {
 	int wide, major, minor;
 
@@ -230,33 +233,33 @@ static void report_blob(struct sur40_blob *blob, struct input_dev *input)
 	major = max(bb_size_x, bb_size_y);
 	minor = min(bb_size_x, bb_size_y);
 
-	input_event(input, EV_ABS, ABS_MT_POSITION_X, pos_x);
-	input_event(input, EV_ABS, ABS_MT_POSITION_Y, pos_y);
-	input_event(input, EV_ABS, ABS_MT_TOOL_X, ctr_x);
-	input_event(input, EV_ABS, ABS_MT_TOOL_Y, ctr_y);
+	input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+	input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+	input_report_abs(input, ABS_MT_TOOL_X, ctr_x);
+	input_report_abs(input, ABS_MT_TOOL_Y, ctr_y);
 
 	/* TODO: use a better orientation measure */
-	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);
+	input_report_abs(input, ABS_MT_ORIENTATION, wide);
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+	input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
 }
 
 /* core function: poll for new input data */
-void sur40_poll(struct input_polled_dev *polldev)
+static void sur40_poll(struct input_polled_dev *polldev)
 {
 
 	struct sur40_state *sur40 = polldev->private;
 	struct input_dev *input = polldev->input;
 	int result, bulk_read, need_blobs, packet_blobs, i;
-	uint32_t packet_id;
+	u32 packet_id;
 
 	struct sur40_header *header = &sur40->bulk_in_buffer->header;
 	struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
 
-	need_blobs = -1;
-
 	dev_dbg(sur40->dev, "poll\n");
 
+	need_blobs = -1;
+
 	do {
 
 		/* perform a blocking bulk read to get data from the device */
@@ -286,9 +289,11 @@ void sur40_poll(struct input_polled_dev *polldev)
 			packet_id = header->packet_id;
 		}
 
-		/* sanity check. when video data is also being retrieved, the
+		/*
+		 * 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. */
+		 * instead of at the end.
+		 */
 		if (packet_id != header->packet_id)
 			dev_warn(sur40->dev, "packet ID mismatch\n");
 
@@ -302,7 +307,7 @@ void sur40_poll(struct input_polled_dev *polldev)
 		for (i = 0; i < packet_blobs; i++) {
 			need_blobs--;
 			dev_dbg(sur40->dev, "processing blob\n");
-			report_blob(&(inblob[i]), input);
+			sur40_report_blob(&(inblob[i]), input);
 		}
 
 	} while (need_blobs > 0);
@@ -311,98 +316,86 @@ void sur40_poll(struct input_polled_dev *polldev)
 	input_sync(input);
 }
 
-/*
- * housekeeping routines
-*/
-
-/* initialize input device parameters */
+/* Initialize input device parameters. */
 static void sur40_input_setup(struct input_dev *input_dev)
 {
-	set_bit(EV_KEY, input_dev->evbit);
-	set_bit(EV_SYN, input_dev->evbit);
-	set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
 
 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-		0, SENSOR_RES_X, 0, 0);
+			     0, SENSOR_RES_X, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-		0, SENSOR_RES_Y, 0, 0);
+			     0, SENSOR_RES_Y, 0, 0);
 
 	input_set_abs_params(input_dev, ABS_MT_TOOL_X,
-		0, SENSOR_RES_X, 0, 0);
+			     0, SENSOR_RES_X, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
-		0, SENSOR_RES_Y, 0, 0);
+			     0, SENSOR_RES_Y, 0, 0);
 
 	/* max value unknown, but major/minor axis
 	 * can never be larger than screen */
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-		0, SENSOR_RES_X, 0, 0);
+			     0, SENSOR_RES_X, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
-		0, SENSOR_RES_Y, 0, 0);
+			     0, SENSOR_RES_Y, 0, 0);
 
 	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
 
 	input_mt_init_slots(input_dev, MAX_CONTACTS,
-		INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 }
 
-/* clean up all allocated buffers/structs */
-static inline void sur40_delete(struct sur40_state *sur40)
-{
-	input_free_polled_device(sur40->input);
-	kfree(sur40->bulk_in_buffer);
-	kfree(sur40);
-}
-
-/* check candidate USB interface */
+/* Check candidate USB interface. */
 static int sur40_probe(struct usb_interface *interface,
-	const struct usb_device_id *id)
+		       const struct usb_device_id *id)
 {
 	struct usb_device *usbdev = interface_to_usbdev(interface);
 	struct sur40_state *sur40;
 	struct usb_host_interface *iface_desc;
 	struct usb_endpoint_descriptor *endpoint;
 	struct input_polled_dev *poll_dev;
-	int result;
+	int error;
 
-	/* check if we really have the right interface */
+	/* Check if we really have the right interface. */
 	iface_desc = &interface->altsetting[0];
 	if (iface_desc->desc.bInterfaceClass != 0xFF)
 		return -ENODEV;
 
-	/* use endpoint #4 (0x86) */
+	/* Use endpoint #4 (0x86). */
 	endpoint = &iface_desc->endpoint[4].desc;
 	if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
 		return -ENODEV;
 
-	/* allocate memory for our device state and initialize it */
+	/* Allocate memory for our device state and initialize it. */
 	sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
 	if (!sur40)
 		return -ENOMEM;
 
 	poll_dev = input_allocate_polled_device();
 	if (!poll_dev) {
-		result = -ENOMEM;
+		error = -ENOMEM;
 		goto err_free_dev;
 	}
 
-	/* setup polled input device control struct */
+	/* Set up polled input device control structure */
 	poll_dev->private = sur40;
 	poll_dev->poll_interval = POLL_INTERVAL;
 	poll_dev->open = sur40_open;
 	poll_dev->poll = sur40_poll;
 	poll_dev->close = sur40_close;
 
-	/* setup regular input device struct */
+	/* Set up regular input device structure */
 	sur40_input_setup(poll_dev->input);
 
 	poll_dev->input->name = "Samsung SUR40";
-	usb_to_input_id(usbdev, &(poll_dev->input->id));
+	usb_to_input_id(usbdev, &poll_dev->input->id);
 	usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
 	strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
 	poll_dev->input->phys = sur40->phys;
+	poll_dev->input->dev.parent = &interface->dev;
 
 	sur40->usbdev = usbdev;
-	sur40->dev = &usbdev->dev;
+	sur40->dev = &interface->dev;
 	sur40->input = poll_dev;
 
 	/* use the bulk-in endpoint tested above */
@@ -411,12 +404,12 @@ static int sur40_probe(struct usb_interface *interface,
 	sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
 	if (!sur40->bulk_in_buffer) {
 		dev_err(&interface->dev, "Unable to allocate input buffer.");
-		result = -ENOMEM;
+		error = -ENOMEM;
 		goto err_free_polldev;
 	}
 
-	result = input_register_polled_device(poll_dev);
-	if (result) {
+	error = input_register_polled_device(poll_dev);
+	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register polled input device.");
 		goto err_free_buffer;
@@ -424,7 +417,7 @@ static int sur40_probe(struct usb_interface *interface,
 
 	/* we can register the device now, as it is ready */
 	usb_set_intfdata(interface, sur40);
-	dev_dbg(&interface->dev, "%s now attached\n", DRIVER_DESC);
+	dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
 
 	return 0;
 
@@ -435,24 +428,30 @@ err_free_polldev:
 err_free_dev:
 	kfree(sur40);
 
-	return result;
+	return error;
 }
 
-/* unregister device & clean up */
+/* Unregister device & clean up. */
 static void sur40_disconnect(struct usb_interface *interface)
 {
 	struct sur40_state *sur40 = usb_get_intfdata(interface);
 
 	input_unregister_polled_device(sur40->input);
+	input_free_polled_device(sur40->input);
+	kfree(sur40->bulk_in_buffer);
+	kfree(sur40);
 
 	usb_set_intfdata(interface, NULL);
-
-	sur40_delete(sur40);
-
-	dev_dbg(&interface->dev, "%s now disconnected\n", DRIVER_DESC);
+	dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
 }
 
-/* usb specific object needed to register this driver with the usb subsystem */
+static const struct usb_device_id sur40_table[] = {
+	{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) },  /* Samsung SUR40 */
+	{ }                                      /* terminating null entry */
+};
+MODULE_DEVICE_TABLE(usb, sur40_table);
+
+/* USB-specific object needed to register this driver with the USB subsystem. */
 static struct usb_driver sur40_driver = {
 	.name = DRIVER_SHORT,
 	.probe = sur40_probe,

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-11  7:31 ` Dmitry Torokhov
@ 2013-11-11 10:08   ` Florian Echtler
  2013-11-11 10:27     ` Henrik Rydberg
  0 siblings, 1 reply; 10+ messages in thread
From: Florian Echtler @ 2013-11-11 10:08 UTC (permalink / raw
  To: Dmitry Torokhov; +Cc: linux-input, benjamin.tissoires, rydberg, dh.herrmann

[-- Attachment #1: Type: text/plain, Size: 969 bytes --]

Hello Dmitry,

On 11.11.2013 08:31, Dmitry Torokhov wrote:
> Hi Florian,
> 
> On Wed, Nov 06, 2013 at 03:26:51PM +0100, Florian Echtler wrote:
>> +
>> +	/* max value unknown, but major/minor axis
>> +	 * can never be larger than screen */
>> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
>> +		0, SENSOR_RES_X, 0, 0);
>> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
>> +		0, SENSOR_RES_Y, 0, 0);
> 
> If the range is unknown do we really want to specify min/max?
Geometrically speaking, these _are_ the min/max values. I can't say for
sure if the device has a cutoff threshold somewhere, but in terms of
geometric interpretation, I'd say this is correct.

> Also, does the patch below mess up or device or it still works?
I'll test this tomorrow (no access to SUR40 today). If everything works,
should I resubmit my patch with yours included or just tell you that
it's fine?

BR, Florian
-- 
SENT FROM MY DEC VT50 TERMINAL


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-11 10:08   ` Florian Echtler
@ 2013-11-11 10:27     ` Henrik Rydberg
  2013-11-12 13:35       ` Florian Echtler
  0 siblings, 1 reply; 10+ messages in thread
From: Henrik Rydberg @ 2013-11-11 10:27 UTC (permalink / raw
  To: Florian Echtler, Dmitry Torokhov
  Cc: linux-input, benjamin.tissoires, dh.herrmann

 >> Also, does the patch below mess up or device or it still works?
> I'll test this tomorrow (no access to SUR40 today). If everything works,
> should I resubmit my patch with yours included or just tell you that
> it's fine?

No need to resubmit, a confirmation will do nicely. Thanks!

Henrik


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-11 10:27     ` Henrik Rydberg
@ 2013-11-12 13:35       ` Florian Echtler
  2013-11-16 11:45         ` Florian Echtler
  0 siblings, 1 reply; 10+ messages in thread
From: Florian Echtler @ 2013-11-12 13:35 UTC (permalink / raw
  To: Henrik Rydberg, Dmitry Torokhov
  Cc: linux-input, benjamin.tissoires, dh.herrmann

[-- Attachment #1: Type: text/plain, Size: 542 bytes --]

On 11.11.2013 11:27, Henrik Rydberg wrote:
>  >> Also, does the patch below mess up or device or it still works?
>> I'll test this tomorrow (no access to SUR40 today). If everything works,
>> should I resubmit my patch with yours included or just tell you that
>> it's fine?
> No need to resubmit, a confirmation will do nicely. Thanks!
> Henrik

I've tested Dmitry's additional patch, confirmed to work. Just for the
record:

Tested-by: Florian Echtler <floe@butterbrot.org>

BR, Florian
-- 
SENT FROM MY DEC VT50 TERMINAL


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-12 13:35       ` Florian Echtler
@ 2013-11-16 11:45         ` Florian Echtler
  2013-11-16 18:08           ` Henrik Rydberg
  0 siblings, 1 reply; 10+ messages in thread
From: Florian Echtler @ 2013-11-16 11:45 UTC (permalink / raw
  To: Henrik Rydberg, Dmitry Torokhov
  Cc: linux-input, benjamin.tissoires, dh.herrmann

Looks like the sur40 driver didn't make it into the 3.13 pull request, 
anything still missing/broken? I was hoping this might still make it 
into Ubuntu 14.04...

BR, Florian

Florian Echtler <floe@butterbrot.org> wrote:
>On 11.11.2013 11:27, Henrik Rydberg wrote:
>>  >> Also, does the patch below mess up or device or it still works?
>>> I'll test this tomorrow (no access to SUR40 today). If everything
>works,
>>> should I resubmit my patch with yours included or just tell you that
>>> it's fine?
>> No need to resubmit, a confirmation will do nicely. Thanks!
>> Henrik
>
>I've tested Dmitry's additional patch, confirmed to work. Just for the
>record:
>
>Tested-by: Florian Echtler <floe@butterbrot.org>
>
>BR, Florian

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-16 11:45         ` Florian Echtler
@ 2013-11-16 18:08           ` Henrik Rydberg
  2013-11-18 14:50             ` Florian Echtler
  0 siblings, 1 reply; 10+ messages in thread
From: Henrik Rydberg @ 2013-11-16 18:08 UTC (permalink / raw
  To: Florian Echtler
  Cc: Dmitry Torokhov, linux-input, benjamin.tissoires, dh.herrmann

Hi Florian,

> Looks like the sur40 driver didn't make it into the 3.13 pull request,
> anything still missing/broken? I was hoping this might still make it into
> Ubuntu 14.04...

Nothing is broken, don't worry. If you want to make sure to hit a
certain merge window, then everything should be set and applied
several weeks before that, to allow for automatic testing and
administration. It could be that Dmitry is stacking up for a second
pull request in a couple of weeks, but I would not count on it. If you
want Ubuntu to pick your patches up, you can always try to send them
to kernel-team@lists.ubuntu.com; they might be willing to carry your
patches to the next kernel release. It helps to refer to the
appropriate commits in linux-next.

Thanks,
Henrik

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
  2013-11-16 18:08           ` Henrik Rydberg
@ 2013-11-18 14:50             ` Florian Echtler
  0 siblings, 0 replies; 10+ messages in thread
From: Florian Echtler @ 2013-11-18 14:50 UTC (permalink / raw
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, linux-input, benjamin.tissoires, dh.herrmann

[-- Attachment #1: Type: text/plain, Size: 1150 bytes --]

Hello Henrik,

thanks for the info, sorry for me being such a nuisance :-)

I'll submit the patch to the Ubuntu kernel team once it's merged into
Dmitry's tree, would be great to see this in the next LTS version.

Best regards, Florian

On 16.11.2013 19:08, Henrik Rydberg wrote:
> Hi Florian,
> 
>> Looks like the sur40 driver didn't make it into the 3.13 pull request,
>> anything still missing/broken? I was hoping this might still make it into
>> Ubuntu 14.04...
> 
> Nothing is broken, don't worry. If you want to make sure to hit a
> certain merge window, then everything should be set and applied
> several weeks before that, to allow for automatic testing and
> administration. It could be that Dmitry is stacking up for a second
> pull request in a couple of weeks, but I would not count on it. If you
> want Ubuntu to pick your patches up, you can always try to send them
> to kernel-team@lists.ubuntu.com; they might be willing to carry your
> patches to the next kernel release. It helps to refer to the
> appropriate commits in linux-next.
> 
> Thanks,
> Henrik
> 


-- 
SENT FROM MY DEC VT50 TERMINAL


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-11-18 14:50 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-06 14:26 [PATCH v3] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense) Florian Echtler
2013-11-08 21:37 ` Henrik Rydberg
2013-11-11  6:56 ` David Herrmann
2013-11-11  7:31 ` Dmitry Torokhov
2013-11-11 10:08   ` Florian Echtler
2013-11-11 10:27     ` Henrik Rydberg
2013-11-12 13:35       ` Florian Echtler
2013-11-16 11:45         ` Florian Echtler
2013-11-16 18:08           ` Henrik Rydberg
2013-11-18 14:50             ` Florian Echtler

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.