From 6317c631cb1fd32f34da98a945747781d5a8906d Mon Sep 17 00:00:00 2001
From: Priya Vijayan <priya.vijayan@intel.com>
Date: Tue, 4 May 2010 14:21:37 -0700
Subject: [PATCH] Add mtdev driver and configs

Add multi-touch driver and configs for event devices.
This module is from He Min <min.he@intel.com> 
Code modifications and configs from Priya Vijayan <priya.vijayan@intel.com>

Patch-mainline: 2.6.34

Signed-off-by: Priya Vijayan <priya.vijayan@intel.com>
---
 drivers/input/Kconfig  |    9 ++
 drivers/input/Makefile |    1 +
 drivers/input/input.c  |    1 +
 drivers/input/mtdev.c  |  307 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/input.h  |    1 +
 5 files changed, 319 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/mtdev.c

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 07c2cd4..0264508 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -135,6 +135,15 @@ config INPUT_EVDEV
 	  To compile this driver as a module, choose M here: the
 	  module will be called evdev.
 
+config INPUT_MTDEV
+        tristate "Multitouch interface"
+        help
+	  Say  Y here if you want to enable Multi-touch input driver for event devices 
+	  If unsure, say N.
+	
+	  To compile this driver as a module, choose M here:the
+	  module will be called mtdev.
+
 config INPUT_EVBUG
 	tristate "Event debugging"
 	help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ad212d..96a4d94 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
 obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
 obj-$(CONFIG_INPUT_EVBUG)	+= evbug.o
+obj-$(CONFIG_INPUT_MTDEV)	+= mtdev.o
 
 obj-$(CONFIG_INPUT_KEYBOARD)	+= keyboard/
 obj-$(CONFIG_INPUT_MOUSE)	+= mouse/
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 86cb2d2..b589dec 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -47,6 +47,7 @@ static unsigned int input_abs_bypass_init_data[] __initdata = {
 	ABS_MT_BLOB_ID,
 	ABS_MT_TRACKING_ID,
 	ABS_MT_PRESSURE,
+	ABS_MT_CONTACT_COUNT,
 	0
 };
 static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)];
