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