162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Touchwindow serial touchscreen driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) 862306a36Sopenharmony_ci * Copyright (c) 2004 Vojtech Pavlik 962306a36Sopenharmony_ci * and Dan Streetman <ddstreet@ieee.org> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * 2005/02/19 Rick Koch: 1562306a36Sopenharmony_ci * The Touchwindow I used is made by Edmark Corp. and 1662306a36Sopenharmony_ci * constantly outputs a stream of 0's unless it is touched. 1762306a36Sopenharmony_ci * It then outputs 3 bytes: X, Y, and a copy of Y. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/input.h> 2562306a36Sopenharmony_ci#include <linux/serio.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DRIVER_DESC "Touchwindow serial touchscreen driver" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>"); 3062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Definitions & global arrays. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define TW_LENGTH 3 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define TW_MIN_XC 0 4062306a36Sopenharmony_ci#define TW_MAX_XC 0xff 4162306a36Sopenharmony_ci#define TW_MIN_YC 0 4262306a36Sopenharmony_ci#define TW_MAX_YC 0xff 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Per-touchscreen data. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct tw { 4962306a36Sopenharmony_ci struct input_dev *dev; 5062306a36Sopenharmony_ci struct serio *serio; 5162306a36Sopenharmony_ci int idx; 5262306a36Sopenharmony_ci int touched; 5362306a36Sopenharmony_ci unsigned char data[TW_LENGTH]; 5462306a36Sopenharmony_ci char phys[32]; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic irqreturn_t tw_interrupt(struct serio *serio, 5862306a36Sopenharmony_ci unsigned char data, unsigned int flags) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct tw *tw = serio_get_drvdata(serio); 6162306a36Sopenharmony_ci struct input_dev *dev = tw->dev; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (data) { /* touch */ 6462306a36Sopenharmony_ci tw->touched = 1; 6562306a36Sopenharmony_ci tw->data[tw->idx++] = data; 6662306a36Sopenharmony_ci /* verify length and that the two Y's are the same */ 6762306a36Sopenharmony_ci if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { 6862306a36Sopenharmony_ci input_report_abs(dev, ABS_X, tw->data[0]); 6962306a36Sopenharmony_ci input_report_abs(dev, ABS_Y, tw->data[1]); 7062306a36Sopenharmony_ci input_report_key(dev, BTN_TOUCH, 1); 7162306a36Sopenharmony_ci input_sync(dev); 7262306a36Sopenharmony_ci tw->idx = 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } else if (tw->touched) { /* untouch */ 7562306a36Sopenharmony_ci input_report_key(dev, BTN_TOUCH, 0); 7662306a36Sopenharmony_ci input_sync(dev); 7762306a36Sopenharmony_ci tw->idx = 0; 7862306a36Sopenharmony_ci tw->touched = 0; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return IRQ_HANDLED; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * tw_disconnect() is the opposite of tw_connect() 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void tw_disconnect(struct serio *serio) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct tw *tw = serio_get_drvdata(serio); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci input_get_device(tw->dev); 9362306a36Sopenharmony_ci input_unregister_device(tw->dev); 9462306a36Sopenharmony_ci serio_close(serio); 9562306a36Sopenharmony_ci serio_set_drvdata(serio, NULL); 9662306a36Sopenharmony_ci input_put_device(tw->dev); 9762306a36Sopenharmony_ci kfree(tw); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * tw_connect() is the routine that is called when someone adds a 10262306a36Sopenharmony_ci * new serio device that supports the Touchwin protocol and registers it as 10362306a36Sopenharmony_ci * an input device. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int tw_connect(struct serio *serio, struct serio_driver *drv) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct tw *tw; 10962306a36Sopenharmony_ci struct input_dev *input_dev; 11062306a36Sopenharmony_ci int err; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci tw = kzalloc(sizeof(struct tw), GFP_KERNEL); 11362306a36Sopenharmony_ci input_dev = input_allocate_device(); 11462306a36Sopenharmony_ci if (!tw || !input_dev) { 11562306a36Sopenharmony_ci err = -ENOMEM; 11662306a36Sopenharmony_ci goto fail1; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci tw->serio = serio; 12062306a36Sopenharmony_ci tw->dev = input_dev; 12162306a36Sopenharmony_ci snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci input_dev->name = "Touchwindow Serial TouchScreen"; 12462306a36Sopenharmony_ci input_dev->phys = tw->phys; 12562306a36Sopenharmony_ci input_dev->id.bustype = BUS_RS232; 12662306a36Sopenharmony_ci input_dev->id.vendor = SERIO_TOUCHWIN; 12762306a36Sopenharmony_ci input_dev->id.product = 0; 12862306a36Sopenharmony_ci input_dev->id.version = 0x0100; 12962306a36Sopenharmony_ci input_dev->dev.parent = &serio->dev; 13062306a36Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 13162306a36Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 13262306a36Sopenharmony_ci input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0); 13362306a36Sopenharmony_ci input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci serio_set_drvdata(serio, tw); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci err = serio_open(serio, drv); 13862306a36Sopenharmony_ci if (err) 13962306a36Sopenharmony_ci goto fail2; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci err = input_register_device(tw->dev); 14262306a36Sopenharmony_ci if (err) 14362306a36Sopenharmony_ci goto fail3; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci fail3: serio_close(serio); 14862306a36Sopenharmony_ci fail2: serio_set_drvdata(serio, NULL); 14962306a36Sopenharmony_ci fail1: input_free_device(input_dev); 15062306a36Sopenharmony_ci kfree(tw); 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * The serio driver structure. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct serio_device_id tw_serio_ids[] = { 15962306a36Sopenharmony_ci { 16062306a36Sopenharmony_ci .type = SERIO_RS232, 16162306a36Sopenharmony_ci .proto = SERIO_TOUCHWIN, 16262306a36Sopenharmony_ci .id = SERIO_ANY, 16362306a36Sopenharmony_ci .extra = SERIO_ANY, 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci { 0 } 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(serio, tw_serio_ids); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic struct serio_driver tw_drv = { 17162306a36Sopenharmony_ci .driver = { 17262306a36Sopenharmony_ci .name = "touchwin", 17362306a36Sopenharmony_ci }, 17462306a36Sopenharmony_ci .description = DRIVER_DESC, 17562306a36Sopenharmony_ci .id_table = tw_serio_ids, 17662306a36Sopenharmony_ci .interrupt = tw_interrupt, 17762306a36Sopenharmony_ci .connect = tw_connect, 17862306a36Sopenharmony_ci .disconnect = tw_disconnect, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cimodule_serio_driver(tw_drv); 182