egalax_ts_serial.c (4449B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * EETI Egalax serial touchscreen driver 4 * 5 * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu> 6 * 7 * based on the 8 * 9 * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) 10 */ 11 12 13#include <linux/errno.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/slab.h> 17#include <linux/input.h> 18#include <linux/serio.h> 19 20#define DRIVER_DESC "EETI Egalax serial touchscreen driver" 21 22/* 23 * Definitions & global arrays. 24 */ 25 26#define EGALAX_FORMAT_MAX_LENGTH 6 27#define EGALAX_FORMAT_START_BIT BIT(7) 28#define EGALAX_FORMAT_PRESSURE_BIT BIT(6) 29#define EGALAX_FORMAT_TOUCH_BIT BIT(0) 30#define EGALAX_FORMAT_RESOLUTION_MASK 0x06 31 32#define EGALAX_MIN_XC 0 33#define EGALAX_MAX_XC 0x4000 34#define EGALAX_MIN_YC 0 35#define EGALAX_MAX_YC 0x4000 36 37/* 38 * Per-touchscreen data. 39 */ 40struct egalax { 41 struct input_dev *input; 42 struct serio *serio; 43 int idx; 44 u8 data[EGALAX_FORMAT_MAX_LENGTH]; 45 char phys[32]; 46}; 47 48static void egalax_process_data(struct egalax *egalax) 49{ 50 struct input_dev *dev = egalax->input; 51 u8 *data = egalax->data; 52 u16 x, y; 53 u8 shift; 54 u8 mask; 55 56 shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); 57 mask = 0xff >> (shift + 1); 58 59 x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; 60 y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; 61 62 input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); 63 input_report_abs(dev, ABS_X, x); 64 input_report_abs(dev, ABS_Y, y); 65 input_sync(dev); 66} 67 68static irqreturn_t egalax_interrupt(struct serio *serio, 69 unsigned char data, unsigned int flags) 70{ 71 struct egalax *egalax = serio_get_drvdata(serio); 72 int pkt_len; 73 74 egalax->data[egalax->idx++] = data; 75 76 if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { 77 pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; 78 if (pkt_len == egalax->idx) { 79 egalax_process_data(egalax); 80 egalax->idx = 0; 81 } 82 } else { 83 dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", 84 egalax->data[0]); 85 egalax->idx = 0; 86 } 87 88 return IRQ_HANDLED; 89} 90 91/* 92 * egalax_connect() is the routine that is called when someone adds a 93 * new serio device that supports egalax protocol and registers it as 94 * an input device. This is usually accomplished using inputattach. 95 */ 96static int egalax_connect(struct serio *serio, struct serio_driver *drv) 97{ 98 struct egalax *egalax; 99 struct input_dev *input_dev; 100 int error; 101 102 egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); 103 input_dev = input_allocate_device(); 104 if (!egalax || !input_dev) { 105 error = -ENOMEM; 106 goto err_free_mem; 107 } 108 109 egalax->serio = serio; 110 egalax->input = input_dev; 111 snprintf(egalax->phys, sizeof(egalax->phys), 112 "%s/input0", serio->phys); 113 114 input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; 115 input_dev->phys = egalax->phys; 116 input_dev->id.bustype = BUS_RS232; 117 input_dev->id.vendor = SERIO_EGALAX; 118 input_dev->id.product = 0; 119 input_dev->id.version = 0x0001; 120 input_dev->dev.parent = &serio->dev; 121 122 input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 123 input_set_abs_params(input_dev, ABS_X, 124 EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); 125 input_set_abs_params(input_dev, ABS_Y, 126 EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); 127 128 serio_set_drvdata(serio, egalax); 129 130 error = serio_open(serio, drv); 131 if (error) 132 goto err_reset_drvdata; 133 134 error = input_register_device(input_dev); 135 if (error) 136 goto err_close_serio; 137 138 return 0; 139 140err_close_serio: 141 serio_close(serio); 142err_reset_drvdata: 143 serio_set_drvdata(serio, NULL); 144err_free_mem: 145 input_free_device(input_dev); 146 kfree(egalax); 147 return error; 148} 149 150static void egalax_disconnect(struct serio *serio) 151{ 152 struct egalax *egalax = serio_get_drvdata(serio); 153 154 serio_close(serio); 155 serio_set_drvdata(serio, NULL); 156 input_unregister_device(egalax->input); 157 kfree(egalax); 158} 159 160/* 161 * The serio driver structure. 162 */ 163 164static const struct serio_device_id egalax_serio_ids[] = { 165 { 166 .type = SERIO_RS232, 167 .proto = SERIO_EGALAX, 168 .id = SERIO_ANY, 169 .extra = SERIO_ANY, 170 }, 171 { 0 } 172}; 173 174MODULE_DEVICE_TABLE(serio, egalax_serio_ids); 175 176static struct serio_driver egalax_drv = { 177 .driver = { 178 .name = "egalax", 179 }, 180 .description = DRIVER_DESC, 181 .id_table = egalax_serio_ids, 182 .interrupt = egalax_interrupt, 183 .connect = egalax_connect, 184 .disconnect = egalax_disconnect, 185}; 186module_serio_driver(egalax_drv); 187 188MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>"); 189MODULE_DESCRIPTION(DRIVER_DESC); 190MODULE_LICENSE("GPL v2");