[PATCH] liyitec: Liyitec PS/2 touchscreen driver
Add an input driver for the Liyitec PS/2 touchscreen.
Signed-off-by: Shaun Jackman <[email protected]>
diff --git a/drivers/input/touchscreen/Kconfig
b/drivers/input/touchscreen/Kconfig
index b1b14f8..392cce2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -73,6 +73,19 @@ config TOUCHSCREEN_ELO
To compile this driver as a module, choose M here: the
module will be called elo.
+config TOUCHSCREEN_LIYITEC
+ tristate "Lyitec PS/2 touchscreen"
+ select SERIO
+ select SERIO_I8042 if X86_PC
+ help
+ Say Y here if you have a Liyitec PS/2 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called liyitec.
+
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile
b/drivers/input/touchscreen/Makefile
index 5e5557c..001c97c 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600
obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_LIYITEC) += liyitec.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
diff --git a/drivers/input/touchscreen/liyitec.c
b/drivers/input/touchscreen/liyitec.c
new file mode 100644
index 0000000..d262d2d
--- /dev/null
+++ b/drivers/input/touchscreen/liyitec.c
@@ -0,0 +1,185 @@
+/* Liyitec PS/2 touchscreen driver
+ * Written by Shaun Jackman <[email protected]>.
+ * Copyright 2006 Pathway Connectivity
+ *
+ * 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.
+ *
+ * This driver was derived from mtouch.c.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Liyitec PS/2 touchscreen driver"
+
+MODULE_AUTHOR("Shaun Jackman <[email protected]>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define LIYITEC_MAX_X 0x3ff
+#define LIYITEC_MAX_Y 0x3ff
+
+#define LIYITEC_Y9 0x80
+#define LIYITEC_X9 0x40
+#define LIYITEC_Y8 0x20
+#define LIYITEC_X8 0x10
+#define LIYITEC_SYNC 0x08
+#define LIYITEC_ABS 0x04
+#define LIYITEC_RIGHT 0x02
+#define LIYITEC_TOUCH 0x01
+
+#define LIYITEC_PACKET_SIZE 3
+#define LIYITEC_CMD_SETSTREAM 0xea
+#define LIYITEC_CMD_ENABLE 0xf4
+#define LIYITEC_RET_ACK 0xfa
+
+struct liyitec {
+ struct input_dev *dev;
+ struct serio *serio;
+ char phys[32];
+ signed char count;
+ unsigned char data[LIYITEC_PACKET_SIZE];
+};
+
+static irqreturn_t liyitec_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct liyitec *liyitec = serio_get_drvdata(serio);
+
+ if (liyitec->count < 0) {
+ if (data != LIYITEC_RET_ACK)
+ dev_dbg(&serio->dev, "expected ACK: 0x%02x\n", data);
+ } else
+ liyitec->data[liyitec->count] = data;
+ liyitec->count++;
+
+ if (liyitec->count == 1 && !(data & LIYITEC_SYNC)) {
+ dev_dbg(&serio->dev, "unsynchronized data: 0x%02x\n", data);
+ liyitec->count = 0;
+ }
+ else if (liyitec->count == LIYITEC_PACKET_SIZE) {
+ struct input_dev *dev = liyitec->dev;
+ input_regs(dev, regs);
+ if (liyitec->data[0] & LIYITEC_ABS) {
+ unsigned x = liyitec->data[1] +
+ (liyitec->data[0] & LIYITEC_X8 ? 0x100 : 0) +
+ (liyitec->data[0] & LIYITEC_X9 ? 0x200 : 0);
+ unsigned y = liyitec->data[2] +
+ (liyitec->data[0] & LIYITEC_Y8 ? 0x100 : 0) +
+ (liyitec->data[0] & LIYITEC_Y9 ? 0x200 : 0);
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ }
+ input_report_key(dev, BTN_TOUCH, liyitec->data[0] & LIYITEC_TOUCH);
+ input_report_key(dev, BTN_RIGHT, liyitec->data[0] & LIYITEC_RIGHT);
+ input_sync(dev);
+ liyitec->count = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int liyitec_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct liyitec *liyitec;
+ struct input_dev *input_dev;
+ int retval;
+
+ liyitec = kzalloc(sizeof *liyitec, GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (liyitec == NULL || input_dev == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ liyitec->serio = serio;
+ liyitec->dev = input_dev;
+ sprintf(liyitec->phys, "%s/input0", serio->phys);
+
+ input_dev->private = liyitec;
+ input_dev->name = "Liyitec PS/2 touchscreen";
+ input_dev->phys = liyitec->phys;
+ input_dev->id.bustype = BUS_I8042;
+ input_dev->id.vendor = SERIO_LIYITEC;
+ 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_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_RIGHT);
+ input_set_abs_params(liyitec->dev, ABS_X, 0, LIYITEC_MAX_X, 0, 0);
+ input_set_abs_params(liyitec->dev, ABS_Y, 0, LIYITEC_MAX_Y, 0, 0);
+
+ serio_set_drvdata(serio, liyitec);
+ retval = serio_open(serio, drv);
+ if (retval)
+ goto out;
+ input_register_device(liyitec->dev);
+
+ liyitec->count = -2;
+ retval = serio_write(serio, LIYITEC_CMD_SETSTREAM);
+ if (retval)
+ goto out;
+ retval = serio_write(serio, LIYITEC_CMD_ENABLE);
+
+out:
+ if (retval) {
+ serio_set_drvdata(serio, NULL);
+ input_free_device(input_dev);
+ kfree(liyitec);
+ }
+ return retval;
+}
+
+static void liyitec_disconnect(struct serio *serio)
+{
+ struct liyitec *liyitec = serio_get_drvdata(serio);
+
+ input_get_device(liyitec->dev);
+ input_unregister_device(liyitec->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(liyitec->dev);
+ kfree(liyitec);
+}
+
+static struct serio_device_id liyitec_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, liyitec_serio_ids);
+
+static struct serio_driver liyitec_drv = {
+ .driver = {
+ .name = "liyitec",
+ },
+ .description = DRIVER_DESC,
+ .id_table = liyitec_serio_ids,
+ .interrupt = liyitec_interrupt,
+ .connect = liyitec_connect,
+ .disconnect = liyitec_disconnect,
+};
+
+static int __init liyitec_init(void)
+{
+ printk(KERN_INFO "liyitec: %s\n", DRIVER_DESC);
+ serio_register_driver(&liyitec_drv);
+ return 0;
+}
+
+static void __exit liyitec_exit(void)
+{
+ serio_unregister_driver(&liyitec_drv);
+}
+
+module_init(liyitec_init);
+module_exit(liyitec_exit);
diff --git a/include/linux/serio.h b/include/linux/serio.h
index aa4d649..199bd34 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -216,5 +216,6 @@ static inline void serio_unpin_driver(st
#define SERIO_LKKBD 0x28
#define SERIO_ELO 0x29
#define SERIO_MICROTOUCH 0x30
+#define SERIO_LIYITEC 0x31
#endif
On 2/2/06, Shaun Jackman <[email protected]> wrote:
> [PATCH] liyitec: Liyitec PS/2 touchscreen driver
>
> Add an input driver for the Liyitec PS/2 touchscreen.
>
I don't see any suibstantial differences from the older patch. I think
it should be integrated into psmouse. Is there a way to query the
device? What kind of boxes use this touchscreen? Maybe using DMI is an
option, like lifebook does?
--
Dmitry
On 2/2/06, Dmitry Torokhov <[email protected]> wrote:
> On 2/2/06, Shaun Jackman <[email protected]> wrote:
> > [PATCH] liyitec: Liyitec PS/2 touchscreen driver
> >
> > Add an input driver for the Liyitec PS/2 touchscreen.
>
> I don't see any suibstantial differences from the older patch. I think
> it should be integrated into psmouse. Is there a way to query the
> device? What kind of boxes use this touchscreen? Maybe using DMI is an
> option, like lifebook does?
I supplied some commentary in another email. I wasn't entirely sure
how to separate some free commentary from the ChangeLog entry in a
[PATCH] post. Sorry for the confusion.
The system I have, an Advantech touchscreen computer, has no DMI
information. So that option is out, at least in my case.
# ./dmidecode
# dmidecode 2.7
# No SMBIOS nor DMI entry point found, sorry.
I agree this driver would be best as a psmouse drive, but some
reliable detection code is necessary first. The serio driver does work
as is. If the user loads the liyitec.ko module or compiles the driver
into the kernel, she likely knows what she's doing.
I suspect there exists a detection routine using PS/2 commands that
will work, but I haven't figured one out yet.
Cheers,
Shaun
On 2/2/06, Dmitry Torokhov <[email protected]> wrote:
> On 2/2/06, Shaun Jackman <[email protected]> wrote:
> > [PATCH] liyitec: Liyitec PS/2 touchscreen driver
> >
> > Add an input driver for the Liyitec PS/2 touchscreen.
>
> I don't see any suibstantial differences from the older patch. I think
> it should be integrated into psmouse. Is there a way to query the
> device? What kind of boxes use this touchscreen? Maybe using DMI is an
> option, like lifebook does?
I've ran a number of experiments to attempt to detect the Liyitec
touch screen. I sent every PS/2 command from 0x00 to 0xff. It responds
in every way as a normal PS/2 mouse. To commands it doesn't
understand, it first responds with 0xfe (NAK), then if it also doesn't
understand the next command it responds with 0xfc (ERR).
I've tried setting every resolution (PS/2 command 0xe8) from 0x00 to
0xff. It responds to 0x00-0x03 with ACK. It responds to 0x04-0xff with
NAK/ERR.
I've tried setting every rate (PS/2 command 0xf3) from 0x00 to 0xff.
It responds to (10, 20, 40, 60, 80, 100, 200) with ACK. It responds to
every other rate with NAK/ERR.
I've even tried sending every valid rate "knock" sequence of length 3,
of which there are 7^3=343, and tried a get info (PS/2 command 0xe9)
and get ID (PS/2 command 0xf2). The get info command never returns
anything unusual, and the get ID command always returns 0 (standard
PS/2 mouse).
Liyitec provides a binary X11 driver for the touch screen. I set the
kernel to dump every byte going to and coming from /dev/psaux and ran
their binary. Their driver sends (enable, scale11, enable, rate, 200,
resolution, 2, enable) to /dev/psaux and nothing more.
So, after some heavy experimentation, I am now rather confident that
there is no trivial way to detect the touch panel's existence.
Although, I would love to hear any suggestions. If anyone's interested
in seeing the raw data, I'd be happy to send it on. In a previous
mail, I mentioned my particular hardware does not have any DMI data,
like the lifebook driver uses.
So, finally, assuming there is no way to detect the touch panel's
presence, should the driver still be rolled into psmouse? If so, how
should the user specify she wishes to use the liyitec driver, and
which serio (PS/2) port the liyitec touch screen is on?
My current patch implements the Liyitec driver as a serio driver that
grabs every available PS/2 port. Quite unfriendly, but works in the
typical case where the keyboard driver grabs the first port, and the
Liyitec driver grabs the psaux port.
Cheers,
Shaun
On Fri, Feb 03, 2006 at 05:39:30PM -0700, Shaun Jackman wrote:
> So, finally, assuming there is no way to detect the touch panel's
> presence, should the driver still be rolled into psmouse? If so, how
> should the user specify she wishes to use the liyitec driver, and
> which serio (PS/2) port the liyitec touch screen is on?
>
> My current patch implements the Liyitec driver as a serio driver that
> grabs every available PS/2 port. Quite unfriendly, but works in the
> typical case where the keyboard driver grabs the first port, and the
> Liyitec driver grabs the psaux port.
I hope the driver at least checks that the port is untranslated
(SERIO_I8042, not SERIO_I8042_XL), and that the device behaves as a
mouse.
Btw, I assume the packet format is also indistinguishable from a
standard PS/2 mouse?
If it were integrated into psmouse, it probably need to be enabled by a
module parameter.
--
Vojtech Pavlik
SuSE Labs, SuSE CR
On 2/4/06, Vojtech Pavlik <[email protected]> wrote:
> On Fri, Feb 03, 2006 at 05:39:30PM -0700, Shaun Jackman wrote:
> > So, finally, assuming there is no way to detect the touch panel's
> > presence, should the driver still be rolled into psmouse? If so, how
> > should the user specify she wishes to use the liyitec driver, and
> > which serio (PS/2) port the liyitec touch screen is on?
> >
> > My current patch implements the Liyitec driver as a serio driver that
> > grabs every available PS/2 port. Quite unfriendly, but works in the
> > typical case where the keyboard driver grabs the first port, and the
> > Liyitec driver grabs the psaux port.
>
> I hope the driver at least checks that the port is untranslated
> (SERIO_I8042, not SERIO_I8042_XL), and that the device behaves as a
> mouse.
I'm unable to find any reference to SERIO_I8042_XL.
$ cat .git/HEAD
d39569232e883a1475b39576b96cfb400c33e816
$ grep -r I8042_XL .
$
The driver does not check that the device behaves as a normal mouse.
It would be relatively straight forward to implement this feature, but
since psmouse already does this, this behaviour would be a major
benefit to moving the Liyitec driver into psmouse.
> Btw, I assume the packet format is also indistinguishable from a
> standard PS/2 mouse?
The Liyitec packet is always a legal PS/2 packet. However, the
overflow bits are used as the tenth bits of precision of the absolute
X and Y coordinates, and so are set far more often than on a normal
PS/2 mouse, assuming the user touches the bottom right corner of the
screen.
> If it were integrated into psmouse, it probably need to be enabled by a
> module parameter.
Okay. I'll do that then. How is the module parameter specified for a
driver that's compiled into the kernel?
Cheers,
Shaun