linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Support for 3M multitouch panel
@ 2009-12-11 10:12 Stephane Chatty
  2009-12-11 12:42 ` Petr Štetiar
  0 siblings, 1 reply; 13+ messages in thread
From: Stephane Chatty @ 2009-12-11 10:12 UTC (permalink / raw)
  To: linux-input; +Cc: dsuljic

Added support for 3M multitouch panel.

Signed-off-by: Stephane Chatty <chatty@enac.fr>


diff -rupN a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c
--- a/drivers/hid/hid-3m-pct.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/hid/hid-3m-pct.c	2009-12-11 10:58:01.000000000 +0100
@@ -0,0 +1,292 @@
+/*
+ *  HID driver for 3M multitouch panel
+ *
+ *  Copyright (c) 2009 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+MODULE_VERSION("0.2");
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("3M PCT multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+static int emulate_touchscreen = 1;
+module_param_named(emulate_touchscreen, emulate_touchscreen, int, 0600);
+MODULE_PARM_DESC(emulate_touchscreen,
+		"Touchscreen emulation on 3M multitouch panel (0=off, 1=on)");
+
+struct mmm_finger {
+	__s32 x, y;
+	__u8 rank;
+	int touch:1, valid:1;
+};
+struct mmm_data {
+	struct mmm_finger f[10];
+	__u8 curid, num;
+	int touch:1, valid:1;
+};
+
+static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	switch (usage->hid & HID_USAGE_PAGE) {
+
+	case HID_UP_BUTTON:
+		return -1;
+
+	case HID_UP_GENDESK:
+		switch (usage->hid) {
+		case HID_GD_X:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_X);
+			if (emulate_touchscreen)
+				input_set_abs_params(hi->input, ABS_X,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		case HID_GD_Y:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_Y);
+			if (emulate_touchscreen)
+				input_set_abs_params(hi->input, ABS_Y,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		}
+		return 0;
+
+	case HID_UP_DIGITIZER:
+		switch (usage->hid) {
+		/* we do not want to map these: no input-oriented meaning */
+		case 0x14:
+		case 0x23:
+		case HID_DG_INPUTMODE:
+		case HID_DG_DEVICEINDEX:
+		case HID_DG_CONTACTCOUNT:
+		case HID_DG_CONTACTMAX:
+		case HID_DG_INRANGE:
+		case HID_DG_CONFIDENCE:
+			return -1;
+		case HID_DG_TIPSWITCH:
+			if (emulate_touchscreen) {
+				hid_map_usage(hi, usage, bit, max,
+					EV_KEY, BTN_TOUCH);
+				return 1;
+			} else
+				return -1;
+		case HID_DG_CONTACTID:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_TRACKING_ID);
+			return 1;
+		}
+		/* let hid-input decide for the others */
+		return 0;
+
+	case 0xff000000:
+		/* we do not want to map these: no input-oriented meaning */
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if (usage->type == EV_KEY || usage->type == EV_ABS)
+		clear_bit(usage->code, *bit);
+
+	return 0;
+}
+
+/*
+ * this function is called when a whole packet has been received and process,
+ * so that it can decide what to send to the input layer.
+ */
+static void mmm_filter_event (struct mmm_data *md, struct input_dev *input)
+{
+	struct mmm_finger *oldest = 0;
+	int pressed = 0, released = 0;
+	int i;
+	for (i = 0; i < 10; ++i) {
+		struct mmm_finger *f = &md->f[i];
+		if (!f->valid) {
+			/* ignore placeholder data */
+		} else if (f->touch) {
+			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
+			input_mt_sync(input);
+			/*
+			 * maintain age rank of this finger for our single
+			 * touch emulation.
+			 */
+			if (f->rank == 0) {
+				f->rank = ++(md->num);
+				if (f->rank == 1)
+					pressed = 1;
+			}
+			if (f->rank == 1)
+				oldest = f;
+		} else {
+			/*
+			 * maintain age rank of other fingers for our single
+			 * touch emulation.
+			 */
+			int j;
+			for (j = 0; j < 10; ++j) {
+				struct mmm_finger *g = &md->f[j];
+				if (g->rank > f->rank) {
+					g->rank--;
+					if (g->rank == 1)
+						oldest = g;
+				}
+			}
+			f->rank = 0;
+			--(md->num);
+			if (md->num == 0)
+				released = 1;
+		}
+		f->valid = 0;
+	}
+
+	if (emulate_touchscreen) {
+		if (oldest) {
+			if (pressed)
+				input_event(input, EV_KEY, BTN_TOUCH, 1);
+			input_event(input, EV_ABS, ABS_X, oldest->x);
+			input_event(input, EV_ABS, ABS_Y, oldest->y);
+		} else if (released) {
+			input_event(input, EV_KEY, BTN_TOUCH, 0);
+		}
+	}
+}
+
+/*
+ * this function is called upon all reports
+ * so that we can accumulate contact point information,
+ * and call input_mt_sync after each point.
+ */
+static int mmm_event (struct hid_device *hid, struct hid_field *field,
+		                        struct hid_usage *usage, __s32 value)
+{
+	struct mmm_data *md = hid_get_drvdata(hid);
+        /* Strangely, this function can be called before
+	 * field->hidinput is initialized! */
+
+        if (hid->claimed & HID_CLAIMED_INPUT) {
+		struct input_dev *input = field->hidinput->input;
+		switch (usage->hid) {
+		case HID_DG_TIPSWITCH:
+			md->touch = value;
+			break;
+		case HID_DG_CONFIDENCE:
+			md->valid = value;
+			break;
+		case HID_DG_CONTACTID:
+			if (md->valid) {
+				md->curid = value;
+				md->f[value].touch = md->touch;
+				md->f[value].valid = 1;
+			}
+			break;
+		case HID_GD_X:
+			if (md->valid)
+				md->f[md->curid].x = value;
+			break;
+		case HID_GD_Y:
+			if (md->valid)
+				md->f[md->curid].y = value;
+			break;
+		case HID_DG_CONTACTCOUNT:
+			mmm_filter_event (md, input);
+			break;
+		}
+	}
+
+	/* we have handled the hidinput part, now remains hiddev */
+        if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+                hid->hiddev_hid_event(hid, field, usage, value);
+
+	return 1;
+}
+
+static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+	struct mmm_data *md;
+
+	md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
+	if (!md) {
+		dev_err(&hdev->dev, "cannot allocate 3M data\n");
+		return -ENOMEM;
+	}
+	hid_set_drvdata(hdev, md);
+
+	ret = hid_parse(hdev);
+	if (!ret)
+		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+	if (ret)
+		kfree (md);
+	return ret;
+}
+
+static void mmm_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id mmm_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, mmm_devices);
+
+static const struct hid_usage_id mmm_grabbed_usages[] = {
+	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mmm_driver = {
+	.name = "3m-pct",
+	.id_table = mmm_devices,
+	.probe = mmm_probe,
+	.remove = mmm_remove,
+	.input_mapping = mmm_input_mapping,
+	.input_mapped = mmm_input_mapped,
+	.usage_table = mmm_grabbed_usages,
+	.event = mmm_event,
+};
+
+static int mmm_init(void)
+{
+	return hid_register_driver(&mmm_driver);
+}
+
+static void mmm_exit(void)
+{
+	hid_unregister_driver(&mmm_driver);
+}
+
+module_init(mmm_init);
+module_exit(mmm_exit);
+MODULE_LICENSE("GPL");
+
diff -rupN a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
--- a/drivers/hid/hid-core.c	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-core.c	2009-12-11 10:57:43.000000000 +0100
@@ -1250,6 +1250,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
 
 /* a list of devices for which there is a specialized driver on HID bus */
 static const struct hid_device_id hid_blacklist[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
diff -rupN a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
--- a/drivers/hid/hid-ids.h	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-ids.h	2009-12-11 10:56:54.000000000 +0100
@@ -18,6 +18,9 @@
 #ifndef HID_IDS_H_FILE
 #define HID_IDS_H_FILE
 
+#define USB_VENDOR_ID_3M		0x0596
+#define USB_DEVICE_ID_3M1968		0x0500
+
 #define USB_VENDOR_ID_A4TECH		0x09da
 #define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
 #define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
diff -rupN a/drivers/hid/Kconfig b/drivers/hid/Kconfig
--- a/drivers/hid/Kconfig	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Kconfig	2009-12-11 10:59:07.000000000 +0100
@@ -55,6 +55,13 @@ source "drivers/hid/usbhid/Kconfig"
 menu "Special HID drivers"
 	depends on HID
 
+config HID_3M_PCT
+	tristate "3M PCT" if EMBEDDED
+	depends on USB_HID
+	default !EMBEDDED
+	---help---
+	Support for 3M PCT touch screens.
+
 config HID_A4TECH
 	tristate "A4 tech" if EMBEDDED
 	depends on USB_HID
diff -rupN a/drivers/hid/Makefile b/drivers/hid/Makefile
--- a/drivers/hid/Makefile	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Makefile	2009-12-11 10:59:29.000000000 +0100
@@ -19,6 +19,7 @@ ifdef CONFIG_LOGIRUMBLEPAD2_FF
 	hid-logitech-objs	+= hid-lg2ff.o
 endif
 
+obj-$(CONFIG_HID_3M_PCT)	+= hid-3m-pct.o
 obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
 obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-11 10:12 [PATCH] Support for 3M multitouch panel Stephane Chatty
@ 2009-12-11 12:42 ` Petr Štetiar
  2009-12-11 14:10   ` Fwd: " Stephane Chatty
  0 siblings, 1 reply; 13+ messages in thread
From: Petr Štetiar @ 2009-12-11 12:42 UTC (permalink / raw)
  To: Stephane Chatty; +Cc: linux-input, dsuljic

Stephane Chatty <chatty@lii-enac.fr> [2009-12-11 11:12:58]:

> Added support for 3M multitouch panel.

Hi,

you maybe forget to run checkpatch on it :-)

WARNING: space prohibited between function name and open parenthesis '('
#131: FILE: drivers/hid/hid-3m-pct.c:122:
+static void mmm_filter_event (struct mmm_data *md, struct input_dev *input)

WARNING: space prohibited between function name and open parenthesis '('
#195: FILE: drivers/hid/hid-3m-pct.c:186:
+static int mmm_event (struct hid_device *hid, struct hid_field *field,

ERROR: code indent should use tabs where possible
#196: FILE: drivers/hid/hid-3m-pct.c:187:
+^I^I                        struct hid_usage *usage, __s32 value)$

ERROR: code indent should use tabs where possible
#199: FILE: drivers/hid/hid-3m-pct.c:190:
+        /* Strangely, this function can be called before$

ERROR: code indent should use tabs where possible
#202: FILE: drivers/hid/hid-3m-pct.c:193:
+        if (hid->claimed & HID_CLAIMED_INPUT) {$

WARNING: space prohibited between function name and open parenthesis '('
#227: FILE: drivers/hid/hid-3m-pct.c:218:
+                       mmm_filter_event (md, input);

ERROR: code indent should use tabs where possible
#233: FILE: drivers/hid/hid-3m-pct.c:224:
+        if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)$

ERROR: code indent should use tabs where possible
#234: FILE: drivers/hid/hid-3m-pct.c:225:
+                hid->hiddev_hid_event(hid, field, usage, value);$

WARNING: space prohibited between function name and open parenthesis '('
#256: FILE: drivers/hid/hid-3m-pct.c:247:
+               kfree (md);

total: 5 errors, 4 warnings, 328 lines checked 

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

* Re: Fwd: [PATCH] Support for 3M multitouch panel
  2009-12-11 12:42 ` Petr Štetiar
