1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23cadd2d9SRichard Lemon /* 33cadd2d9SRichard Lemon * iNexio serial touchscreen driver 43cadd2d9SRichard Lemon * 53cadd2d9SRichard Lemon * Copyright (c) 2008 Richard Lemon 63cadd2d9SRichard Lemon * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman 73cadd2d9SRichard Lemon */ 83cadd2d9SRichard Lemon 93cadd2d9SRichard Lemon 103cadd2d9SRichard Lemon /* 113cadd2d9SRichard Lemon * 2008/06/19 Richard Lemon <richard@codelemon.com> 123cadd2d9SRichard Lemon * Copied mtouch.c and edited for iNexio protocol 133cadd2d9SRichard Lemon */ 143cadd2d9SRichard Lemon 153cadd2d9SRichard Lemon #include <linux/errno.h> 163cadd2d9SRichard Lemon #include <linux/kernel.h> 173cadd2d9SRichard Lemon #include <linux/module.h> 183cadd2d9SRichard Lemon #include <linux/slab.h> 193cadd2d9SRichard Lemon #include <linux/input.h> 203cadd2d9SRichard Lemon #include <linux/serio.h> 213cadd2d9SRichard Lemon 223cadd2d9SRichard Lemon #define DRIVER_DESC "iNexio serial touchscreen driver" 233cadd2d9SRichard Lemon 243cadd2d9SRichard Lemon MODULE_AUTHOR("Richard Lemon <richard@codelemon.com>"); 253cadd2d9SRichard Lemon MODULE_DESCRIPTION(DRIVER_DESC); 263cadd2d9SRichard Lemon MODULE_LICENSE("GPL"); 273cadd2d9SRichard Lemon 283cadd2d9SRichard Lemon /* 293cadd2d9SRichard Lemon * Definitions & global arrays. 303cadd2d9SRichard Lemon */ 313cadd2d9SRichard Lemon 323cadd2d9SRichard Lemon #define INEXIO_FORMAT_TOUCH_BIT 0x01 333cadd2d9SRichard Lemon #define INEXIO_FORMAT_LENGTH 5 343cadd2d9SRichard Lemon #define INEXIO_RESPONSE_BEGIN_BYTE 0x80 353cadd2d9SRichard Lemon 363cadd2d9SRichard Lemon /* todo: check specs for max length of all responses */ 373cadd2d9SRichard Lemon #define INEXIO_MAX_LENGTH 16 383cadd2d9SRichard Lemon 393cadd2d9SRichard Lemon #define INEXIO_MIN_XC 0 403cadd2d9SRichard Lemon #define INEXIO_MAX_XC 0x3fff 413cadd2d9SRichard Lemon #define INEXIO_MIN_YC 0 423cadd2d9SRichard Lemon #define INEXIO_MAX_YC 0x3fff 433cadd2d9SRichard Lemon 443cadd2d9SRichard Lemon #define INEXIO_GET_XC(data) (((data[1])<<7) | data[2]) 453cadd2d9SRichard Lemon #define INEXIO_GET_YC(data) (((data[3])<<7) | data[4]) 463cadd2d9SRichard Lemon #define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0]) 473cadd2d9SRichard Lemon 483cadd2d9SRichard Lemon /* 493cadd2d9SRichard Lemon * Per-touchscreen data. 503cadd2d9SRichard Lemon */ 513cadd2d9SRichard Lemon 523cadd2d9SRichard Lemon struct inexio { 533cadd2d9SRichard Lemon struct input_dev *dev; 543cadd2d9SRichard Lemon struct serio *serio; 553cadd2d9SRichard Lemon int idx; 563cadd2d9SRichard Lemon unsigned char data[INEXIO_MAX_LENGTH]; 573cadd2d9SRichard Lemon char phys[32]; 583cadd2d9SRichard Lemon }; 593cadd2d9SRichard Lemon 603cadd2d9SRichard Lemon static void inexio_process_data(struct inexio *pinexio) 613cadd2d9SRichard Lemon { 623cadd2d9SRichard Lemon struct input_dev *dev = pinexio->dev; 633cadd2d9SRichard Lemon 643cadd2d9SRichard Lemon if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) { 653cadd2d9SRichard Lemon input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data)); 663cadd2d9SRichard Lemon input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data)); 673cadd2d9SRichard Lemon input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data)); 683cadd2d9SRichard Lemon input_sync(dev); 693cadd2d9SRichard Lemon 703cadd2d9SRichard Lemon pinexio->idx = 0; 713cadd2d9SRichard Lemon } 723cadd2d9SRichard Lemon } 733cadd2d9SRichard Lemon 743cadd2d9SRichard Lemon static irqreturn_t inexio_interrupt(struct serio *serio, 753cadd2d9SRichard Lemon unsigned char data, unsigned int flags) 763cadd2d9SRichard Lemon { 773cadd2d9SRichard Lemon struct inexio *pinexio = serio_get_drvdata(serio); 783cadd2d9SRichard Lemon 793cadd2d9SRichard Lemon pinexio->data[pinexio->idx] = data; 803cadd2d9SRichard Lemon 813cadd2d9SRichard Lemon if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) 823cadd2d9SRichard Lemon inexio_process_data(pinexio); 833cadd2d9SRichard Lemon else 843cadd2d9SRichard Lemon printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); 853cadd2d9SRichard Lemon 863cadd2d9SRichard Lemon return IRQ_HANDLED; 873cadd2d9SRichard Lemon } 883cadd2d9SRichard Lemon 893cadd2d9SRichard Lemon /* 903cadd2d9SRichard Lemon * inexio_disconnect() is the opposite of inexio_connect() 913cadd2d9SRichard Lemon */ 923cadd2d9SRichard Lemon 933cadd2d9SRichard Lemon static void inexio_disconnect(struct serio *serio) 943cadd2d9SRichard Lemon { 953cadd2d9SRichard Lemon struct inexio *pinexio = serio_get_drvdata(serio); 963cadd2d9SRichard Lemon 973cadd2d9SRichard Lemon input_get_device(pinexio->dev); 983cadd2d9SRichard Lemon input_unregister_device(pinexio->dev); 993cadd2d9SRichard Lemon serio_close(serio); 1003cadd2d9SRichard Lemon serio_set_drvdata(serio, NULL); 1013cadd2d9SRichard Lemon input_put_device(pinexio->dev); 1023cadd2d9SRichard Lemon kfree(pinexio); 1033cadd2d9SRichard Lemon } 1043cadd2d9SRichard Lemon 1053cadd2d9SRichard Lemon /* 1063cadd2d9SRichard Lemon * inexio_connect() is the routine that is called when someone adds a 1073cadd2d9SRichard Lemon * new serio device that supports iNexio protocol and registers it as 1083cadd2d9SRichard Lemon * an input device. This is usually accomplished using inputattach. 1093cadd2d9SRichard Lemon */ 1103cadd2d9SRichard Lemon 1113cadd2d9SRichard Lemon static int inexio_connect(struct serio *serio, struct serio_driver *drv) 1123cadd2d9SRichard Lemon { 1133cadd2d9SRichard Lemon struct inexio *pinexio; 1143cadd2d9SRichard Lemon struct input_dev *input_dev; 1153cadd2d9SRichard Lemon int err; 1163cadd2d9SRichard Lemon 1173cadd2d9SRichard Lemon pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL); 1183cadd2d9SRichard Lemon input_dev = input_allocate_device(); 1193cadd2d9SRichard Lemon if (!pinexio || !input_dev) { 1203cadd2d9SRichard Lemon err = -ENOMEM; 1213cadd2d9SRichard Lemon goto fail1; 1223cadd2d9SRichard Lemon } 1233cadd2d9SRichard Lemon 1243cadd2d9SRichard Lemon pinexio->serio = serio; 1253cadd2d9SRichard Lemon pinexio->dev = input_dev; 1263cadd2d9SRichard Lemon snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys); 1273cadd2d9SRichard Lemon 1283cadd2d9SRichard Lemon input_dev->name = "iNexio Serial TouchScreen"; 1293cadd2d9SRichard Lemon input_dev->phys = pinexio->phys; 1303cadd2d9SRichard Lemon input_dev->id.bustype = BUS_RS232; 1313cadd2d9SRichard Lemon input_dev->id.vendor = SERIO_INEXIO; 1323cadd2d9SRichard Lemon input_dev->id.product = 0; 1333cadd2d9SRichard Lemon input_dev->id.version = 0x0001; 1343cadd2d9SRichard Lemon input_dev->dev.parent = &serio->dev; 1353cadd2d9SRichard Lemon input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1363cadd2d9SRichard Lemon input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 1373cadd2d9SRichard Lemon input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0); 1383cadd2d9SRichard Lemon input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0); 1393cadd2d9SRichard Lemon 1403cadd2d9SRichard Lemon serio_set_drvdata(serio, pinexio); 1413cadd2d9SRichard Lemon 1423cadd2d9SRichard Lemon err = serio_open(serio, drv); 1433cadd2d9SRichard Lemon if (err) 1443cadd2d9SRichard Lemon goto fail2; 1453cadd2d9SRichard Lemon 1463cadd2d9SRichard Lemon err = input_register_device(pinexio->dev); 1473cadd2d9SRichard Lemon if (err) 1483cadd2d9SRichard Lemon goto fail3; 1493cadd2d9SRichard Lemon 1503cadd2d9SRichard Lemon return 0; 1513cadd2d9SRichard Lemon 1523cadd2d9SRichard Lemon fail3: serio_close(serio); 1533cadd2d9SRichard Lemon fail2: serio_set_drvdata(serio, NULL); 1543cadd2d9SRichard Lemon fail1: input_free_device(input_dev); 1553cadd2d9SRichard Lemon kfree(pinexio); 1563cadd2d9SRichard Lemon return err; 1573cadd2d9SRichard Lemon } 1583cadd2d9SRichard Lemon 1593cadd2d9SRichard Lemon /* 1603cadd2d9SRichard Lemon * The serio driver structure. 1613cadd2d9SRichard Lemon */ 1623cadd2d9SRichard Lemon 16379308587SArvind Yadav static const struct serio_device_id inexio_serio_ids[] = { 1643cadd2d9SRichard Lemon { 1653cadd2d9SRichard Lemon .type = SERIO_RS232, 1663cadd2d9SRichard Lemon .proto = SERIO_INEXIO, 1673cadd2d9SRichard Lemon .id = SERIO_ANY, 1683cadd2d9SRichard Lemon .extra = SERIO_ANY, 1693cadd2d9SRichard Lemon }, 1703cadd2d9SRichard Lemon { 0 } 1713cadd2d9SRichard Lemon }; 1723cadd2d9SRichard Lemon 1733cadd2d9SRichard Lemon MODULE_DEVICE_TABLE(serio, inexio_serio_ids); 1743cadd2d9SRichard Lemon 1753cadd2d9SRichard Lemon static struct serio_driver inexio_drv = { 1763cadd2d9SRichard Lemon .driver = { 1773cadd2d9SRichard Lemon .name = "inexio", 1783cadd2d9SRichard Lemon }, 1793cadd2d9SRichard Lemon .description = DRIVER_DESC, 1803cadd2d9SRichard Lemon .id_table = inexio_serio_ids, 1813cadd2d9SRichard Lemon .interrupt = inexio_interrupt, 1823cadd2d9SRichard Lemon .connect = inexio_connect, 1833cadd2d9SRichard Lemon .disconnect = inexio_disconnect, 1843cadd2d9SRichard Lemon }; 1853cadd2d9SRichard Lemon 18665ac9f7aSAxel Lin module_serio_driver(inexio_drv); 187