11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2000-2001 Vojtech Pavlik
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Gunze AHL-51S touchscreen driver for Linux
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Should you need to contact me, the author, you can do so either by
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serio.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"Gunze AHL-51S touchscreen driver"
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC);
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Definitions & global arrays.
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	GUNZE_MAX_LENGTH	10
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Per-touchscreen data.
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct gunze {
53eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	struct input_dev *dev;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serio *serio;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int idx;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char data[GUNZE_MAX_LENGTH];
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char phys[32];
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
607d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void gunze_process_packet(struct gunze* gunze)
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
62eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	struct input_dev *dev = gunze->dev;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
66a07461ec0cffb105c7e7b7404520ea2c74129db0Dmitry Torokhov		printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	input_sync(dev);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic irqreturn_t gunze_interrupt(struct serio *serio,
777d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells		unsigned char data, unsigned int flags)
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gunze* gunze = serio_get_drvdata(serio);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data == '\r') {
827d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells		gunze_process_packet(gunze);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gunze->idx = 0;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (gunze->idx < GUNZE_MAX_LENGTH)
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			gunze->data[gunze->idx++] = data;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * gunze_disconnect() is the opposite of gunze_connect()
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void gunze_disconnect(struct serio *serio)
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
97eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	struct gunze *gunze = serio_get_drvdata(serio);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
99eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_get_device(gunze->dev);
100eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_unregister_device(gunze->dev);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_close(serio);
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_set_drvdata(serio, NULL);
103eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_put_device(gunze->dev);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(gunze);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * gunze_connect() is the routine that is called when someone adds a
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * new serio device that supports Gunze protocol and registers it as
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * an input device.
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int gunze_connect(struct serio *serio, struct serio_driver *drv)
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct gunze *gunze;
116eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	struct input_dev *input_dev;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
119eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
120eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev = input_allocate_device();
121eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	if (!gunze || !input_dev) {
122eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov		err = -ENOMEM;
12352c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov		goto fail1;
124eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	}
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gunze->serio = serio;
127eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	gunze->dev = input_dev;
128a21466cc77b25dc2afd1292c79c7fc8fd454a1a7Dmitry Torokhov	snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
130eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->name = "Gunze AHL-51S TouchScreen";
131eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->phys = gunze->phys;
132eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->id.bustype = BUS_RS232;
133eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->id.vendor = SERIO_GUNZE;
134eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->id.product = 0x0051;
135eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_dev->id.version = 0x0100;
136a5394fb075a80212765ee3cd4a7842bdccf5fc0aDmitry Torokhov	input_dev->dev.parent = &serio->dev;
1377b19ada2ed3c1eccb9fe94d74b05e1428224663dJiri Slaby	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
1387b19ada2ed3c1eccb9fe94d74b05e1428224663dJiri Slaby	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
139eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
140eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_set_drvdata(serio, gunze);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = serio_open(serio, drv);
145eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	if (err)
14652c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov		goto fail2;
14752c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov
14852c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov	err = input_register_device(gunze->dev);
14952c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov	if (err)
15052c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov		goto fail3;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
153eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov
15452c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov fail3:	serio_close(serio);
15552c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov fail2:	serio_set_drvdata(serio, NULL);
15652c1f5704d7555a16641429b2e7af5d26d7b119aDmitry Torokhov fail1:	input_free_device(input_dev);
157eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	kfree(gunze);
158eca1ed196cd5b523c1057204cd3672555ad58dfeDmitry Torokhov	return err;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The serio driver structure.
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serio_device_id gunze_serio_ids[] = {
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.type	= SERIO_RS232,
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.proto	= SERIO_GUNZE,
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.id	= SERIO_ANY,
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.extra	= SERIO_ANY,
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0 }
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(serio, gunze_serio_ids);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serio_driver gunze_drv = {
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.driver		= {
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "gunze",
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.description	= DRIVER_DESC,
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table	= gunze_serio_ids,
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.interrupt	= gunze_interrupt,
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.connect	= gunze_connect,
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect	= gunze_disconnect,
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18865ac9f7a23c934ee8c40dc20955e75db4924bfeaAxel Linmodule_serio_driver(gunze_drv);
189