@ 2009-12-11 14:10   ` Stephane Chatty
  2009-12-11 22:36     ` Dmitry Torokhov
  0 siblings, 1 reply; 13+ messages in thread
From: Stephane Chatty @ 2009-12-11 14:10 UTC (permalink / raw)
  To: ynezz, chatty; +Cc: linux-input, dsuljic


Added support for 3M multitouch panel.

Signed-off-by: Stephane Chatty <chatty@enac.fr>


diff -rupN a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c
--- a/drivers/hid/hid-3m-pct.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/hid/hid-3m-pct.c	2009-12-11 10:58:01.000000000 +0100
@@ -0,0 +1,294 @@
+/*
+ *  HID driver for 3M multitouch panels
+ *
+ *  Copyright (c) 2009 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+MODULE_VERSION("0.2");
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("3M PCT multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+static int emulate_touchscreen = 1;
+module_param_named(emulate_touchscreen, emulate_touchscreen, int, 0600);
+MODULE_PARM_DESC(emulate_touchscreen,
+		"Touchscreen emulation on 3M multitouch panel (0=off, 1=on)");
+
+struct mmm_finger {
+	__s32 x, y;
+	__u8 rank;
+	int touch:1, valid:1;
+};
+struct mmm_data {
+	struct mmm_finger f[10];
+	__u8 curid, num;
+	int touch:1, valid:1;
+};
+
+static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	switch (usage->hid & HID_USAGE_PAGE) {
+
+	case HID_UP_BUTTON:
+		return -1;
+
+	case HID_UP_GENDESK:
+		switch (usage->hid) {
+		case HID_GD_X:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_X);
+			if (emulate_touchscreen)
+				input_set_abs_params(hi->input, ABS_X,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		case HID_GD_Y:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_Y);
+			if (emulate_touchscreen)
+				input_set_abs_params(hi->input, ABS_Y,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		}
+		return 0;
+
+	case HID_UP_DIGITIZER:
+		switch (usage->hid) {
+		/* we do not want to map these: no input-oriented meaning */
+		case 0x14:
+		case 0x23:
+		case HID_DG_INPUTMODE:
+		case HID_DG_DEVICEINDEX:
+		case HID_DG_CONTACTCOUNT:
+		case HID_DG_CONTACTMAX:
+		case HID_DG_INRANGE:
+		case HID_DG_CONFIDENCE:
+			return -1;
+		case HID_DG_TIPSWITCH:
+			if (emulate_touchscreen) {
+				hid_map_usage(hi, usage, bit, max,
+					EV_KEY, BTN_TOUCH);
+				return 1;
+			} else
+				return -1;
+		case HID_DG_CONTACTID:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_TRACKING_ID);
+			return 1;
+		}
+		/* let hid-input decide for the others */
+		return 0;
+
+	case 0xff000000:
+		/* we do not want to map these: no input-oriented meaning */
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if (usage->type == EV_KEY || usage->type == EV_ABS)
+		clear_bit(usage->code, *bit);
+
+	return 0;
+}
+
+/*
+ * this function is called when a whole packet has been received and process,
+ * so that it can decide what to send to the input layer.
+ */
+static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
+{
+	struct mmm_finger *oldest = 0;
+	int pressed = 0, released = 0;
+	int i;
+	for (i = 0; i < 10; ++i) {
+		struct mmm_finger *f = &md->f[i];
+		if (!f->valid) {
+			/* ignore placeholder data */
+		} else if (f->touch) {
+			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
+			input_mt_sync(input);
+			/*
+			 * maintain age rank of this finger for our single
+			 * touch emulation.
+			 */
+			if (f->rank == 0) {
+				f->rank = ++(md->num);
+				if (f->rank == 1)
+					pressed = 1;
+			}
+			if (f->rank == 1)
+				oldest = f;
+		} else {
+			/*
+			 * maintain age rank of other fingers for our single
+			 * touch emulation.
+			 */
+			int j;
+			for (j = 0; j < 10; ++j) {
+				struct mmm_finger *g = &md->f[j];
+				if (g->rank > f->rank) {
+					g->rank--;
+					if (g->rank == 1)
+						oldest = g;
+				}
+			}
+			f->rank = 0;
+			--(md->num);
+			if (md->num == 0)
+				released = 1;
+		}
+		f->valid = 0;
+	}
+
+	if (emulate_touchscreen) {
+		if (oldest) {
+			if (pressed)
+				input_event(input, EV_KEY, BTN_TOUCH, 1);
+			input_event(input, EV_ABS, ABS_X, oldest->x);
+			input_event(input, EV_ABS, ABS_Y, oldest->y);
+		} else if (released) {
+			input_event(input, EV_KEY, BTN_TOUCH, 0);
+		}
+	}
+}
+
+/*
+ * this function is called upon all reports
+ * so that we can accumulate contact point information,
+ * and call input_mt_sync after each point.
+ */
+static int mmm_event(struct hid_device *hid, struct hid_field *field,
+			struct hid_usage *usage, __s32 value)
+{
+	struct mmm_data *md = hid_get_drvdata(hid);
+       /*
+	 * Strangely, this function can be called before
+	 * field->hidinput is initialized!
+	 */
+
+	if (hid->claimed & HID_CLAIMED_INPUT) {
+		struct input_dev *input = field->hidinput->input;
+		switch (usage->hid) {
+		case HID_DG_TIPSWITCH:
+			md->touch = value;
+			break;
+		case HID_DG_CONFIDENCE:
+			md->valid = value;
+			break;
+		case HID_DG_CONTACTID:
+			if (md->valid) {
+				md->curid = value;
+				md->f[value].touch = md->touch;
+				md->f[value].valid = 1;
+			}
+			break;
+		case HID_GD_X:
+			if (md->valid)
+				md->f[md->curid].x = value;
+			break;
+		case HID_GD_Y:
+			if (md->valid)
+				md->f[md->curid].y = value;
+			break;
+		case HID_DG_CONTACTCOUNT:
+			mmm_filter_event(md, input);
+			break;
+		}
+	}
+
+	/* we have handled the hidinput part, now remains hiddev */
+	if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+		hid->hiddev_hid_event(hid, field, usage, value);
+
+	return 1;
+}
+
+static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+	struct mmm_data *md;
+
+	md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
+	if (!md) {
+		dev_err(&hdev->dev, "cannot allocate 3M data\n");
+		return -ENOMEM;
+	}
+	hid_set_drvdata(hdev, md);
+
+	ret = hid_parse(hdev);
+	if (!ret)
+		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+	if (ret)
+		kfree(md);
+	return ret;
+}
+
+static void mmm_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id mmm_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, mmm_devices);
+
+static const struct hid_usage_id mmm_grabbed_usages[] = {
+	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mmm_driver = {
+	.name = "3m-pct",
+	.id_table = mmm_devices,
+	.probe = mmm_probe,
+	.remove = mmm_remove,
+	.input_mapping = mmm_input_mapping,
+	.input_mapped = mmm_input_mapped,
+	.usage_table = mmm_grabbed_usages,
+	.event = mmm_event,
+};
+
+static int mmm_init(void)
+{
+	return hid_register_driver(&mmm_driver);
+}
+
+static void mmm_exit(void)
+{
+	hid_unregister_driver(&mmm_driver);
+}
+
+module_init(mmm_init);
+module_exit(mmm_exit);
+MODULE_LICENSE("GPL");
+
diff -rupN a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
--- a/drivers/hid/hid-core.c	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-core.c	2009-12-11 10:57:43.000000000 +0100
@@ -1250,6 +1250,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
 
 /* a list of devices for which there is a specialized driver on HID bus */
 static const struct hid_device_id hid_blacklist[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
diff -rupN a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
--- a/drivers/hid/hid-ids.h	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-ids.h	2009-12-11 10:56:54.000000000 +0100
@@ -18,6 +18,9 @@
 #ifndef HID_IDS_H_FILE
 #define HID_IDS_H_FILE
 
+#define USB_VENDOR_ID_3M		0x0596
+#define USB_DEVICE_ID_3M1968		0x0500
+
 #define USB_VENDOR_ID_A4TECH		0x09da
 #define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
 #define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
diff -rupN a/drivers/hid/Kconfig b/drivers/hid/Kconfig
--- a/drivers/hid/Kconfig	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Kconfig	2009-12-11 10:59:07.000000000 +0100
@@ -55,6 +55,13 @@ source "drivers/hid/usbhid/Kconfig"
 menu "Special HID drivers"
 	depends on HID
 
+config HID_3M_PCT
+	tristate "3M PCT" if EMBEDDED
+	depends on USB_HID
+	default !EMBEDDED
+	---help---
+	Support for 3M PCT touch screens.
+
 config HID_A4TECH
 	tristate "A4 tech" if EMBEDDED
 	depends on USB_HID
diff -rupN a/drivers/hid/Makefile b/drivers/hid/Makefile
--- a/drivers/hid/Makefile	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Makefile	2009-12-11 10:59:29.000000000 +0100
@@ -19,6 +19,7 @@ ifdef CONFIG_LOGIRUMBLEPAD2_FF
 	hid-logitech-objs	+= hid-lg2ff.o
 endif
 
+obj-$(CONFIG_HID_3M_PCT)	+= hid-3m-pct.o
 obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
 obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o

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

* Re: Fwd: [PATCH] Support for 3M multitouch panel
  2009-12-11 14:10   ` Fwd: " Stephane Chatty
@ 2009-12-11 22:36     ` Dmitry Torokhov
  2009-12-12  8:13       ` Stéphane Chatty
  2009-12-22 18:00       ` Stéphane Chatty
  0 siblings, 2 replies; 13+ messages in thread
From: Dmitry Torokhov @ 2009-12-11 22:36 UTC (permalink / raw)
  To: Stephane Chatty; +Cc: ynezz, chatty, linux-input, dsuljic

Hi Stephane,

On Fri, Dec 11, 2009 at 03:10:38PM +0100, Stephane Chatty wrote:
> 
> Added support for 3M multitouch panel.
> 
> Signed-off-by: Stephane Chatty <chatty@enac.fr>
> 
> 
> diff -rupN a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c
> --- a/drivers/hid/hid-3m-pct.c	1970-01-01 01:00:00.000000000 +0100
> +++ b/drivers/hid/hid-3m-pct.c	2009-12-11 10:58:01.000000000 +0100
> @@ -0,0 +1,294 @@
> +/*
> + *  HID driver for 3M multitouch panels
> + *
> + *  Copyright (c) 2009 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/device.h>
> +#include <linux/hid.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +
> +MODULE_VERSION("0.2");
> +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
> +MODULE_DESCRIPTION("3M PCT multitouch panels");
> +MODULE_LICENSE("GPL");
> +
> +#include "hid-ids.h"
> +
> +static int emulate_touchscreen = 1;
> +module_param_named(emulate_touchscreen, emulate_touchscreen, int, 0600);
> +MODULE_PARM_DESC(emulate_touchscreen,
> +		"Touchscreen emulation on 3M multitouch panel (0=off, 1=on)");
> +

I thought we agreed that this option is unnecessary.

> +struct mmm_finger {
> +	__s32 x, y;
> +	__u8 rank;
> +	int touch:1, valid:1;
> +};

Does it make sense to turn access to touch and valid into
read-modify-write sequence?  Just change them to be 'bool's, it won't
cause your structures to grow in size.
 
> +struct mmm_data {
> +	struct mmm_finger f[10];
> +	__u8 curid, num;
> +	int touch:1, valid:1;

Same as above.

> +};
> +
> +static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
> +		struct hid_field *field, struct hid_usage *usage,
> +		unsigned long **bit, int *max)
> +{
> +	switch (usage->hid & HID_USAGE_PAGE) {
> +
> +	case HID_UP_BUTTON:
> +		return -1;
> +
> +	case HID_UP_GENDESK:
> +		switch (usage->hid) {
> +		case HID_GD_X:
> +			hid_map_usage(hi, usage, bit, max,
> +					EV_ABS, ABS_MT_POSITION_X);
> +			if (emulate_touchscreen)
> +				input_set_abs_params(hi->input, ABS_X,
> +						field->logical_minimum,
> +						field->logical_maximum, 0, 0);
> +			return 1;
> +		case HID_GD_Y:
> +			hid_map_usage(hi, usage, bit, max,
> +					EV_ABS, ABS_MT_POSITION_Y);
> +			if (emulate_touchscreen)
> +				input_set_abs_params(hi->input, ABS_Y,
> +						field->logical_minimum,
> +						field->logical_maximum, 0, 0);
> +			return 1;
> +		}
> +		return 0;
> +
> +	case HID_UP_DIGITIZER:
> +		switch (usage->hid) {
> +		/* we do not want to map these: no input-oriented meaning */
> +		case 0x14:
> +		case 0x23:
> +		case HID_DG_INPUTMODE:
> +		case HID_DG_DEVICEINDEX:
> +		case HID_DG_CONTACTCOUNT:
> +		case HID_DG_CONTACTMAX:
> +		case HID_DG_INRANGE:
> +		case HID_DG_CONFIDENCE:
> +			return -1;
> +		case HID_DG_TIPSWITCH:
> +			if (emulate_touchscreen) {
> +				hid_map_usage(hi, usage, bit, max,
> +					EV_KEY, BTN_TOUCH);
> +				return 1;
> +			} else
> +				return -1;
> +		case HID_DG_CONTACTID:
> +			hid_map_usage(hi, usage, bit, max,
> +					EV_ABS, ABS_MT_TRACKING_ID);
> +			return 1;
> +		}
> +		/* let hid-input decide for the others */
> +		return 0;
> +
> +	case 0xff000000:
> +		/* we do not want to map these: no input-oriented meaning */
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
> +		struct hid_field *field, struct hid_usage *usage,
> +		unsigned long **bit, int *max)
> +{
> +	if (usage->type == EV_KEY || usage->type == EV_ABS)
> +		clear_bit(usage->code, *bit);
> +
> +	return 0;
> +}
> +
> +/*
> + * this function is called when a whole packet has been received and process,
> + * so that it can decide what to send to the input layer.
> + */
> +static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
> +{
> +	struct mmm_finger *oldest = 0;
> +	int pressed = 0, released = 0;

These are bools so make them so.

> +	int i;

Blank lines between declarations and code are appreciated.

> +	for (i = 0; i < 10; ++i) {
> +		struct mmm_finger *f = &md->f[i];
> +		if (!f->valid) {
> +			/* ignore placeholder data */
> +		} else if (f->touch) {
> +			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
> +			input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
> +			input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
> +			input_mt_sync(input);
> +			/*
> +			 * maintain age rank of this finger for our single
> +			 * touch emulation.
> +			 */
> +			if (f->rank == 0) {
> +				f->rank = ++(md->num);
> +				if (f->rank == 1)
> +					pressed = 1;
> +			}
> +			if (f->rank == 1)
> +				oldest = f;
> +		} else {
> +			/*
> +			 * maintain age rank of other fingers for our single
> +			 * touch emulation.
> +			 */
> +			int j;
> +			for (j = 0; j < 10; ++j) {
> +				struct mmm_finger *g = &md->f[j];
> +				if (g->rank > f->rank) {
> +					g->rank--;
> +					if (g->rank == 1)
> +						oldest = g;
> +				}
> +			}
> +			f->rank = 0;
> +			--(md->num);
> +			if (md->num == 0)
> +				released = 1;
> +		}

Maybe move out into a separate function to report one finger?

> +		f->valid = 0;
> +	}
> +
> +	if (emulate_touchscreen) {
> +		if (oldest) {
> +			if (pressed)
> +				input_event(input, EV_KEY, BTN_TOUCH, 1);
> +			input_event(input, EV_ABS, ABS_X, oldest->x);
> +			input_event(input, EV_ABS, ABS_Y, oldest->y);
> +		} else if (released) {
> +			input_event(input, EV_KEY, BTN_TOUCH, 0);
> +		}
> +	}
> +}
> +
> +/*
> + * this function is called upon all reports
> + * so that we can accumulate contact point information,
> + * and call input_mt_sync after each point.
> + */
> +static int mmm_event(struct hid_device *hid, struct hid_field *field,
> +			struct hid_usage *usage, __s32 value)
> +{
> +	struct mmm_data *md = hid_get_drvdata(hid);
> +       /*

Indent with tab please.

> +	 * Strangely, this function can be called before
> +	 * field->hidinput is initialized!
> +	 */
> +
> +	if (hid->claimed & HID_CLAIMED_INPUT) {
> +		struct input_dev *input = field->hidinput->input;
> +		switch (usage->hid) {
> +		case HID_DG_TIPSWITCH:
> +			md->touch = value;
> +			break;
> +		case HID_DG_CONFIDENCE:
> +			md->valid = value;
> +			break;
> +		case HID_DG_CONTACTID:
> +			if (md->valid) {
> +				md->curid = value;
> +				md->f[value].touch = md->touch;
> +				md->f[value].valid = 1;
> +			}
> +			break;
> +		case HID_GD_X:
> +			if (md->valid)
> +				md->f[md->curid].x = value;
> +			break;
> +		case HID_GD_Y:
> +			if (md->valid)
> +				md->f[md->curid].y = value;
> +			break;
> +		case HID_DG_CONTACTCOUNT:
> +			mmm_filter_event(md, input);
> +			break;
> +		}
> +	}
> +
> +	/* we have handled the hidinput part, now remains hiddev */
> +	if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
> +		hid->hiddev_hid_event(hid, field, usage, value);
> +
> +	return 1;
> +}
> +
> +static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> +	int ret;
> +	struct mmm_data *md;
> +
> +	md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
> +	if (!md) {
> +		dev_err(&hdev->dev, "cannot allocate 3M data\n");
> +		return -ENOMEM;
> +	}
> +	hid_set_drvdata(hdev, md);
> +
> +	ret = hid_parse(hdev);
> +	if (!ret)
> +		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> +
> +	if (ret)
> +		kfree(md);
> +	return ret;
> +}
> +
> +static void mmm_remove(struct hid_device *hdev)
> +{
> +	hid_hw_stop(hdev);
> +	kfree(hid_get_drvdata(hdev));

hid_set_drvdata(hdev, NULL); is a polite thing to do.

> +}
> +
> +static const struct hid_device_id mmm_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, mmm_devices);
> +
> +static const struct hid_usage_id mmm_grabbed_usages[] = {
> +	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
> +	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
> +};
> +
> +static struct hid_driver mmm_driver = {
> +	.name = "3m-pct",
> +	.id_table = mmm_devices,
> +	.probe = mmm_probe,
> +	.remove = mmm_remove,
> +	.input_mapping = mmm_input_mapping,
> +	.input_mapped = mmm_input_mapped,
> +	.usage_table = mmm_grabbed_usages,
> +	.event = mmm_event,
> +};
> +
> +static int mmm_init(void)

