162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) 2000-2001 Vojtech Pavlik
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * Gunze AHL-51S touchscreen driver for Linux
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/input.h>
1562306a36Sopenharmony_ci#include <linux/serio.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define DRIVER_DESC	"Gunze AHL-51S touchscreen driver"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
2062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
2162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Definitions & global arrays.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define	GUNZE_MAX_LENGTH	10
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Per-touchscreen data.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct gunze {
3462306a36Sopenharmony_ci	struct input_dev *dev;
3562306a36Sopenharmony_ci	struct serio *serio;
3662306a36Sopenharmony_ci	int idx;
3762306a36Sopenharmony_ci	unsigned char data[GUNZE_MAX_LENGTH];
3862306a36Sopenharmony_ci	char phys[32];
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void gunze_process_packet(struct gunze *gunze)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct input_dev *dev = gunze->dev;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
4662306a36Sopenharmony_ci		(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
4762306a36Sopenharmony_ci		printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
5262306a36Sopenharmony_ci	input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
5362306a36Sopenharmony_ci	input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
5462306a36Sopenharmony_ci	input_sync(dev);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic irqreturn_t gunze_interrupt(struct serio *serio,
5862306a36Sopenharmony_ci		unsigned char data, unsigned int flags)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct gunze *gunze = serio_get_drvdata(serio);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (data == '\r') {
6362306a36Sopenharmony_ci		gunze_process_packet(gunze);
6462306a36Sopenharmony_ci		gunze->idx = 0;
6562306a36Sopenharmony_ci	} else {
6662306a36Sopenharmony_ci		if (gunze->idx < GUNZE_MAX_LENGTH)
6762306a36Sopenharmony_ci			gunze->data[gunze->idx++] = data;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	return IRQ_HANDLED;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * gunze_disconnect() is the opposite of gunze_connect()
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void gunze_disconnect(struct serio *serio)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct gunze *gunze = serio_get_drvdata(serio);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	input_get_device(gunze->dev);
8162306a36Sopenharmony_ci	input_unregister_device(gunze->dev);
8262306a36Sopenharmony_ci	serio_close(serio);
8362306a36Sopenharmony_ci	serio_set_drvdata(serio, NULL);
8462306a36Sopenharmony_ci	input_put_device(gunze->dev);
8562306a36Sopenharmony_ci	kfree(gunze);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * gunze_connect() is the routine that is called when someone adds a
9062306a36Sopenharmony_ci * new serio device that supports Gunze protocol and registers it as
9162306a36Sopenharmony_ci * an input device.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int gunze_connect(struct serio *serio, struct serio_driver *drv)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct gunze *gunze;
9762306a36Sopenharmony_ci	struct input_dev *input_dev;
9862306a36Sopenharmony_ci	int err;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
10162306a36Sopenharmony_ci	input_dev = input_allocate_device();
10262306a36Sopenharmony_ci	if (!gunze || !input_dev) {
10362306a36Sopenharmony_ci		err = -ENOMEM;
10462306a36Sopenharmony_ci		goto fail1;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	gunze->serio = serio;
10862306a36Sopenharmony_ci	gunze->dev = input_dev;
10962306a36Sopenharmony_ci	snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	input_dev->name = "Gunze AHL-51S TouchScreen";
11262306a36Sopenharmony_ci	input_dev->phys = gunze->phys;
11362306a36Sopenharmony_ci	input_dev->id.bustype = BUS_RS232;
11462306a36Sopenharmony_ci	input_dev->id.vendor = SERIO_GUNZE;
11562306a36Sopenharmony_ci	input_dev->id.product = 0x0051;
11662306a36Sopenharmony_ci	input_dev->id.version = 0x0100;
11762306a36Sopenharmony_ci	input_dev->dev.parent = &serio->dev;
11862306a36Sopenharmony_ci	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
11962306a36Sopenharmony_ci	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
12062306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
12162306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	serio_set_drvdata(serio, gunze);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	err = serio_open(serio, drv);
12662306a36Sopenharmony_ci	if (err)
12762306a36Sopenharmony_ci		goto fail2;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = input_register_device(gunze->dev);
13062306a36Sopenharmony_ci	if (err)
13162306a36Sopenharmony_ci		goto fail3;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci fail3:	serio_close(serio);
13662306a36Sopenharmony_ci fail2:	serio_set_drvdata(serio, NULL);
13762306a36Sopenharmony_ci fail1:	input_free_device(input_dev);
13862306a36Sopenharmony_ci	kfree(gunze);
13962306a36Sopenharmony_ci	return err;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * The serio driver structure.
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic const struct serio_device_id gunze_serio_ids[] = {
14762306a36Sopenharmony_ci	{
14862306a36Sopenharmony_ci		.type	= SERIO_RS232,
14962306a36Sopenharmony_ci		.proto	= SERIO_GUNZE,
15062306a36Sopenharmony_ci		.id	= SERIO_ANY,
15162306a36Sopenharmony_ci		.extra	= SERIO_ANY,
15262306a36Sopenharmony_ci	},
15362306a36Sopenharmony_ci	{ 0 }
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(serio, gunze_serio_ids);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct serio_driver gunze_drv = {
15962306a36Sopenharmony_ci	.driver		= {
16062306a36Sopenharmony_ci		.name	= "gunze",
16162306a36Sopenharmony_ci	},
16262306a36Sopenharmony_ci	.description	= DRIVER_DESC,
16362306a36Sopenharmony_ci	.id_table	= gunze_serio_ids,
16462306a36Sopenharmony_ci	.interrupt	= gunze_interrupt,
16562306a36Sopenharmony_ci	.connect	= gunze_connect,
16662306a36Sopenharmony_ci	.disconnect	= gunze_disconnect,
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cimodule_serio_driver(gunze_drv);
170