hid-ntrig.c revision dbf2b17de505d390b5ecf5b5944fc0c88f6d66fe
1/* 2 * HID driver for N-Trig touchscreens 3 * 4 * Copyright (c) 2008 Rafi Rubin 5 * Copyright (c) 2009 Stephane Chatty 6 * 7 */ 8 9/* 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the Free 12 * Software Foundation; either version 2 of the License, or (at your option) 13 * any later version. 14 */ 15 16#include <linux/device.h> 17#include <linux/hid.h> 18#include <linux/module.h> 19 20#include "hid-ids.h" 21 22#define NTRIG_DUPLICATE_USAGES 0x001 23 24#define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ 25 EV_KEY, (c)) 26 27struct ntrig_data { 28 /* Incoming raw values for a single contact */ 29 __u16 x, y, w, h; 30 __u16 id; 31 __u8 confidence; 32 33 bool reading_mt; 34 __u8 first_contact_confidence; 35 36 __u8 mt_footer[4]; 37 __u8 mt_foot_count; 38}; 39 40/* 41 * this driver is aimed at two firmware versions in circulation: 42 * - dual pen/finger single touch 43 * - finger multitouch, pen not working 44 */ 45 46static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, 47 struct hid_field *field, struct hid_usage *usage, 48 unsigned long **bit, int *max) 49{ 50 /* No special mappings needed for the pen and single touch */ 51 if (field->physical) 52 return 0; 53 54 switch (usage->hid & HID_USAGE_PAGE) { 55 case HID_UP_GENDESK: 56 switch (usage->hid) { 57 case HID_GD_X: 58 hid_map_usage(hi, usage, bit, max, 59 EV_ABS, ABS_MT_POSITION_X); 60 input_set_abs_params(hi->input, ABS_X, 61 field->logical_minimum, 62 field->logical_maximum, 0, 0); 63 return 1; 64 case HID_GD_Y: 65 hid_map_usage(hi, usage, bit, max, 66 EV_ABS, ABS_MT_POSITION_Y); 67 input_set_abs_params(hi->input, ABS_Y, 68 field->logical_minimum, 69 field->logical_maximum, 0, 0); 70 return 1; 71 } 72 return 0; 73 74 case HID_UP_DIGITIZER: 75 switch (usage->hid) { 76 /* we do not want to map these for now */ 77 case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */ 78 case HID_DG_INPUTMODE: 79 case HID_DG_DEVICEINDEX: 80 case HID_DG_CONTACTMAX: 81 return -1; 82 83 /* width/height mapped on TouchMajor/TouchMinor/Orientation */ 84 case HID_DG_WIDTH: 85 hid_map_usage(hi, usage, bit, max, 86 EV_ABS, ABS_MT_TOUCH_MAJOR); 87 return 1; 88 case HID_DG_HEIGHT: 89 hid_map_usage(hi, usage, bit, max, 90 EV_ABS, ABS_MT_TOUCH_MINOR); 91 input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 92 0, 1, 0, 0); 93 return 1; 94 } 95 return 0; 96 97 case 0xff000000: 98 /* we do not want to map these: no input-oriented meaning */ 99 return -1; 100 } 101 102 return 0; 103} 104 105static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, 106 struct hid_field *field, struct hid_usage *usage, 107 unsigned long **bit, int *max) 108{ 109 /* No special mappings needed for the pen and single touch */ 110 if (field->physical) 111 return 0; 112 113 if (usage->type == EV_KEY || usage->type == EV_REL 114 || usage->type == EV_ABS) 115 clear_bit(usage->code, *bit); 116 117 return 0; 118} 119 120/* 121 * this function is called upon all reports 122 * so that we can filter contact point information, 123 * decide whether we are in multi or single touch mode 124 * and call input_mt_sync after each point if necessary 125 */ 126static int ntrig_event (struct hid_device *hid, struct hid_field *field, 127 struct hid_usage *usage, __s32 value) 128{ 129 struct input_dev *input = field->hidinput->input; 130 struct ntrig_data *nd = hid_get_drvdata(hid); 131 132 /* No special handling needed for the pen */ 133 if (field->application == HID_DG_PEN) 134 return 0; 135 136 if (hid->claimed & HID_CLAIMED_INPUT) { 137 switch (usage->hid) { 138 case 0xff000001: 139 /* Tag indicating the start of a multitouch group */ 140 nd->reading_mt = 1; 141 nd->first_contact_confidence = 0; 142 break; 143 case HID_DG_CONFIDENCE: 144 nd->confidence = value; 145 break; 146 case HID_GD_X: 147 nd->x = value; 148 /* Clear the contact footer */ 149 nd->mt_foot_count = 0; 150 break; 151 case HID_GD_Y: 152 nd->y = value; 153 break; 154 case HID_DG_CONTACTID: 155 nd->id = value; 156 break; 157 case HID_DG_WIDTH: 158 nd->w = value; 159 break; 160 case HID_DG_HEIGHT: 161 nd->h = value; 162 /* 163 * when in single touch mode, this is the last 164 * report received in a finger event. We want 165 * to emit a normal (X, Y) position 166 */ 167 if (!nd->reading_mt) { 168 input_event(input, EV_ABS, ABS_X, nd->x); 169 input_event(input, EV_ABS, ABS_Y, nd->y); 170 } 171 break; 172 case 0xff000002: 173 /* 174 * we receive this when the device is in multitouch 175 * mode. The first of the three values tagged with 176 * this usage tells if the contact point is real 177 * or a placeholder 178 */ 179 180 /* Shouldn't get more than 4 footer packets, so skip */ 181 if (nd->mt_foot_count >= 4) 182 break; 183 184 nd->mt_footer[nd->mt_foot_count++] = value; 185 186 /* if the footer isn't complete break */ 187 if (nd->mt_foot_count != 4) 188 break; 189 190 /* Pen activity signal, trigger end of touch. */ 191 if (nd->mt_footer[2]) { 192 nd->confidence = 0; 193 break; 194 } 195 196 /* If the contact was invalid */ 197 if (!(nd->confidence && nd->mt_footer[0]) 198 || nd->w <= 250 199 || nd->h <= 190) { 200 nd->confidence = 0; 201 break; 202 } 203 204 /* emit a normal (X, Y) for the first point only */ 205 if (nd->id == 0) { 206 nd->first_contact_confidence = nd->confidence; 207 input_event(input, EV_ABS, ABS_X, nd->x); 208 input_event(input, EV_ABS, ABS_Y, nd->y); 209 } 210 input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); 211 input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); 212 if (nd->w > nd->h) { 213 input_event(input, EV_ABS, 214 ABS_MT_ORIENTATION, 1); 215 input_event(input, EV_ABS, 216 ABS_MT_TOUCH_MAJOR, nd->w); 217 input_event(input, EV_ABS, 218 ABS_MT_TOUCH_MINOR, nd->h); 219 } else { 220 input_event(input, EV_ABS, 221 ABS_MT_ORIENTATION, 0); 222 input_event(input, EV_ABS, 223 ABS_MT_TOUCH_MAJOR, nd->h); 224 input_event(input, EV_ABS, 225 ABS_MT_TOUCH_MINOR, nd->w); 226 } 227 input_mt_sync(field->hidinput->input); 228 break; 229 230 case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ 231 if (!nd->reading_mt) 232 break; 233 234 nd->reading_mt = 0; 235 236 if (nd->first_contact_confidence) { 237 switch (value) { 238 case 0: /* for single touch devices */ 239 case 1: 240 input_report_key(input, 241 BTN_TOOL_DOUBLETAP, 1); 242 break; 243 case 2: 244 input_report_key(input, 245 BTN_TOOL_TRIPLETAP, 1); 246 break; 247 case 3: 248 default: 249 input_report_key(input, 250 BTN_TOOL_QUADTAP, 1); 251 } 252 input_report_key(input, BTN_TOUCH, 1); 253 } else { 254 input_report_key(input, 255 BTN_TOOL_DOUBLETAP, 0); 256 input_report_key(input, 257 BTN_TOOL_TRIPLETAP, 0); 258 input_report_key(input, 259 BTN_TOOL_QUADTAP, 0); 260 } 261 break; 262 263 default: 264 /* fallback to the generic hidinput handling */ 265 return 0; 266 } 267 } 268 269 /* we have handled the hidinput part, now remains hiddev */ 270 if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) 271 hid->hiddev_hid_event(hid, field, usage, value); 272 273 return 1; 274} 275 276static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) 277{ 278 int ret; 279 struct ntrig_data *nd; 280 struct hid_input *hidinput; 281 struct input_dev *input; 282 283 if (id->driver_data) 284 hdev->quirks |= HID_QUIRK_MULTI_INPUT; 285 286 nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); 287 if (!nd) { 288 dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); 289 return -ENOMEM; 290 } 291 292 nd->reading_mt = 0; 293 hid_set_drvdata(hdev, nd); 294 295 ret = hid_parse(hdev); 296 if (ret) { 297 dev_err(&hdev->dev, "parse failed\n"); 298 goto err_free; 299 } 300 301 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 302 if (ret) { 303 dev_err(&hdev->dev, "hw start failed\n"); 304 goto err_free; 305 } 306 307 308 list_for_each_entry(hidinput, &hdev->inputs, list) { 309 input = hidinput->input; 310 switch (hidinput->report->field[0]->application) { 311 case HID_DG_PEN: 312 input->name = "N-Trig Pen"; 313 break; 314 case HID_DG_TOUCHSCREEN: 315 __clear_bit(BTN_TOOL_PEN, input->keybit); 316 /* 317 * A little something special to enable 318 * two and three finger taps. 319 */ 320 __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); 321 __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); 322 __set_bit(BTN_TOOL_QUADTAP, input->keybit); 323 /* 324 * The physical touchscreen (single touch) 325 * input has a value for physical, whereas 326 * the multitouch only has logical input 327 * fields. 328 */ 329 input->name = 330 (hidinput->report->field[0] 331 ->physical) ? 332 "N-Trig Touchscreen" : 333 "N-Trig MultiTouch"; 334 break; 335 } 336 } 337 338 return 0; 339err_free: 340 kfree(nd); 341 return ret; 342} 343 344static void ntrig_remove(struct hid_device *hdev) 345{ 346 hid_hw_stop(hdev); 347 kfree(hid_get_drvdata(hdev)); 348} 349 350static const struct hid_device_id ntrig_devices[] = { 351 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN), 352 .driver_data = NTRIG_DUPLICATE_USAGES }, 353 { } 354}; 355MODULE_DEVICE_TABLE(hid, ntrig_devices); 356 357static const struct hid_usage_id ntrig_grabbed_usages[] = { 358 { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, 359 { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } 360}; 361 362static struct hid_driver ntrig_driver = { 363 .name = "ntrig", 364 .id_table = ntrig_devices, 365 .probe = ntrig_probe, 366 .remove = ntrig_remove, 367 .input_mapping = ntrig_input_mapping, 368 .input_mapped = ntrig_input_mapped, 369 .usage_table = ntrig_grabbed_usages, 370 .event = ntrig_event, 371}; 372 373static int __init ntrig_init(void) 374{ 375 return hid_register_driver(&ntrig_driver); 376} 377 378static void __exit ntrig_exit(void) 379{ 380 hid_unregister_driver(&ntrig_driver); 381} 382 383module_init(ntrig_init); 384module_exit(ntrig_exit); 385MODULE_LICENSE("GPL"); 386