diff --git a/drivers/input/mtdev.c b/drivers/input/mtdev.c
new file mode 100644
index 0000000..8b01220
--- /dev/null
+++ b/drivers/input/mtdev.c
@@ -0,0 +1,312 @@
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/wait.h>
+#include <linux/kthread.h>
+
+#define MTDEV_MAX_POINTERS 5
+
+#ifndef ABS_MT_PRESSURE
+#define ABS_MT_PRESSURE 0x3a
+#endif
+#ifndef ABS_MT_CONTACT_COUNT
+#define ABS_MT_CONTACT_COUNT 0x3b
+#endif
+
+struct mtdev_input_dev
+{
+	struct input_dev* input_dev;
+	int id;
+	bool ready;
+	int x;
+	int y;
+	int z;
+	int touch;
+};
+
+struct mtdev_dev
+{
+	int count;
+	int last_count;
+	wait_queue_head_t wq;
+	struct input_handle handle;
+	struct mtdev_input_dev devs[MTDEV_MAX_POINTERS];
+};
+
+//id==-1 means to find an empty slot
+static int find_match_id(struct mtdev_dev * mtdev,int id)
+{
+	int i=0;
+
+	for (i=0;i<MTDEV_MAX_POINTERS;i++)
+	{
+		if(mtdev->devs[i].id==id)
+		{
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+static int mtdev_kt(void *data)
+{
+	struct mtdev_dev *mtdev=(struct mtdev_dev*)data;
+	int i=0;
+	int err=0;
+	printk("mtdev_kt entered\n");
+	if(!mtdev)
+		return -1;
+	//wait_event_interruptible(mtdev->wq,kthread_should_stop());
+	for(;i<MTDEV_MAX_POINTERS;i++)
+	{
+		struct input_dev *pdev=NULL;
+		pdev=mtdev->devs[i].input_dev;
+
+		err=input_register_device(pdev);
+		if(err)
+		{
+			printk("error in register mtdev %d\n",err);
+			return err;
+		}
+		else
+			printk("successfully registered input %d\n",i);
+		mtdev->devs[i].ready=true;
+
+	}
+	return 0;
+}
+
+static void mtdev_event(struct input_handle * handle, 
+			unsigned int type, unsigned int code, int value)
+{
+	static int i=0;
+	//int err=0;
+	int j=0;
+	struct mtdev_dev *mtdev=handle->private;
+	//printk("mtdev_event %x %x %x\n",type,code,value);
+	if(!mtdev->devs[1].ready||!mtdev->devs[0].ready)
+		return;
+	if(type==EV_ABS)
+	{
+		switch(code)
+		{
+			case ABS_MT_CONTACT_COUNT:
+				if(value!=0)
+				{
+					//we start from the first point
+					i=0;
+					//printk("mtdev:contact count is %d\n",value);
+				}
+				else if(value>MTDEV_MAX_POINTERS)
+				{
+					value=MTDEV_MAX_POINTERS;
+				}
+
+				//found last release fingers and send release event
+				for(j=0;j<MTDEV_MAX_POINTERS;j++)
+				{
+					if(mtdev->devs[j].touch==0
+						&&mtdev->devs[j].id!=-1)
+					{
+						input_report_key(mtdev->devs[j].input_dev,BTN_TOUCH,0);
+						input_sync(mtdev->devs[j].input_dev);
+						printk("%d id %d released\n",j,mtdev->devs[j].id);
+						mtdev->devs[j].id=-1;
+					}
+					mtdev->devs[j].touch=0;
+				}
+				mtdev->count=value;
+				
+				mtdev->last_count=value;
+
+				break;
+			case ABS_MT_TRACKING_ID:
+				{
+					i=find_match_id(mtdev,value);
+					if(i==-1||i>=MTDEV_MAX_POINTERS)
+					{
+						i=find_match_id(mtdev,-1);
+						if(i==-1||i>=MTDEV_MAX_POINTERS)
+						{
+							printk("no empty slot for id %d\n",value);
+							break;
+						}
+						else
+						{
+							//newly pressed 
+							mtdev->devs[i].touch=2;
+							mtdev->devs[i].id=value;
+							printk("found slot %d for id %d\n",i,value);
+							break;
+						}
+					}
+					//printk("found slot %d for id%d\n",i,value);
+					//keep the point
+					mtdev->devs[i].touch=1;
+					
+				}
+				break;
+			case ABS_MT_POSITION_X:
+				if(i<MTDEV_MAX_POINTERS&&i!=-1)
+					mtdev->devs[i].x=value;
+				//printk("mt x :%d\n",value);
+				break;
+			case ABS_MT_POSITION_Y:
+				if(i<MTDEV_MAX_POINTERS&&i!=-1)
+					mtdev->devs[i].y=value;
+				//printk("mt y :%d\n",value);
+				break;
+			case ABS_MT_PRESSURE:
+				if(i<MTDEV_MAX_POINTERS&&i!=-1)
+					mtdev->devs[i].z=value;
+				break;
+			default:
+				break;
+		}
+	}
+	else if(type == EV_SYN && code == SYN_MT_REPORT)
+	{
+		if(i<MTDEV_MAX_POINTERS&&i!=-1)
+		{
+			if(mtdev->devs[i].touch==2)
+			{
+				input_report_key(mtdev->devs[i].input_dev,BTN_TOUCH,1);
+
+			}
+			input_report_abs(mtdev->devs[i].input_dev,ABS_X,mtdev->devs[i].x);
+			input_report_abs(mtdev->devs[i].input_dev,ABS_Y,mtdev->devs[i].y);
+			input_report_abs(mtdev->devs[i].input_dev,ABS_PRESSURE,mtdev->devs[i].z);
+			input_sync(mtdev->devs[i].input_dev);
+			//printk("mtdev_event %d id %d (%d,%d,%d)\n",i,mtdev->devs[i].id,mtdev->devs[i].x,mtdev->devs[i].y,mtdev->devs[i].z);
+			//i++;
+		}
+	}
+
+}
+/*
+ * grab all the input of mt device, create new single touch input devices
+ *
+ */
+static int mtdev_connect(struct input_handler *handler, struct input_dev *dev,
+		const struct input_device_id *id)
+{
+	struct mtdev_dev* mtdev;
+	struct task_struct * task=NULL;
+	int i=0;
+	int err=0;
+	printk("mtdev_connect\n");
+	mtdev=kzalloc(sizeof(struct mtdev_dev),GFP_KERNEL);
+	if(!mtdev)
+		return -ENOMEM;
+	mtdev->handle.dev=input_get_device(dev);
+	mtdev->handle.name="mtdev";
+	mtdev->handle.handler=handler;
+	mtdev->handle.private=mtdev;
+	mtdev->count=0;
+	mtdev->last_count=0;
+	init_waitqueue_head(&mtdev->wq);
+	for(;i<MTDEV_MAX_POINTERS;i++)
+	{
+		//we just store the data here, and will register it 
+		//when the first event comes
+		struct input_dev *pdev=NULL;
+		mtdev->devs[i].ready=false;
+		mtdev->devs[i].id=-1;
+		mtdev->devs[i].touch=-1;
+		mtdev->devs[i].input_dev=input_allocate_device();
+		if(!mtdev->devs[i].input_dev)
+			return -ENOMEM;
+		pdev=mtdev->devs[i].input_dev;
+		memcpy(pdev->evbit,dev->evbit,sizeof(pdev->evbit));
+		memcpy(pdev->keybit,dev->keybit,sizeof(pdev->keybit));
+		memcpy(pdev->absbit,dev->absbit,sizeof(pdev->absbit));
+
+		memcpy(pdev->abs,dev->abs,sizeof(pdev->abs));
+		memcpy(pdev->absmax,dev->absmax,sizeof(pdev->absmax));
+		memcpy(pdev->absmin,dev->absmin,sizeof(pdev->absmin));
+
+		pdev->name="mtdev virtual input";
+	}
+
+	//create a thread to create the new input devices
+	//because there's a mutex,which may cause dead lock
+	task=kthread_run(mtdev_kt,mtdev,"mtdev thread");
+	if(!task)
+		printk("error !!!!\n");
+	else
+		printk("kthread created OK\n");
+
+
+	err=input_grab_device(&mtdev->handle);
+	if(err)
+	{
+		printk("error in grab device %d\n",err);
+		return err;
+	}
+	else
+		printk("successfully grab device \n");
+
+	wake_up_all(&mtdev->wq);
+	return 0;
+}
+
+static void mtdev_disconnect(struct input_handle *handle)
+{
+	printk("mtdev_disconnect\n");
+	input_release_device(handle);
+}
+
+static const struct input_device_id mtdev_ids[] = {
+	{
+		.flags=INPUT_DEVICE_ID_MATCH_VENDOR|INPUT_DEVICE_ID_MATCH_PRODUCT,
+		.vendor=0x1f87,
+		.product=0x0002,
+	},
+	{
+		.flags=INPUT_DEVICE_ID_MATCH_VENDOR|INPUT_DEVICE_ID_MATCH_PRODUCT,
+		.vendor=0x1f87,
+		.product=0x0001,
+	},
+	{	
+		.flags=INPUT_DEVICE_ID_MATCH_VENDOR|INPUT_DEVICE_ID_MATCH_PRODUCT,
+		.vendor=0x0483,
+		.product=0x3261, 
+	},
+	{
+		.flags=INPUT_DEVICE_ID_MATCH_VENDOR|INPUT_DEVICE_ID_MATCH_PRODUCT,
+		.vendor=0x2087,
+		.product=0x0a01,
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(input,mtdev_ids);
+
+static struct input_handler mtdev_handler = {
+	.event		= mtdev_event,
+	.connect	= mtdev_connect,
+	.disconnect	= mtdev_disconnect,
+	.name		= "mtdev",
+	.id_table	= mtdev_ids,
+};
+
+
+static int __init mtdev_init(void)
+{
+	return input_register_handler(&mtdev_handler);
+}
+
+static void __exit mtdev_exit(void)
+{
+	input_unregister_handler(&mtdev_handler);
+}
+
+module_init(mtdev_init);
+module_exit(mtdev_exit);
+
+MODULE_AUTHOR("He Min <min.he@intel.com>");
+MODULE_DESCRIPTION("Multi-touch input driver event devices");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/input.h b/include/linux/input.h
index 663208a..55bf8bc 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -662,6 +662,7 @@ struct input_absinfo {
 #define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
 #define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
 #define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
+#define ABS_MT_CONTACT_COUNT	0x3b	/* Contact count */
 
 #define ABS_MAX			0x3f
 #define ABS_CNT			(ABS_MAX+1)
-- 
1.6.2.2