__init

> +{
> +	return hid_register_driver(&mmm_driver);
> +}
> +
> +static void mmm_exit(void)

__exit

> +{
> +	hid_unregister_driver(&mmm_driver);
> +}
> +
> +module_init(mmm_init);
> +module_exit(mmm_exit);
> +MODULE_LICENSE("GPL");
> +
> diff -rupN a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> --- a/drivers/hid/hid-core.c	2009-12-03 04:51:21.000000000 +0100
> +++ b/drivers/hid/hid-core.c	2009-12-11 10:57:43.000000000 +0100
> @@ -1250,6 +1250,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
>  
>  /* a list of devices for which there is a specialized driver on HID bus */
>  static const struct hid_device_id hid_blacklist[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
> diff -rupN a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> --- a/drivers/hid/hid-ids.h	2009-12-03 04:51:21.000000000 +0100
> +++ b/drivers/hid/hid-ids.h	2009-12-11 10:56:54.000000000 +0100
> @@ -18,6 +18,9 @@
>  #ifndef HID_IDS_H_FILE
>  #define HID_IDS_H_FILE
>  
> +#define USB_VENDOR_ID_3M		0x0596
> +#define USB_DEVICE_ID_3M1968		0x0500
> +
>  #define USB_VENDOR_ID_A4TECH		0x09da
>  #define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
>  #define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
> diff -rupN a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> --- a/drivers/hid/Kconfig	2009-12-03 04:51:21.000000000 +0100
> +++ b/drivers/hid/Kconfig	2009-12-11 10:59:07.000000000 +0100
> @@ -55,6 +55,13 @@ source "drivers/hid/usbhid/Kconfig"
>  menu "Special HID drivers"
>  	depends on HID
>  
> +config HID_3M_PCT
> +	tristate "3M PCT" if EMBEDDED
> +	depends on USB_HID
> +	default !EMBEDDED
> +	---help---
> +	Support for 3M PCT touch screens.
> +
>  config HID_A4TECH
>  	tristate "A4 tech" if EMBEDDED
>  	depends on USB_HID
> diff -rupN a/drivers/hid/Makefile b/drivers/hid/Makefile
> --- a/drivers/hid/Makefile	2009-12-03 04:51:21.000000000 +0100
> +++ b/drivers/hid/Makefile	2009-12-11 10:59:29.000000000 +0100
> @@ -19,6 +19,7 @@ ifdef CONFIG_LOGIRUMBLEPAD2_FF
>  	hid-logitech-objs	+= hid-lg2ff.o
>  endif
>  
> +obj-$(CONFIG_HID_3M_PCT)	+= hid-3m-pct.o
>  obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
>  obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
>  obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o

Also please add Jiri Kosina <jkosina@suse.cz> to your next submission
since the driver will go through his tree. Thanks.


> --
> 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

-- 
Dmitry

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-11 22:36     ` Dmitry Torokhov
@ 2009-12-12  8:13       ` Stéphane Chatty
  2009-12-12  8:30         ` Dmitry Torokhov
  2009-12-22 18:00       ` Stéphane Chatty
  1 sibling, 1 reply; 13+ messages in thread
From: Stéphane Chatty @ 2009-12-12  8:13 UTC (permalink / raw)
  To: Dmitry Torokhov, Peter Hutterer, Rafi Rubin, Benjamin Tissoires
  Cc: linux-input


Le 11 déc. 09 à 23:36, Dmitry Torokhov a écrit :

> Hi Stephane,
>
>
>> +static int emulate_touchscreen = 1;
>> +module_param_named(emulate_touchscreen, emulate_touchscreen, int,  
>> 0600);
>> +MODULE_PARM_DESC(emulate_touchscreen,
>> +		"Touchscreen emulation on 3M multitouch panel (0=off, 1=on)");
>> +
>
> I thought we agreed that this option is unnecessary.
>

Well, actually I was unsure what the conclusion of your discussion  
was. I decided that I would send this patch as is to see what  
happens :-)

