hid-ntrig.c revision 57fd637ad9ac6b13c1c47b9a0ced4ee99bb26e76
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 __s32 x, y, id, w, h; 29 char reading_a_point, found_contact_id; 30}; 31 32/* 33 * this driver is aimed at two firmware versions in circulation: 34 * - dual pen/finger single touch 35 * - finger multitouch, pen not working 36 */ 37 38static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, 39 struct hid_field *field, struct hid_usage *usage, 40 unsigned long **bit, int *max) 41{ 42 switch (usage->hid & HID_USAGE_PAGE) { 43 44 case HID_UP_GENDESK: 45 switch (usage->hid) { 46 case HID_GD_X: 47 hid_map_usage(hi, usage, bit, max, 48 EV_ABS, ABS_MT_POSITION_X); 49 input_set_abs_params(hi->input, ABS_X, 50 field->logical_minimum, 51 field->logical_maximum, 0, 0); 52 return 1; 53 case HID_GD_Y: 54 hid_map_usage(hi, usage, bit, max, 55 EV_ABS, ABS_MT_POSITION_Y); 56 input_set_abs_params(hi->input, ABS_Y, 57 field->logical_minimum, 58 field->logical_maximum, 0, 0); 59 return 1; 60 } 61 return 0; 62 63 case HID_UP_DIGITIZER: 64 switch (usage->hid) { 65 /* we do not want to map these for now */ 66 case HID_DG_INVERT: /* value is always 0 */ 67 case HID_DG_ERASER: /* value is always 0 */ 68 case HID_DG_CONTACTID: /* value is useless */ 69 case HID_DG_BARRELSWITCH: /* doubtful */ 70 case HID_DG_INPUTMODE: 71 case HID_DG_DEVICEINDEX: 72 case HID_DG_CONTACTCOUNT: 73 case HID_DG_CONTACTMAX: 74 return -1; 75 76 /* original mapping by Rafi Rubin */ 77 case HID_DG_CONFIDENCE: 78 nt_map_key_clear(BTN_TOOL_DOUBLETAP); 79 return 1; 80 81 /* width/height mapped on TouchMajor/TouchMinor/Orientation */ 82 case HID_DG_WIDTH: 83 hid_map_usage(hi, usage, bit, max, 84 EV_ABS, ABS_MT_TOUCH_MAJOR); 85 return 1; 86 case HID_DG_HEIGHT: 87 hid_map_usage(hi, usage, bit, max, 88 EV_ABS, ABS_MT_TOUCH_MINOR); 89 input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 90 0, 1, 0, 0); 91 return 1; 92 } 93 return 0; 94 95 case 0xff000000: 96 /* we do not want to map these: no input-oriented meaning */ 97 return -1; 98 } 99 100 return 0; 101} 102 103static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, 104 struct hid_field *field, struct hid_usage *usage, 105 unsigned long **bit, int *max) 106{ 107 if (usage->type == EV_KEY || usage->type == EV_REL 108 || usage->type == EV_ABS) 109 clear_bit(usage->code, *bit); 110 111 return 0; 112} 113 114/* 115 * this function is called upon all reports 116 * so that we can filter contact point information, 117 * decide whether we are in multi or single touch mode 118 * and call input_mt_sync after each point if necessary 119 */ 120static int ntrig_event (struct hid_device *hid, struct hid_field *field, 121 struct hid_usage *usage, __s32 value) 122{ 123 struct input_dev *input = field->hidinput->input; 124 struct ntrig_data *nd = hid_get_drvdata(hid); 125 126 if (hid->claimed & HID_CLAIMED_INPUT) { 127 switch (usage->hid) { 128 case HID_GD_X: 129 nd->x = value; 130 nd->reading_a_point = 1; 131 break; 132 case HID_GD_Y: 133 nd->y = value; 134 break; 135 case HID_DG_CONTACTID: 136 nd->id = value; 137 /* we receive this only when in multitouch mode */ 138 nd->found_contact_id = 1; 139 break; 140 case HID_DG_WIDTH: 141 nd->w = value; 142 break; 143 case HID_DG_HEIGHT: 144 nd->h = value; 145 /* 146 * when in single touch mode, this is the last 147 * report received in a finger event. We want 148 * to emit a normal (X, Y) position 149 */ 150 if (! nd->found_contact_id) { 151 input_event(input, EV_ABS, ABS_X, nd->x); 152 input_event(input, EV_ABS, ABS_Y, nd->y); 153 } 154 break; 155 case HID_DG_TIPPRESSURE: 156 /* 157 * when in single touch mode, this is the last 158 * report received in a pen event. We want 159 * to emit a normal (X, Y) position 160 */ 161 if (! nd->found_contact_id) { 162 input_event(input, EV_ABS, ABS_X, nd->x); 163 input_event(input, EV_ABS, ABS_Y, nd->y); 164 input_event(input, EV_ABS, ABS_PRESSURE, value); 165 } 166 break; 167 case 0xff000002: 168 /* 169 * we receive this when the device is in multitouch 170 * mode. The first of the three values tagged with 171 * this usage tells if the contact point is real 172 * or a placeholder 173 */ 174 if (!nd->reading_a_point || value != 1) 175 break; 176 /* emit a normal (X, Y) for the first point only */ 177 if (nd->id == 0) { 178 input_event(input, EV_ABS, ABS_X, nd->x); 179 input_event(input, EV_ABS, ABS_Y, nd->y); 180 } 181 input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); 182 input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); 183 if (nd->w > nd->h) { 184 input_event(input, EV_ABS, 185 ABS_MT_ORIENTATION, 1); 186 input_event(input, EV_ABS, 187 ABS_MT_TOUCH_MAJOR, nd->w); 188 input_event(input, EV_ABS, 189 ABS_MT_TOUCH_MINOR, nd->h); 190 } else { 191 input_event(input, EV_ABS, 192 ABS_MT_ORIENTATION, 0); 193 input_event(input, EV_ABS, 194 ABS_MT_TOUCH_MAJOR, nd->h); 195 input_event(input, EV_ABS, 196 ABS_MT_TOUCH_MINOR, nd->w); 197 } 198 input_mt_sync(field->hidinput->input); 199 nd->reading_a_point = 0; 200 nd->found_contact_id = 0; 201 break; 202 203 default: 204 /* fallback to the generic hidinput handling */ 205 return 0; 206 } 207 } 208 209 /* we have handled the hidinput part, now remains hiddev */ 210 if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) 211 hid->hiddev_hid_event(hid, field, usage, value); 212 213 return 1; 214} 215 216static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) 217{ 218 int ret; 219 struct ntrig_data *nd; 220 221 nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); 222 if (!nd) { 223 dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); 224 return -ENOMEM; 225 } 226 nd->reading_a_point = 0; 227 nd->found_contact_id = 0; 228 hid_set_drvdata(hdev, nd); 229 230 ret = hid_parse(hdev); 231 if (!ret) 232 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 233 234 if (ret) 235 kfree (nd); 236 return ret; 237} 238 239static void ntrig_remove(struct hid_device *hdev) 240{ 241 hid_hw_stop(hdev); 242 kfree(hid_get_drvdata(hdev)); 243} 244 245static const struct hid_device_id ntrig_devices[] = { 246 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN), 247 .driver_data = NTRIG_DUPLICATE_USAGES }, 248 { } 249}; 250MODULE_DEVICE_TABLE(hid, ntrig_devices); 251 252static const struct hid_usage_id ntrig_grabbed_usages[] = { 253 { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, 254 { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} 255}; 256 257static struct hid_driver ntrig_driver = { 258 .name = "ntrig", 259 .id_table = ntrig_devices, 260 .probe = ntrig_probe, 261 .remove = ntrig_remove, 262 .input_mapping = ntrig_input_mapping, 263 .input_mapped = ntrig_input_mapped, 264 .usage_table = ntrig_grabbed_usages, 265 .event = ntrig_event, 266}; 267 268static int ntrig_init(void) 269{ 270 return hid_register_driver(&ntrig_driver); 271} 272 273static void ntrig_exit(void) 274{ 275 hid_unregister_driver(&ntrig_driver); 276} 277 278module_init(ntrig_init); 279module_exit(ntrig_exit); 280MODULE_LICENSE("GPL"); 281