18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MicroTouch (3M) serial touchscreen driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2004 Vojtech Pavlik 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * 2005/02/19 Dan Streetman <ddstreet@ieee.org> 118c2ecf20Sopenharmony_ci * Copied elo.c and edited for MicroTouch protocol 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/input.h> 198c2ecf20Sopenharmony_ci#include <linux/serio.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DRIVER_DESC "MicroTouch serial touchscreen driver" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Definitions & global arrays. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80 328c2ecf20Sopenharmony_ci#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40 338c2ecf20Sopenharmony_ci#define MTOUCH_FORMAT_TABLET_LENGTH 5 348c2ecf20Sopenharmony_ci#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01 358c2ecf20Sopenharmony_ci#define MTOUCH_RESPONSE_END_BYTE 0x0d 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* todo: check specs for max length of all responses */ 388c2ecf20Sopenharmony_ci#define MTOUCH_MAX_LENGTH 16 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MTOUCH_MIN_XC 0 418c2ecf20Sopenharmony_ci#define MTOUCH_MAX_XC 0x3fff 428c2ecf20Sopenharmony_ci#define MTOUCH_MIN_YC 0 438c2ecf20Sopenharmony_ci#define MTOUCH_MAX_YC 0x3fff 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1]) 468c2ecf20Sopenharmony_ci#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3]) 478c2ecf20Sopenharmony_ci#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0]) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Per-touchscreen data. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct mtouch { 548c2ecf20Sopenharmony_ci struct input_dev *dev; 558c2ecf20Sopenharmony_ci struct serio *serio; 568c2ecf20Sopenharmony_ci int idx; 578c2ecf20Sopenharmony_ci unsigned char data[MTOUCH_MAX_LENGTH]; 588c2ecf20Sopenharmony_ci char phys[32]; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void mtouch_process_format_tablet(struct mtouch *mtouch) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct input_dev *dev = mtouch->dev; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) { 668c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data)); 678c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data)); 688c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data)); 698c2ecf20Sopenharmony_ci input_sync(dev); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mtouch->idx = 0; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void mtouch_process_response(struct mtouch *mtouch) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) { 788c2ecf20Sopenharmony_ci /* FIXME - process response */ 798c2ecf20Sopenharmony_ci mtouch->idx = 0; 808c2ecf20Sopenharmony_ci } else if (MTOUCH_MAX_LENGTH == mtouch->idx) { 818c2ecf20Sopenharmony_ci printk(KERN_ERR "mtouch.c: too many response bytes\n"); 828c2ecf20Sopenharmony_ci mtouch->idx = 0; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic irqreturn_t mtouch_interrupt(struct serio *serio, 878c2ecf20Sopenharmony_ci unsigned char data, unsigned int flags) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct mtouch *mtouch = serio_get_drvdata(serio); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mtouch->data[mtouch->idx] = data; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0]) 948c2ecf20Sopenharmony_ci mtouch_process_format_tablet(mtouch); 958c2ecf20Sopenharmony_ci else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0]) 968c2ecf20Sopenharmony_ci mtouch_process_response(mtouch); 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * mtouch_disconnect() is the opposite of mtouch_connect() 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void mtouch_disconnect(struct serio *serio) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct mtouch *mtouch = serio_get_drvdata(serio); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci input_get_device(mtouch->dev); 1128c2ecf20Sopenharmony_ci input_unregister_device(mtouch->dev); 1138c2ecf20Sopenharmony_ci serio_close(serio); 1148c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 1158c2ecf20Sopenharmony_ci input_put_device(mtouch->dev); 1168c2ecf20Sopenharmony_ci kfree(mtouch); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * mtouch_connect() is the routine that is called when someone adds a 1218c2ecf20Sopenharmony_ci * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as 1228c2ecf20Sopenharmony_ci * an input device. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int mtouch_connect(struct serio *serio, struct serio_driver *drv) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct mtouch *mtouch; 1288c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1298c2ecf20Sopenharmony_ci int err; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL); 1328c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 1338c2ecf20Sopenharmony_ci if (!mtouch || !input_dev) { 1348c2ecf20Sopenharmony_ci err = -ENOMEM; 1358c2ecf20Sopenharmony_ci goto fail1; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci mtouch->serio = serio; 1398c2ecf20Sopenharmony_ci mtouch->dev = input_dev; 1408c2ecf20Sopenharmony_ci snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci input_dev->name = "MicroTouch Serial TouchScreen"; 1438c2ecf20Sopenharmony_ci input_dev->phys = mtouch->phys; 1448c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_RS232; 1458c2ecf20Sopenharmony_ci input_dev->id.vendor = SERIO_MICROTOUCH; 1468c2ecf20Sopenharmony_ci input_dev->id.product = 0; 1478c2ecf20Sopenharmony_ci input_dev->id.version = 0x0100; 1488c2ecf20Sopenharmony_ci input_dev->dev.parent = &serio->dev; 1498c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1508c2ecf20Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 1518c2ecf20Sopenharmony_ci input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0); 1528c2ecf20Sopenharmony_ci input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci serio_set_drvdata(serio, mtouch); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci err = serio_open(serio, drv); 1578c2ecf20Sopenharmony_ci if (err) 1588c2ecf20Sopenharmony_ci goto fail2; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci err = input_register_device(mtouch->dev); 1618c2ecf20Sopenharmony_ci if (err) 1628c2ecf20Sopenharmony_ci goto fail3; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci fail3: serio_close(serio); 1678c2ecf20Sopenharmony_ci fail2: serio_set_drvdata(serio, NULL); 1688c2ecf20Sopenharmony_ci fail1: input_free_device(input_dev); 1698c2ecf20Sopenharmony_ci kfree(mtouch); 1708c2ecf20Sopenharmony_ci return err; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * The serio driver structure. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct serio_device_id mtouch_serio_ids[] = { 1788c2ecf20Sopenharmony_ci { 1798c2ecf20Sopenharmony_ci .type = SERIO_RS232, 1808c2ecf20Sopenharmony_ci .proto = SERIO_MICROTOUCH, 1818c2ecf20Sopenharmony_ci .id = SERIO_ANY, 1828c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 1838c2ecf20Sopenharmony_ci }, 1848c2ecf20Sopenharmony_ci { 0 } 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, mtouch_serio_ids); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct serio_driver mtouch_drv = { 1908c2ecf20Sopenharmony_ci .driver = { 1918c2ecf20Sopenharmony_ci .name = "mtouch", 1928c2ecf20Sopenharmony_ci }, 1938c2ecf20Sopenharmony_ci .description = DRIVER_DESC, 1948c2ecf20Sopenharmony_ci .id_table = mtouch_serio_ids, 1958c2ecf20Sopenharmony_ci .interrupt = mtouch_interrupt, 1968c2ecf20Sopenharmony_ci .connect = mtouch_connect, 1978c2ecf20Sopenharmony_ci .disconnect = mtouch_disconnect, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cimodule_serio_driver(mtouch_drv); 201