I've written an input driver for the Liyitec PS/2 touch panel. The
patch follows. As this is my first input driver, I'd appreciate any
feedback.
Please cc me in your reply. Cheers,
Shaun
2006-01-31 Shaun Jackman <[email protected]>
* drivers/input/touchscreen/Kconfig (TOUCHSCREEN_LIYITEC): New item.
* drivers/input/touchscreen/Makefile: Add liyitec driver.
* drivers/input/touchscreen/liyitec.c: New file.
* include/linux/serio.h (SERIO_LIYITEC): New constant.
--- linux-2.6.15.orig/drivers/input/touchscreen/Kconfig 2006-01-02
20:21:10.000000000 -0700
+++ linux-2.6.15/drivers/input/touchscreen/Kconfig 2006-01-31
15:56:36.000000000 -0700
@@ -60,6 +60,19 @@
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
--- linux-2.6.15.orig/drivers/input/touchscreen/Makefile 2006-01-02
20:21:10.000000000 -0700
+++ linux-2.6.15/drivers/input/touchscreen/Makefile 2006-01-27
16:01:39.000000000 -0700
@@ -8,6 +8,7 @@
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
--- linux-2.6.15.orig/drivers/input/touchscreen/liyitec.c 1969-12-31
17:00:00.000000000 -0700
+++ linux-2.6.15/drivers/input/touchscreen/liyitec.c 2006-01-31
15:06:49.000000000 -0700
@@ -0,0 +1,188 @@
+/* Liyitec PS/2 touch panel 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 touch panel 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_ENABLE 0xf4
+#define LIYITEC_CMD_SETSTREAM 0xea
+#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)
+ printk(KERN_DEBUG "liyitec: expected ACK: 0x%02x\n", data);
+ } else
+ liyitec->data[liyitec->count] = data;
+ liyitec->count++;
+
+ if (liyitec->count == 1 && !(data & LIYITEC_SYNC)) {
+ printk(KERN_DEBUG "liyitec: 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);
+ printk(KERN_DEBUG "liyitec: (%d,%d)\n", x, y); //xxx
+ }
+ input_report_key(dev, BTN_TOUCH, liyitec->data[0] & LIYITEC_TOUCH);
+ input_report_key(dev, BTN_RIGHT, liyitec->data[0] & LIYITEC_RIGHT);
+ printk(KERN_DEBUG "liyitec: %02x\n", liyitec->data[0]); //xxx
+ 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 touch screen";
+ 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)
+{
+ printk(KERN_INFO "liyitec: %s\n", __func__);
+ serio_unregister_driver(&liyitec_drv);
+}
+
+module_init(liyitec_init);
+module_exit(liyitec_exit);
--- linux-2.6.15.orig/include/linux/serio.h 2006-01-02 20:21:10.000000000 -0700
+++ linux-2.6.15/include/linux/serio.h 2006-01-27 16:57:57.000000000 -0700
@@ -216,5 +216,6 @@
#define SERIO_LKKBD 0x28
#define SERIO_ELO 0x29
#define SERIO_MICROTOUCH 0x30
+#define SERIO_LIYITEC 0x31
#endif
On Tue, Jan 31, 2006 at 03:59:20PM -0700, Shaun Jackman wrote:
> I've written an input driver for the Liyitec PS/2 touch panel. The
> patch follows. As this is my first input driver, I'd appreciate any
> feedback.
> 2006-01-31 Shaun Jackman <[email protected]>
>
> * drivers/input/touchscreen/Kconfig (TOUCHSCREEN_LIYITEC): New item.
> * drivers/input/touchscreen/Makefile: Add liyitec driver.
> * drivers/input/touchscreen/liyitec.c: New file.
> * include/linux/serio.h (SERIO_LIYITEC): New constant.
This is not ChangeLog style Linux is using.
> +static void liyitec_disconnect(struct serio *serio)
> +{
> + struct liyitec *liyitec = serio_get_drvdata(serio);
> +
> + input_get_device(liyitec->dev);
What do you want to do with return value?
> + input_unregister_device(liyitec->dev);
> + serio_close(serio);
> + serio_set_drvdata(serio, NULL);
> + input_put_device(liyitec->dev);
> + kfree(liyitec);
> +}
> +static void __exit liyitec_exit(void)
> +{
> + printk(KERN_INFO "liyitec: %s\n", __func__);
I suggest to drop this line.
> + serio_unregister_driver(&liyitec_drv);
> +}
>Shaun Jackman wrote:
>I've written an input driver for the Liyitec PS/2 touch panel. The
>patch follows. As this is my first input driver, I'd appreciate any
>feedback.
>
>Please cc me in your reply. Cheers,
>Shaun
[snip]
>+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)
>+ printk(KERN_DEBUG "liyitec: expected ACK: 0x%02x\n", data);
>+ } else
>+ liyitec->data[liyitec->count] = data;
>+ liyitec->count++;
>+
>+ if (liyitec->count == 1 && !(data & LIYITEC_SYNC)) {
>+ printk(KERN_DEBUG "liyitec: unsynchronized data: 0x%02x\n", data);
It would be great to use dev_*() rather than printks.
dev_dbg(&serio->dev, "unsynchronized data: 0x%02x\n", data);
for instance.
regards,
--
Jiri Slaby http://www.fi.muni.cz/~xslaby
~\-/~ [email protected] ~\-/~
B67499670407CE62ACC8 22A032CC55C339D47A7E
On Tuesday 31 January 2006 17:59, Shaun Jackman wrote:
> +
> +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 touch screen";
> + 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);
> +
Hi Shaun,
Thank you for adding support for the new touchscreen, however I have a
couple of questions:
What stops this driver form binding to serio ports that have devices other
that Liyitec touchscreen connected to them? Such as keyboard port when
keyboard works in non-translated mode or regular AUX port with standard
PS/2 mouse? Is there a way to query for presence of the touchscreen?
Moreover this driver should be integrated into psmouse so proper protocol
is selected automatically.
--
Dmitry
On 1/31/06, Alexey Dobriyan <[email protected]> wrote:
> > +static void liyitec_disconnect(struct serio *serio)
> > +{
> > + struct liyitec *liyitec = serio_get_drvdata(serio);
> > +
> > + input_get_device(liyitec->dev);
>
> What do you want to do with return value?
As far as I know, input_get_device returns its argument. So, I don't
want anything with the return value. input_get_device does increment
the reference count of the underlying kobject though. Am I correct
here?
> > +static void __exit liyitec_exit(void)
> > +{
> > + printk(KERN_INFO "liyitec: %s\n", __func__);
>
> I suggest to drop this line.
Right. Will do.
Please cc me in your reply. Cheers,
Shaun
On 1/31/06, Jiri Slaby <[email protected]> wrote:
> It would be great to use dev_*() rather than printks.
> dev_dbg(&serio->dev, "unsynchronized data: 0x%02x\n", data);
> for instance.
Thanks, Jiri. Will do.
Cheers,
Shaun
On 1/31/06, Dmitry Torokhov <[email protected]> wrote:
> Hi Shaun,
>
> Thank you for adding support for the new touchscreen, however I have a
> couple of questions:
>
> What stops this driver form binding to serio ports that have devices other
> that Liyitec touchscreen connected to them? Such as keyboard port when
> keyboard works in non-translated mode or regular AUX port with standard
> PS/2 mouse?
Nothing is preventing it from seizing other PS/2 ports, as far as I
know. I believe it's working for me right now because the keyboard
grabs the keyboard PS/2 port first, and then the liyitec driver grabs
the rest of the ports, which, in my case, is only the psaux port which
is connected to the liyitec screen.
> Is there a way to query for presence of the touchscreen?
I'm not sure. I'll find out what the touchscreen's response to the
PS/2 GETID command is.
> Moreover this driver should be integrated into psmouse so proper protocol
> is selected automatically.
In a private mail to me, Vojtech Pavlik pointed out that lifebook.c is
a PS/2 touch screen that does exactly this, and in only 136 lines of
code. I'll see if I can adapt my existing serio driver to be a psmouse
driver.
Thanks for your comments, Dmitry. Cheers,
Shaun
On 1/31/06, Dmitry Torokhov <[email protected]> wrote:
> What stops this driver form binding to serio ports that have devices other
> that Liyitec touchscreen connected to them? Such as keyboard port when
> keyboard works in non-translated mode or regular AUX port with standard
> PS/2 mouse? Is there a way to query for presence of the touchscreen?
>
> Moreover this driver should be integrated into psmouse so proper protocol
> is selected automatically.
I've just posted an updated version of my Liyitec driver that
incorporates the trivial changes suggested by others on this list.
This driver is quite useful in its current state, as it does allow a
Liyitec touch screen to be used, which currently has no support at
all. It does however have a few outstanding issues.
1. The Liyitec driver will grab all unclaimed PS/2 devices.
2. It does this because there is no probing and identification code.
3. This driver would probably be better off as a psmouse driver rather
than a serio driver, but this would first require the missing probe
code.
It should certainly be possible to detect the presence of a Liyitec
touch screen, but I haven't figured out how yet. Trivially, the
Liyitec touch screen appears to be a PS/2 mouse that happens to be
returning very unusual coordinates.
Cheers,
Shaun