summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig36
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/elo.c167
-rw-r--r--drivers/input/touchscreen/penmount.c185
-rw-r--r--drivers/input/touchscreen/touchright.c196
-rw-r--r--drivers/input/touchscreen/touchwin.c203
6 files changed, 756 insertions, 34 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b1b14f8d4dd6..9418bbe47072 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -108,4 +108,40 @@ config TOUCHSCREEN_HP600
To compile this driver as a module, choose M here: the
module will be called hp680_ts_input.
+config TOUCHSCREEN_PENMOUNT
+ tristate "Penmount serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Penmount serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called penmount.
+
+config TOUCHSCREEN_TOUCHRIGHT
+ tristate "Touchright serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchright serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchwin serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchwin.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5e5557c43121..1abb8f10d608 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,3 +12,6 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
index c86a2eb310fd..ab565335ee44 100644
--- a/drivers/input/touchscreen/elo.c
+++ b/drivers/input/touchscreen/elo.c
@@ -23,6 +23,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
+#include <linux/ctype.h>
#define DRIVER_DESC "Elo serial touchscreen driver"
@@ -34,7 +35,19 @@ MODULE_LICENSE("GPL");
* Definitions & global arrays.
*/
-#define ELO_MAX_LENGTH 10
+#define ELO_MAX_LENGTH 10
+
+#define ELO10_PACKET_LEN 8
+#define ELO10_TOUCH 0x03
+#define ELO10_PRESSURE 0x80
+
+#define ELO10_LEAD_BYTE 'U'
+
+#define ELO10_ID_CMD 'i'
+
+#define ELO10_TOUCH_PACKET 'T'
+#define ELO10_ACK_PACKET 'A'
+#define ELI10_ID_PACKET 'I'
/*
* Per-touchscreen data.
@@ -43,51 +56,67 @@ MODULE_LICENSE("GPL");
struct elo {
struct input_dev *dev;
struct serio *serio;
+ struct mutex cmd_mutex;
+ struct completion cmd_done;
int id;
int idx;
+ unsigned char expected_packet;
unsigned char csum;
unsigned char data[ELO_MAX_LENGTH];
+ unsigned char response[ELO10_PACKET_LEN];
char phys[32];
};
-static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
- elo->csum += elo->data[elo->idx] = data;
-
+ elo->data[elo->idx] = data;
switch (elo->idx++) {
-
case 0:
- if (data != 'U') {
+ elo->csum = 0xaa;
+ if (data != ELO10_LEAD_BYTE) {
+ pr_debug("elo: unsynchronized data: 0x%02x\n", data);
elo->idx = 0;
- elo->csum = 0;
- }
- break;
-
- case 1:
- if (data != 'T') {
- elo->idx = 0;
- elo->csum = 0;
}
break;
case 9:
- if (elo->csum) {
+ elo->idx = 0;
+ if (data != elo->csum) {
+ pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n",
+ data, elo->csum);
+ break;
+ }
+ if (elo->data[1] != elo->expected_packet) {
+ if (elo->data[1] != ELO10_TOUCH_PACKET)
+ pr_debug("elo: unexpected packet: 0x%02x\n",
+ elo->data[1]);
+ break;
+ }
+ if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
input_regs(dev, regs);
input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
- input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]);
- input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]);
+ if (elo->data[2] & ELO10_PRESSURE)
+ input_report_abs(dev, ABS_PRESSURE,
+ (elo->data[8] << 8) | elo->data[7]);
+ input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
input_sync(dev);
+ } else if (elo->data[1] == ELO10_ACK_PACKET) {
+ if (elo->data[2] == '0')
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ complete(&elo->cmd_done);
+ } else {
+ memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+ elo->expected_packet = ELO10_ACK_PACKET;
}
- elo->idx = 0;
- elo->csum = 0;
break;
}
+ elo->csum += data;
}
-static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
@@ -135,7 +164,7 @@ static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_re
}
}
-static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
@@ -161,7 +190,7 @@ static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_re
static irqreturn_t elo_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
- struct elo* elo = serio_get_drvdata(serio);
+ struct elo *elo = serio_get_drvdata(serio);
switch(elo->id) {
case 0:
@@ -181,17 +210,81 @@ static irqreturn_t elo_interrupt(struct serio *serio,
return IRQ_HANDLED;
}
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+ int rc = -1;
+ int i;
+ unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+ mutex_lock(&elo->cmd_mutex);
+
+ serio_pause_rx(elo->serio);
+ elo->expected_packet = toupper(packet[0]);
+ init_completion(&elo->cmd_done);
+ serio_continue_rx(elo->serio);
+
+ if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+ goto out;
+
+ for (i = 0; i < ELO10_PACKET_LEN; i++) {
+ csum += packet[i];
+ if (serio_write(elo->serio, packet[i]))
+ goto out;
+ }
+
+ if (serio_write(elo->serio, csum))
+ goto out;
+
+ wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+ if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+ /* We are back in reporting mode, the command was ACKed */
+ memcpy(packet, elo->response, ELO10_PACKET_LEN);
+ rc = 0;
+ }
+
+ out:
+ mutex_unlock(&elo->cmd_mutex);
+ return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+ static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+ struct input_dev *dev = elo->dev;
+ unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+ if (elo_command_10(elo, packet))
+ return -1;
+
+ dev->id.version = (packet[5] << 8) | packet[4];
+
+ input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+ if (packet[3] & ELO10_PRESSURE)
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+ printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, "
+ "features: %x02x, controller: 0x%02x\n",
+ elo_types[(packet[1] -'0') & 0x03],
+ packet[5], packet[4], packet[3], packet[7]);
+
+ return 0;
+}
+
/*
* elo_disconnect() is the opposite of elo_connect()
*/
static void elo_disconnect(struct serio *serio)
{
- struct elo* elo = serio_get_drvdata(serio);
+ struct elo *elo = serio_get_drvdata(serio);
+ input_get_device(elo->dev);
input_unregister_device(elo->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
+ input_put_device(elo->dev);
kfree(elo);
}
@@ -211,12 +304,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
input_dev = input_allocate_device();
if (!elo || !input_dev) {
err = -ENOMEM;
- goto fail;
+ goto fail1;
}
elo->serio = serio;
elo->id = serio->id.id;
elo->dev = input_dev;
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ mutex_init(&elo->cmd_mutex);
+ init_completion(&elo->cmd_done);
snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
input_dev->private = elo;
@@ -231,12 +327,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ serio_set_drvdata(serio, elo);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
switch (elo->id) {
case 0: /* 10-byte protocol */
- input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ if (elo_setup_10(elo))
+ goto fail3;
+
break;
case 1: /* 6-byte protocol */
@@ -253,17 +354,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
break;
}
- serio_set_drvdata(serio, elo);
-
- err = serio_open(serio, drv);
+ err = input_register_device(elo->dev);
if (err)
- goto fail;
+ goto fail3;
- input_register_device(elo->dev);
return 0;
- fail: serio_set_drvdata(serio, NULL);
- input_free_device(input_dev);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
kfree(elo);
return err;
}
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644
index 000000000000..f7370109d43e
--- /dev/null
+++ b/drivers/input/touchscreen/penmount.c
@@ -0,0 +1,185 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Penmount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define PM_MAX_LENGTH 5
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[PM_MAX_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+ struct input_dev *dev = pm->dev;
+
+ pm->data[pm->idx] = data;
+
+ if (pm->data[0] & 0x80) {
+ if (PM_MAX_LENGTH == ++pm->idx) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]);
+ input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]);
+ input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+ input_sync(dev);
+ pm->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ input_get_device(pm->dev);
+ input_unregister_device(pm->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pm->dev);
+ kfree(pm);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct pm *pm;
+ struct input_dev *input_dev;
+ int err;
+
+ pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pm->serio = serio;
+ pm->dev = input_dev;
+ snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+
+ input_dev->private = pm;
+ input_dev->name = "Penmount Serial TouchScreen";
+ input_dev->phys = pm->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_PENMOUNT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->cdev.dev = &serio->dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0);
+ input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0);
+
+ serio_set_drvdata(serio, pm);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pm->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PENMOUNT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+ .driver = {
+ .name = "penmountlpc",
+ },
+ .description = DRIVER_DESC,
+ .id_table = pm_serio_ids,
+ .interrupt = pm_interrupt,
+ .connect = pm_connect,
+ .disconnect = pm_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init pm_init(void)
+{
+ serio_register_driver(&pm_drv);
+ return 0;
+}
+
+static void __exit pm_exit(void)
+{
+ serio_unregister_driver(&pm_drv);
+}
+
+module_init(pm_init);
+module_exit(pm_exit);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644
index 000000000000..1c89fa538651
--- /dev/null
+++ b/drivers/input/touchscreen/touchright.c
@@ -0,0 +1,196 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT 0x01
+#define TR_FORMAT_STATUS_BYTE 0x40
+#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[TR_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+ struct input_dev *dev = tr->dev;
+
+ tr->data[tr->idx] = data;
+
+ if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+ if (++tr->idx == TR_LENGTH) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X,
+ (tr->data[1] << 5) | (tr->data[2] >> 1));
+ input_report_abs(dev, ABS_Y,
+ (tr->data[3] << 5) | (tr->data[4] >> 1));
+ input_report_key(dev, BTN_TOUCH,
+ tr->data[0] & TR_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ tr->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+
+ input_get_device(tr->dev);
+ input_unregister_device(tr->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tr->dev);
+ kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tr *tr;
+ struct input_dev *input_dev;
+ int err;
+
+ tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tr || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tr->serio = serio;
+ tr->dev = input_dev;
+ snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+ input_dev->private = tr;
+ input_dev->name = "Touchright Serial TouchScreen";
+ input_dev->phys = tr->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHRIGHT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+ input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tr);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tr->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tr);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHRIGHT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+ .driver = {
+ .name = "touchright",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tr_serio_ids,
+ .interrupt = tr_interrupt,
+ .connect = tr_connect,
+ .disconnect = tr_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tr_init(void)
+{
+ serio_register_driver(&tr_drv);
+ return 0;
+}
+
+static void __exit tr_exit(void)
+{
+ serio_unregister_driver(&tr_drv);
+}
+
+module_init(tr_init);
+module_exit(tr_exit);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644
index 000000000000..a7b4c755958e
--- /dev/null
+++ b/drivers/input/touchscreen/touchwin.c
@@ -0,0 +1,203 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ * The Touchwindow I used is made by Edmark Corp. and
+ * constantly outputs a stream of 0's unless it is touched.
+ * It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ int touched;
+ unsigned char data[TW_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+ struct input_dev *dev = tw->dev;
+
+ if (data) { /* touch */
+ tw->touched = 1;
+ tw->data[tw->idx++] = data;
+ /* verify length and that the two Y's are the same */
+ if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X, tw->data[0]);
+ input_report_abs(dev, ABS_Y, tw->data[1]);
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ tw->idx = 0;
+ }
+ } else if (tw->touched) { /* untouch */
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ tw->idx = 0;
+ tw->touched = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+
+ input_get_device(tw->dev);
+ input_unregister_device(tw->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tw->dev);
+ kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tw *tw;
+ struct input_dev *input_dev;
+ int err;
+
+ tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tw || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tw->serio = serio;
+ tw->dev = input_dev;
+ snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+ input_dev->private = tw;
+ input_dev->name = "Touchwindow Serial TouchScreen";
+ input_dev->phys = tw->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHWIN;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+ input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tw);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tw->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tw);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHWIN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+ .driver = {
+ .name = "touchwin",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tw_serio_ids,
+ .interrupt = tw_interrupt,
+ .connect = tw_connect,
+ .disconnect = tw_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tw_init(void)
+{
+ serio_register_driver(&tw_drv);
+ return 0;
+}
+
+static void __exit tw_exit(void)
+{
+ serio_unregister_driver(&tw_drv);
+}
+
+module_init(tw_init);
+module_exit(tw_exit);