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