Here are our options:
  1) mix touchscreen emulation and multitouch at all times
  2) only activate touchscreen emulation when requested (module  
parameter, or something else)
  3) have separate input nodes
  4) kill the touchscreen emulation code, let the higher layers do it  
if necessary

I personally would rule out option 4. The others are OK to me. Your  
thoughts?

St.

PS: since yesterday, I have a working firmware on my NTrig panel. It  
will be interesting to see what happens with a device that  
distinguishes fingers and stylus but does not provide finger  
tracking. Maybe it will give a new light on this.

--
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	[flat|nested] 13+ messages in thread

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-12  8:13       ` Stéphane Chatty
@ 2009-12-12  8:30         ` Dmitry Torokhov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry Torokhov @ 2009-12-12  8:30 UTC (permalink / raw)
  To: Stéphane Chatty
  Cc: Peter Hutterer, Rafi Rubin, Benjamin Tissoires, linux-input

On Sat, Dec 12, 2009 at 09:13:01AM +0100, Stéphane Chatty wrote:
>
> Le 11 déc. 09 à 23:36, Dmitry Torokhov a écrit :
>
>> Hi Stephane,
>>
>>
>>> +static int emulate_touchscreen = 1;
>>> +module_param_named(emulate_touchscreen, emulate_touchscreen, int,  
>>> 0600);
>>> +MODULE_PARM_DESC(emulate_touchscreen,
>>> +		"Touchscreen emulation on 3M multitouch panel (0=off, 1=on)");
>>> +
>>
>> I thought we agreed that this option is unnecessary.
>>
>
> Well, actually I was unsure what the conclusion of your discussion was. I 
> decided that I would send this patch as is to see what happens :-)
>
> Here are our options:
>  1) mix touchscreen emulation and multitouch at all times
>  2) only activate touchscreen emulation when requested (module  
> parameter, or something else)
>  3) have separate input nodes
>  4) kill the touchscreen emulation code, let the higher layers do it if 
> necessary
>
> I personally would rule out option 4. The others are OK to me. Your  
> thoughts?

