linux/drivers/input/touchscreen/penmount.c
<<
>>
Prefs
   1/*
   2 * Penmount serial touchscreen driver
   3 *
   4 * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
   5 * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>
   6 *
   7 * Based on ELO driver (drivers/input/touchscreen/elo.c)
   8 * Copyright (c) 2004 Vojtech Pavlik
   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
  14 * by 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/input/mt.h>
  23#include <linux/serio.h>
  24
  25#define DRIVER_DESC     "PenMount serial touchscreen driver"
  26
  27MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
  28MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");
  29MODULE_DESCRIPTION(DRIVER_DESC);
  30MODULE_LICENSE("GPL");
  31
  32/*
  33 * Definitions & global arrays.
  34 */
  35
  36#define PM_MAX_LENGTH   6
  37#define PM_MAX_MTSLOT   16
  38#define PM_3000_MTSLOT  2
  39#define PM_6250_MTSLOT  12
  40
  41/*
  42 * Multi-touch slot
  43 */
  44
  45struct mt_slot {
  46        unsigned short x, y;
  47        bool active; /* is the touch valid? */
  48};
  49
  50/*
  51 * Per-touchscreen data.
  52 */
  53
  54struct pm {
  55        struct input_dev *dev;
  56        struct serio *serio;
  57        int idx;
  58        unsigned char data[PM_MAX_LENGTH];
  59        char phys[32];
  60        unsigned char packetsize;
  61        unsigned char maxcontacts;
  62        struct mt_slot slots[PM_MAX_MTSLOT];
  63        void (*parse_packet)(struct pm *);
  64};
  65
  66/*
  67 * pm_mtevent() sends mt events and also emulates pointer movement
  68 */
  69
  70static void pm_mtevent(struct pm *pm, struct input_dev *input)
  71{
  72        int i;
  73
  74        for (i = 0; i < pm->maxcontacts; ++i) {
  75                input_mt_slot(input, i);
  76                input_mt_report_slot_state(input, MT_TOOL_FINGER,
  77                                pm->slots[i].active);
  78                if (pm->slots[i].active) {
  79                        input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x);
  80                        input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y);
  81                }
  82        }
  83
  84        input_mt_report_pointer_emulation(input, true);
  85        input_sync(input);
  86}
  87
  88/*
  89 * pm_checkpacket() checks if data packet is valid
  90 */
  91
  92static bool pm_checkpacket(unsigned char *packet)
  93{
  94        int total = 0;
  95        int i;
  96
  97        for (i = 0; i < 5; i++)
  98                total += packet[i];
  99
 100        return packet[5] == (unsigned char)~(total & 0xff);
 101}
 102
 103static void pm_parse_9000(struct pm *pm)
 104{
 105        struct input_dev *dev = pm->dev;
 106
 107        if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) {
 108                input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]);
 109                input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]);
 110                input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
 111                input_sync(dev);
 112                pm->idx = 0;
 113        }
 114}
 115
 116static void pm_parse_6000(struct pm *pm)
 117{
 118        struct input_dev *dev = pm->dev;
 119
 120        if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) {
 121                if (pm_checkpacket(pm->data)) {
 122                        input_report_abs(dev, ABS_X,
 123                                        pm->data[2] * 256 + pm->data[1]);
 124                        input_report_abs(dev, ABS_Y,
 125                                        pm->data[4] * 256 + pm->data[3]);
 126                        input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);
 127                        input_sync(dev);
 128                }
 129                pm->idx = 0;
 130        }
 131}
 132
 133static void pm_parse_3000(struct pm *pm)
 134{
 135        struct input_dev *dev = pm->dev;
 136
 137        if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) {
 138                if (pm_checkpacket(pm->data)) {
 139                        int slotnum = pm->data[0] & 0x0f;
 140                        pm->slots[slotnum].active = pm->data[0] & 0x30;
 141                        pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
 142                        pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
 143                        pm_mtevent(pm, dev);
 144                }
 145                pm->idx = 0;
 146        }
 147}
 148
 149static void pm_parse_6250(struct pm *pm)
 150{
 151        struct input_dev *dev = pm->dev;
 152
 153        if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) {
 154                if (pm_checkpacket(pm->data)) {
 155                        int slotnum = pm->data[0] & 0x0f;
 156                        pm->slots[slotnum].active = pm->data[0] & 0x40;
 157                        pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
 158                        pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
 159                        pm_mtevent(pm, dev);
 160                }
 161                pm->idx = 0;
 162        }
 163}
 164
 165static irqreturn_t pm_interrupt(struct serio *serio,
 166                unsigned char data, unsigned int flags)
 167{
 168        struct pm *pm = serio_get_drvdata(serio);
 169
 170        pm->data[pm->idx] = data;
 171
 172        pm->parse_packet(pm);
 173
 174        return IRQ_HANDLED;
 175}
 176
 177/*
 178 * pm_disconnect() is the opposite of pm_connect()
 179 */
 180
 181static void pm_disconnect(struct serio *serio)
 182{
 183        struct pm *pm = serio_get_drvdata(serio);
 184
 185        serio_close(serio);
 186
 187        input_unregister_device(pm->dev);
 188        kfree(pm);
 189
 190        serio_set_drvdata(serio, NULL);
 191}
 192
 193/*
 194 * pm_connect() is the routine that is called when someone adds a
 195 * new serio device that supports PenMount protocol and registers it as
 196 * an input device.
 197 */
 198
 199static int pm_connect(struct serio *serio, struct serio_driver *drv)
 200{
 201        struct pm *pm;
 202        struct input_dev *input_dev;
 203        int max_x, max_y;
 204        int err;
 205
 206        pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
 207        input_dev = input_allocate_device();
 208        if (!pm || !input_dev) {
 209                err = -ENOMEM;
 210                goto fail1;
 211        }
 212
 213        pm->serio = serio;
 214        pm->dev = input_dev;
 215        snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
 216        pm->maxcontacts = 1;
 217
 218        input_dev->name = "PenMount Serial TouchScreen";
 219        input_dev->phys = pm->phys;
 220        input_dev->id.bustype = BUS_RS232;
 221        input_dev->id.vendor = SERIO_PENMOUNT;
 222        input_dev->id.product = 0;
 223        input_dev->id.version = 0x0100;
 224        input_dev->dev.parent = &serio->dev;
 225
 226        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 227        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 228
 229        switch (serio->id.id) {
 230        default:
 231        case 0:
 232                pm->packetsize = 5;
 233                pm->parse_packet = pm_parse_9000;
 234                input_dev->id.product = 0x9000;
 235                max_x = max_y = 0x3ff;
 236                break;
 237
 238        case 1:
 239                pm->packetsize = 6;
 240                pm->parse_packet = pm_parse_6000;
 241                input_dev->id.product = 0x6000;
 242                max_x = max_y = 0x3ff;
 243                break;
 244
 245        case 2:
 246                pm->packetsize = 6;
 247                pm->parse_packet = pm_parse_3000;
 248                input_dev->id.product = 0x3000;
 249                max_x = max_y = 0x7ff;
 250                pm->maxcontacts = PM_3000_MTSLOT;
 251                break;
 252
 253        case 3:
 254                pm->packetsize = 6;
 255                pm->parse_packet = pm_parse_6250;
 256                input_dev->id.product = 0x6250;
 257                max_x = max_y = 0x3ff;
 258                pm->maxcontacts = PM_6250_MTSLOT;
 259                break;
 260        }
 261
 262        input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0);
 263        input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
 264
 265        if (pm->maxcontacts > 1) {
 266                input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
 267                input_set_abs_params(pm->dev,
 268                                     ABS_MT_POSITION_X, 0, max_x, 0, 0);
 269                input_set_abs_params(pm->dev,
 270                                     ABS_MT_POSITION_Y, 0, max_y, 0, 0);
 271        }
 272
 273        serio_set_drvdata(serio, pm);
 274
 275        err = serio_open(serio, drv);
 276        if (err)
 277                goto fail2;
 278
 279        err = input_register_device(pm->dev);
 280        if (err)
 281                goto fail3;
 282
 283        return 0;
 284
 285 fail3: serio_close(serio);
 286 fail2: serio_set_drvdata(serio, NULL);
 287 fail1: input_free_device(input_dev);
 288        kfree(pm);
 289        return err;
 290}
 291
 292/*
 293 * The serio driver structure.
 294 */
 295
 296static struct serio_device_id pm_serio_ids[] = {
 297        {
 298                .type   = SERIO_RS232,
 299                .proto  = SERIO_PENMOUNT,
 300                .id     = SERIO_ANY,
 301                .extra  = SERIO_ANY,
 302        },
 303        { 0 }
 304};
 305
 306MODULE_DEVICE_TABLE(serio, pm_serio_ids);
 307
 308static struct serio_driver pm_drv = {
 309        .driver         = {
 310                .name   = "serio-penmount",
 311        },
 312        .description    = DRIVER_DESC,
 313        .id_table       = pm_serio_ids,
 314        .interrupt      = pm_interrupt,
 315        .connect        = pm_connect,
 316        .disconnect     = pm_disconnect,
 317};
 318
 319module_serio_driver(pm_drv);
 320