linux/drivers/hid/hid-3m-pct.c
<<
>>
Prefs
   1/*
   2 *  HID driver for 3M PCT multitouch panels
   3 *
   4 *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
   5 *  Copyright (c) 2010      Henrik Rydberg <rydberg@euromail.se>
   6 *  Copyright (c) 2010      Canonical, Ltd.
   7 *
   8 */
   9
  10/*
  11 * This program is free software; you can redistribute it and/or modify it
  12 * under the terms of the GNU General Public License as published by the Free
  13 * Software Foundation; either version 2 of the License, or (at your option)
  14 * any later version.
  15 */
  16
  17#include <linux/device.h>
  18#include <linux/hid.h>
  19#include <linux/module.h>
  20#include <linux/slab.h>
  21#include <linux/usb.h>
  22#include <linux/input/mt.h>
  23
  24MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
  25MODULE_DESCRIPTION("3M PCT multitouch panels");
  26MODULE_LICENSE("GPL");
  27
  28#include "hid-ids.h"
  29
  30#define MAX_SLOTS               60
  31
  32/* estimated signal-to-noise ratios */
  33#define SN_MOVE                 2048
  34#define SN_WIDTH                128
  35
  36struct mmm_finger {
  37        __s32 x, y, w, h;
  38        bool touch, valid;
  39};
  40
  41struct mmm_data {
  42        struct mmm_finger f[MAX_SLOTS];
  43        __u8 curid;
  44        __u8 nexp, nreal;
  45        bool touch, valid;
  46};
  47
  48static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
  49                struct hid_field *field, struct hid_usage *usage,
  50                unsigned long **bit, int *max)
  51{
  52        int f1 = field->logical_minimum;
  53        int f2 = field->logical_maximum;
  54        int df = f2 - f1;
  55
  56        switch (usage->hid & HID_USAGE_PAGE) {
  57
  58        case HID_UP_BUTTON:
  59                return -1;
  60
  61        case HID_UP_GENDESK:
  62                switch (usage->hid) {
  63                case HID_GD_X:
  64                        hid_map_usage(hi, usage, bit, max,
  65                                        EV_ABS, ABS_MT_POSITION_X);
  66                        input_set_abs_params(hi->input, ABS_MT_POSITION_X,
  67                                             f1, f2, df / SN_MOVE, 0);
  68                        /* touchscreen emulation */
  69                        input_set_abs_params(hi->input, ABS_X,
  70                                             f1, f2, df / SN_MOVE, 0);
  71                        return 1;
  72                case HID_GD_Y:
  73                        hid_map_usage(hi, usage, bit, max,
  74                                        EV_ABS, ABS_MT_POSITION_Y);
  75                        input_set_abs_params(hi->input, ABS_MT_POSITION_Y,
  76                                             f1, f2, df / SN_MOVE, 0);
  77                        /* touchscreen emulation */
  78                        input_set_abs_params(hi->input, ABS_Y,
  79                                             f1, f2, df / SN_MOVE, 0);
  80                        return 1;
  81                }
  82                return 0;
  83
  84        case HID_UP_DIGITIZER:
  85                switch (usage->hid) {
  86                /* we do not want to map these: no input-oriented meaning */
  87                case 0x14:
  88                case 0x23:
  89                case HID_DG_INPUTMODE:
  90                case HID_DG_DEVICEINDEX:
  91                case HID_DG_CONTACTCOUNT:
  92                case HID_DG_CONTACTMAX:
  93                case HID_DG_INRANGE:
  94                case HID_DG_CONFIDENCE:
  95                        return -1;
  96                case HID_DG_TIPSWITCH:
  97                        /* touchscreen emulation */
  98                        hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
  99                        input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
 100                        return 1;
 101                case HID_DG_WIDTH:
 102                        hid_map_usage(hi, usage, bit, max,
 103                                        EV_ABS, ABS_MT_TOUCH_MAJOR);
 104                        input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR,
 105                                             f1, f2, df / SN_WIDTH, 0);
 106                        return 1;
 107                case HID_DG_HEIGHT:
 108                        hid_map_usage(hi, usage, bit, max,
 109                                        EV_ABS, ABS_MT_TOUCH_MINOR);
 110                        input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR,
 111                                             f1, f2, df / SN_WIDTH, 0);
 112                        input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
 113                                        0, 1, 0, 0);
 114                        return 1;
 115                case HID_DG_CONTACTID:
 116                        input_mt_init_slots(hi->input, MAX_SLOTS);
 117                        return 1;
 118                }
 119                /* let hid-input decide for the others */
 120                return 0;
 121
 122        case 0xff000000:
 123                /* we do not want to map these: no input-oriented meaning */
 124                return -1;
 125        }
 126
 127        return 0;
 128}
 129
 130static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 131                struct hid_field *field, struct hid_usage *usage,
 132                unsigned long **bit, int *max)
 133{
 134        /* tell hid-input to skip setup of these event types */
 135        if (usage->type == EV_KEY || usage->type == EV_ABS)
 136                set_bit(usage->type, hi->input->evbit);
 137        return -1;
 138}
 139
 140/*
 141 * this function is called when a whole packet has been received and processed,
 142 * so that it can decide what to send to the input layer.
 143 */
 144static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
 145{
 146        int i;
 147        for (i = 0; i < MAX_SLOTS; ++i) {
 148                struct mmm_finger *f = &md->f[i];
 149                if (!f->valid) {
 150                        /* this finger is just placeholder data, ignore */
 151                        continue;
 152                }
 153                input_mt_slot(input, i);
 154                input_mt_report_slot_state(input, MT_TOOL_FINGER, f->touch);
 155                if (f->touch) {
 156                        /* this finger is on the screen */
 157                        int wide = (f->w > f->h);
 158                        /* divided by two to match visual scale of touch */
 159                        int major = max(f->w, f->h) >> 1;
 160                        int minor = min(f->w, f->h) >> 1;
 161
 162                        input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
 163                        input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
 164                        input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
 165                        input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
 166                        input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
 167                }
 168                f->valid = 0;
 169        }
 170
 171        input_mt_report_pointer_emulation(input, true);
 172        input_sync(input);
 173}
 174
 175/*
 176 * this function is called upon all reports
 177 * so that we can accumulate contact point information,
 178 * and call input_mt_sync after each point.
 179 */
 180static int mmm_event(struct hid_device *hid, struct hid_field *field,
 181                                struct hid_usage *usage, __s32 value)
 182{
 183        struct mmm_data *md = hid_get_drvdata(hid);
 184        /*
 185         * strangely, this function can be called before
 186         * field->hidinput is initialized!
 187         */
 188        if (hid->claimed & HID_CLAIMED_INPUT) {
 189                struct input_dev *input = field->hidinput->input;
 190                switch (usage->hid) {
 191                case HID_DG_TIPSWITCH:
 192                        md->touch = value;
 193                        break;
 194                case HID_DG_CONFIDENCE:
 195                        md->valid = value;
 196                        break;
 197                case HID_DG_WIDTH:
 198                        if (md->valid)
 199                                md->f[md->curid].w = value;
 200                        break;
 201                case HID_DG_HEIGHT:
 202                        if (md->valid)
 203                                md->f[md->curid].h = value;
 204                        break;
 205                case HID_DG_CONTACTID:
 206                        value = clamp_val(value, 0, MAX_SLOTS - 1);
 207                        if (md->valid) {
 208                                md->curid = value;
 209                                md->f[value].touch = md->touch;
 210                                md->f[value].valid = 1;
 211                                md->nreal++;
 212                        }
 213                        break;
 214                case HID_GD_X:
 215                        if (md->valid)
 216                                md->f[md->curid].x = value;
 217                        break;
 218                case HID_GD_Y:
 219                        if (md->valid)
 220                                md->f[md->curid].y = value;
 221                        break;
 222                case HID_DG_CONTACTCOUNT:
 223                        if (value)
 224                                md->nexp = value;
 225                        if (md->nreal >= md->nexp) {
 226                                mmm_filter_event(md, input);
 227                                md->nreal = 0;
 228                        }
 229                        break;
 230                }
 231        }
 232
 233        /* we have handled the hidinput part, now remains hiddev */
 234        if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
 235                hid->hiddev_hid_event(hid, field, usage, value);
 236
 237        return 1;
 238}
 239
 240static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 241{
 242        int ret;
 243        struct mmm_data *md;
 244
 245        hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
 246
 247        md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
 248        if (!md) {
 249                hid_err(hdev, "cannot allocate 3M data\n");
 250                return -ENOMEM;
 251        }
 252        hid_set_drvdata(hdev, md);
 253
 254        ret = hid_parse(hdev);
 255        if (!ret)
 256                ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 257
 258        if (ret)
 259                kfree(md);
 260        return ret;
 261}
 262
 263static void mmm_remove(struct hid_device *hdev)
 264{
 265        hid_hw_stop(hdev);
 266        kfree(hid_get_drvdata(hdev));
 267        hid_set_drvdata(hdev, NULL);
 268}
 269
 270static const struct hid_device_id mmm_devices[] = {
 271        { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
 272        { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
 273        { }
 274};
 275MODULE_DEVICE_TABLE(hid, mmm_devices);
 276
 277static const struct hid_usage_id mmm_grabbed_usages[] = {
 278        { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
 279        { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
 280};
 281
 282static struct hid_driver mmm_driver = {
 283        .name = "3m-pct",
 284        .id_table = mmm_devices,
 285        .probe = mmm_probe,
 286        .remove = mmm_remove,
 287        .input_mapping = mmm_input_mapping,
 288        .input_mapped = mmm_input_mapped,
 289        .usage_table = mmm_grabbed_usages,
 290        .event = mmm_event,
 291};
 292
 293static int __init mmm_init(void)
 294{
 295        return hid_register_driver(&mmm_driver);
 296}
 297
 298static void __exit mmm_exit(void)
 299{
 300        hid_unregister_driver(&mmm_driver);
 301}
 302
 303module_init(mmm_init);
 304module_exit(mmm_exit);
 305
 306