#1 until everyone moves on to multitouch... and everyone means
_everyone_, not only X and distributions. I could consider #3 if we see
that multitouch data overwhelms oldish userspace, but OTOH users would
probably prefer to update their userspace to take advantage of their
hardware so the multitouch data will be opened anyway... so we back to
#1.

-- 
Dmitry
--
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	[flat|nested] 13+ messages in thread

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-11 22:36     ` Dmitry Torokhov
  2009-12-12  8:13       ` Stéphane Chatty
@ 2009-12-22 18:00       ` Stéphane Chatty
  2009-12-22 18:56         ` Dmitry Torokhov
  1 sibling, 1 reply; 13+ messages in thread
From: Stéphane Chatty @ 2009-12-22 18:00 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: ynezz, linux-input

Hi Dmitry,

I am working on the 3M driver and taking your comments into account  
(and applying them to the Stantum driver as well) but I have an  
question on the comment below:

>
>> +struct mmm_finger {
>> +	__s32 x, y;
>> +	__u8 rank;
>> +	int touch:1, valid:1;
>> +};
>
> Does it make sense to turn access to touch and valid into
> read-modify-write sequence?  Just change them to be 'bool's, it won't
> cause your structures to grow in size.
>
>> +struct mmm_data {
>> +	struct mmm_finger f[10];
>> +	__u8 curid, num;
>> +	int touch:1, valid:1;
>
> Same as above.

Why do you think a read-modify-write sequence would be better? in my  
mind these were just cached values, just like x and y.

Cheers,

St.
  

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-22 18:00       ` Stéphane Chatty
@ 2009-12-22 18:56         ` Dmitry Torokhov
  2009-12-22 19:27           ` Stéphane Chatty
  2009-12-22 22:04           ` Stephane Chatty
  0 siblings, 2 replies; 13+ messages in thread
From: Dmitry Torokhov @ 2009-12-22 18:56 UTC (permalink / raw)
  To: Stéphane Chatty; +Cc: ynezz, linux-input

Hi Stéphane,

On Tue, Dec 22, 2009 at 07:00:49PM +0100, Stéphane Chatty wrote:
> Hi Dmitry,
>
> I am working on the 3M driver and taking your comments into account (and 
> applying them to the Stantum driver as well) but I have an question on 
> the comment below:
>
>>
>>> +struct mmm_finger {
>>> +	__s32 x, y;
>>> +	__u8 rank;
>>> +	int touch:1, valid:1;
>>> +};
>>
>> Does it make sense to turn access to touch and valid into
>> read-modify-write sequence?  Just change them to be 'bool's, it won't
>> cause your structures to grow in size.
>>
>>> +struct mmm_data {
>>> +	struct mmm_finger f[10];
>>> +	__u8 curid, num;
>>> +	int touch:1, valid:1;
>>
>> Same as above.
>
> Why do you think a read-modify-write sequence would be better? in my  
> mind these were just cached values, just like x and y.

I meant "read-modify-write" is worse.

To modify a bitfiled the processor has to fetch data from memory (or
cache if it is in cache) perform a logical operation on it, and store
it back in teh memory. With boolean occupying 1 byte, on most
architectures (older alphas is one exception I know of) var=true can be
done by simple store, no need for "read-modify" part.

-- 
Dmitry
--
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	[flat|nested] 13+ messages in thread

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-22 18:56         ` Dmitry Torokhov
@ 2009-12-22 19:27           ` Stéphane Chatty
  2009-12-22 22:04           ` Stephane Chatty
  1 sibling, 0 replies; 13+ messages in thread
From: Stéphane Chatty @ 2009-12-22 19:27 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: ynezz, linux-input


Le 22 déc. 09 à 19:56, Dmitry Torokhov a écrit :
>
> I meant "read-modify-write" is worse.
>
> To modify a bitfiled the processor has to fetch data from memory (or
> cache if it is in cache) perform a logical operation on it, and store
> it back in teh memory. With boolean occupying 1 byte, on most
> architectures (older alphas is one exception I know of) var=true  
> can be
> done by simple store, no need for "read-modify" part.

OK. I had not realized this. My processor architecture courses were  
too long ago.

St.

--
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	[flat|nested] 13+ messages in thread

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-22 18:56         ` Dmitry Torokhov
  2009-12-22 19:27           ` Stéphane Chatty
@ 2009-12-22 22:04           ` Stephane Chatty
  2009-12-23  0:22             ` Jiri Kosina
  2009-12-23  0:33             ` Jiri Kosina
  1 sibling, 2 replies; 13+ messages in thread
From: Stephane Chatty @ 2009-12-22 22:04 UTC (permalink / raw)
  To: dmitry.torokhov, chatty; +Cc: chatty, jkosina, ynezz, linux-input


Added support for 3M multitouch panels.

Signed-off-by: Stephane Chatty <chatty@enac.fr>

diff -rupN a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c
--- a/drivers/hid/hid-3m-pct.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/hid/hid-3m-pct.c	2009-12-22 22:36:25.000000000 +0100
@@ -0,0 +1,291 @@
+/*
+ *  HID driver for 3M PCT multitouch panels
+ *
+ *  Copyright (c) 2009 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+MODULE_VERSION("0.6");
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("3M PCT multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+struct mmm_finger {
+	__s32 x, y;
+	__u8 rank;
+	bool touch, valid;
+};
+
+struct mmm_data {
+	struct mmm_finger f[10];
+	__u8 curid, num;
+	bool touch, valid;
+};
+
+static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	switch (usage->hid & HID_USAGE_PAGE) {
+
+	case HID_UP_BUTTON:
+		return -1;
+
+	case HID_UP_GENDESK:
+		switch (usage->hid) {
+		case HID_GD_X:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_X);
+			/* touchscreen emulation */
+			input_set_abs_params(hi->input, ABS_X,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		case HID_GD_Y:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_POSITION_Y);
+			/* touchscreen emulation */
+			input_set_abs_params(hi->input, ABS_Y,
+						field->logical_minimum,
+						field->logical_maximum, 0, 0);
+			return 1;
+		}
+		return 0;
+
+	case HID_UP_DIGITIZER:
+		switch (usage->hid) {
+		/* we do not want to map these: no input-oriented meaning */
+		case 0x14:
+		case 0x23:
+		case HID_DG_INPUTMODE:
+		case HID_DG_DEVICEINDEX:
+		case HID_DG_CONTACTCOUNT:
+		case HID_DG_CONTACTMAX:
+		case HID_DG_INRANGE:
+		case HID_DG_CONFIDENCE:
+			return -1;
+		case HID_DG_TIPSWITCH:
+			/* touchscreen emulation */
+			hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+			return 1;
+		case HID_DG_CONTACTID:
+			hid_map_usage(hi, usage, bit, max,
+					EV_ABS, ABS_MT_TRACKING_ID);
+			return 1;
+		}
+		/* let hid-input decide for the others */
+		return 0;
+
+	case 0xff000000:
+		/* we do not want to map these: no input-oriented meaning */
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if (usage->type == EV_KEY || usage->type == EV_ABS)
+		clear_bit(usage->code, *bit);
+
+	return 0;
+}
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
+{
+	struct mmm_finger *oldest = 0;
+	bool pressed = false, released = false;
+	int i;
+
+	/*
+	 * we need to iterate on all fingers to decide if we have a press
+	 * or a release event in our touchscreen emulation.
+	 */
+	for (i = 0; i < 10; ++i) {
+		struct mmm_finger *f = &md->f[i];
+		if (!f->valid) {
+			/* this finger is just placeholder data, ignore */
+		} else if (f->touch) {
+			/* this finger is on the screen */
+			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
+			input_mt_sync(input);
+			/*
+			 * touchscreen emulation: maintain the age rank
+			 * of this finger, decide if we have a press
+			 */
+			if (f->rank == 0) {
+				f->rank = ++(md->num);
+				if (f->rank == 1)
+					pressed = true;
+			}
+			if (f->rank == 1)
+				oldest = f;
+		} else {
+			/* this finger took off the screen */
+			/* touchscreen emulation: maintain age rank of others */
+			int j;
+
+			for (j = 0; j < 10; ++j) {
+				struct mmm_finger *g = &md->f[j];
+				if (g->rank > f->rank) {
+					g->rank--;
+					if (g->rank == 1)
+						oldest = g;
+				}
+			}
+			f->rank = 0;
+			--(md->num);
+			if (md->num == 0)
+				released = true;
+		}
+		f->valid = 0;
+	}
+
+	/* touchscreen emulation */
+	if (oldest) {
+		if (pressed)
+			input_event(input, EV_KEY, BTN_TOUCH, 1);
+		input_event(input, EV_ABS, ABS_X, oldest->x);
+		input_event(input, EV_ABS, ABS_Y, oldest->y);
+	} else if (released) {
+		input_event(input, EV_KEY, BTN_TOUCH, 0);
+	}
+}
+
+/*
+ * this function is called upon all reports
+ * so that we can accumulate contact point information,
+ * and call input_mt_sync after each point.
+ */
+static int mmm_event(struct hid_device *hid, struct hid_field *field,
+				struct hid_usage *usage, __s32 value)
+{
+	struct mmm_data *md = hid_get_drvdata(hid);
+	/*
+	 * strangely, this function can be called before
+	 * field->hidinput is initialized!
+	 */
+	if (hid->claimed & HID_CLAIMED_INPUT) {
+		struct input_dev *input = field->hidinput->input;
+		switch (usage->hid) {
+		case HID_DG_TIPSWITCH:
+			md->touch = value;
+			break;
+		case HID_DG_CONFIDENCE:
+			md->valid = value;
+			break;
+		case HID_DG_CONTACTID:
+			if (md->valid) {
+				md->curid = value;
+				md->f[value].touch = md->touch;
+				md->f[value].valid = 1;
+			}
+			break;
+		case HID_GD_X:
+			if (md->valid)
+				md->f[md->curid].x = value;
+			break;
+		case HID_GD_Y:
+			if (md->valid)
+				md->f[md->curid].y = value;
+			break;
+		case HID_DG_CONTACTCOUNT:
+			mmm_filter_event(md, input);
+			break;
+		}
+	}
+
+	/* we have handled the hidinput part, now remains hiddev */
+	if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+		hid->hiddev_hid_event(hid, field, usage, value);
+
+	return 1;
+}
+
+static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+	struct mmm_data *md;
+
+	md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
+	if (!md) {
+		dev_err(&hdev->dev, "cannot allocate 3M data\n");
+		return -ENOMEM;
+	}
+	hid_set_drvdata(hdev, md);
+
+	ret = hid_parse(hdev);
+	if (!ret)
+		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+
+	if (ret)
+		kfree(md);
+	return ret;
+}
+
+static void mmm_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+	hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id mmm_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MMM, USB_DEVICE_ID_MMM1968) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, mmm_devices);
+
+static const struct hid_usage_id mmm_grabbed_usages[] = {
+	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mmm_driver = {
+	.name = "3m-pct",
+	.id_table = mmm_devices,
+	.probe = mmm_probe,
+	.remove = mmm_remove,
+	.input_mapping = mmm_input_mapping,
+	.input_mapped = mmm_input_mapped,
+	.usage_table = mmm_grabbed_usages,
+	.event = mmm_event,
+};
+
+static int __init mmm_init(void)
+{
+	return hid_register_driver(&mmm_driver);
+}
+
+static void __exit mmm_exit(void)
+{
+	hid_unregister_driver(&mmm_driver);
+}
+
+module_init(mmm_init);
+module_exit(mmm_exit);
+MODULE_LICENSE("GPL");
+
diff -rupN a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
--- a/drivers/hid/hid-core.c	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-core.c	2009-12-11 10:57:43.000000000 +0100
@@ -1250,6 +1250,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
 
 /* a list of devices for which there is a specialized driver on HID bus */
 static const struct hid_device_id hid_blacklist[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
diff -rupN a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
--- a/drivers/hid/hid-ids.h	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/hid-ids.h	2009-12-11 10:56:54.000000000 +0100
@@ -18,6 +18,9 @@
 #ifndef HID_IDS_H_FILE
 #define HID_IDS_H_FILE
 
+#define USB_VENDOR_ID_3M		0x0596
+#define USB_DEVICE_ID_3M1968		0x0500
+
 #define USB_VENDOR_ID_A4TECH		0x09da
 #define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
 #define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
diff -rupN a/drivers/hid/Kconfig b/drivers/hid/Kconfig
--- a/drivers/hid/Kconfig	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Kconfig	2009-12-11 10:59:07.000000000 +0100
@@ -55,6 +55,13 @@ source "drivers/hid/usbhid/Kconfig"
 menu "Special HID drivers"
 	depends on HID
 
+config HID_3M_PCT
+	tristate "3M PCT" if EMBEDDED
+	depends on USB_HID
+	default !EMBEDDED
+	---help---
+	Support for 3M PCT touch screens.
+
 config HID_A4TECH
 	tristate "A4 tech" if EMBEDDED
 	depends on USB_HID
diff -rupN a/drivers/hid/Makefile b/drivers/hid/Makefile
--- a/drivers/hid/Makefile	2009-12-03 04:51:21.000000000 +0100
+++ b/drivers/hid/Makefile	2009-12-11 10:59:29.000000000 +0100
@@ -19,6 +19,7 @@ ifdef CONFIG_LOGIRUMBLEPAD2_FF
 	hid-logitech-objs	+= hid-lg2ff.o
 endif
 
+obj-$(CONFIG_HID_3M_PCT)	+= hid-3m-pct.o
 obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
 obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-22 22:04           ` Stephane Chatty
@ 2009-12-23  0:22             ` Jiri Kosina
  2009-12-23  0:33             ` Jiri Kosina
  1 sibling, 0 replies; 13+ messages in thread
From: Jiri Kosina @ 2009-12-23  0:22 UTC (permalink / raw)
  To: Stephane Chatty; +Cc: dmitry.torokhov, chatty, ynezz, linux-input

On Tue, 22 Dec 2009, Stephane Chatty wrote:

> Added support for 3M multitouch panels.

Looks good to me, applied to my tree, thanks for the driver!

(and thanks Dmitry for the review, as I haven't been originally CCed, I 
came too late into the game).

-- 
Jiri Kosina
SUSE Labs, Novell Inc.

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-22 22:04           ` Stephane Chatty
  2009-12-23  0:22             ` Jiri Kosina
@ 2009-12-23  0:33             ` Jiri Kosina
  2009-12-23  9:47               ` Stéphane Chatty
  1 sibling, 1 reply; 13+ messages in thread
From: Jiri Kosina @ 2009-12-23  0:33 UTC (permalink / raw)
  To: Stephane Chatty; +Cc: dmitry.torokhov, chatty, ynezz, linux-input

On Tue, 22 Dec 2009, Stephane Chatty wrote:

> Added support for 3M multitouch panels.

... I had to fix trivial build failure (3M vs. MMM inconsistency in 
defines).

> +config HID_3M_PCT
> +	tristate "3M PCT" if EMBEDDED
> +	depends on USB_HID
> +	default !EMBEDDED

Hmm, I wouldn't be so sure about this in this case ... the point behind 
'default !EMBEDDED' was that simple a straightforward quirks for HID 
devices (which are implemented as drivers on HID bus) wouldn't have to be 
enabled separately, if the device is otherwise more-or-less HID standard 
compliant.

But this driver is rather standalone driver, I guess we'd want to have it 
normally selectable.

-- 
Jiri Kosina
SUSE Labs, Novell Inc.

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

* Re: [PATCH] Support for 3M multitouch panel
  2009-12-23  0:33             ` Jiri Kosina
@ 2009-12-23  9:47               ` Stéphane Chatty
  0 siblings, 0 replies; 13+ messages in thread
From: Stéphane Chatty @ 2009-12-23  9:47 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Stephane Chatty, dmitry.torokhov, ynezz, linux-input


Le 23 déc. 09 à 01:33, Jiri Kosina a écrit :

> On Tue, 22 Dec 2009, Stephane Chatty wrote:
>
>> Added support for 3M multitouch panels.
>
> ... I had to fix trivial build failure (3M vs. MMM inconsistency in
> defines).

Oops, sorry. This 3M / MMM thing has bothered me until the end, and I  
probably did one change too much.

>
>> +config HID_3M_PCT
>> +	tristate "3M PCT" if EMBEDDED
>> +	depends on USB_HID
>> +	default !EMBEDDED
>
> Hmm, I wouldn't be so sure about this in this case ... the point  
> behind
> 'default !EMBEDDED' was that simple a straightforward quirks for HID
> devices (which are implemented as drivers on HID bus) wouldn't have  
> to be
> enabled separately, if the device is otherwise more-or-less HID  
> standard
> compliant.
>
> But this driver is rather standalone driver, I guess we'd want to  
> have it
> normally selectable.

I guess too. I had just copied other sections, not fully  
understanding what I was doing.

St.

--
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	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2009-12-23  9:48 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-11 10:12 [PATCH] Support for 3M multitouch panel Stephane Chatty
2009-12-11 12:42 ` Petr Štetiar
2009-12-11 14:10   ` Fwd: " Stephane Chatty
2009-12-11 22:36     ` Dmitry Torokhov
2009-12-12  8:13       ` Stéphane Chatty
2009-12-12  8:30         ` Dmitry Torokhov
2009-12-22 18:00       ` Stéphane Chatty
2009-12-22 18:56         ` Dmitry Torokhov
2009-12-22 19:27           ` Stéphane Chatty
2009-12-22 22:04           ` Stephane Chatty
2009-12-23  0:22             ` Jiri Kosina
2009-12-23  0:33             ` Jiri Kosina
2009-12-23  9:47               ` Stéphane Chatty

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).