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