cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");