diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2010-08-25 13:02:14 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2010-08-25 13:02:14 +0200 |
| commit | 14ff3671081190349b51d0183fa7c24be1b852aa (patch) | |
| tree | 2325b61c8a02195edb300c909d7914506e423a28 /usr/src/dkms_source_tree/hid-ntrig.c | |
initial version with Dell XT2 support only
Diffstat (limited to 'usr/src/dkms_source_tree/hid-ntrig.c')
| -rw-r--r-- | usr/src/dkms_source_tree/hid-ntrig.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/usr/src/dkms_source_tree/hid-ntrig.c b/usr/src/dkms_source_tree/hid-ntrig.c new file mode 100644 index 0000000..7bc1a5e --- /dev/null +++ b/usr/src/dkms_source_tree/hid-ntrig.c | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | /* | ||
| 2 | * HID driver for N-Trig touchscreens | ||
| 3 | * | ||
| 4 | * Copyright (c) 2008-2010 Rafi Rubin | ||
| 5 | * Copyright (c) 2009-2010 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/usb.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | |||
| 22 | #include "hid-ids.h" | ||
| 23 | |||
| 24 | void usbhid_submit_report | ||
| 25 | (struct hid_device *hid, struct hid_report *report, unsigned char dir); | ||
| 26 | |||
| 27 | #define NTRIG_DUPLICATE_USAGES 0x001 | ||
| 28 | |||
| 29 | struct mt_data { | ||
| 30 | int x, y, w, h; | ||
| 31 | int tipswitch; | ||
| 32 | int confidence; | ||
| 33 | int in_range; | ||
| 34 | int foot[4]; | ||
| 35 | unsigned int nfoot; | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct ntrig_data { | ||
| 39 | unsigned int id; | ||
| 40 | struct mt_data data[16]; | ||
| 41 | int contactcount; | ||
| 42 | }; | ||
| 43 | |||
| 44 | #define SN_MOVE 100 | ||
| 45 | #define SN_SIZE 50 | ||
| 46 | |||
| 47 | /* | ||
| 48 | * this driver is aimed at two firmware versions in circulation: | ||
| 49 | * - dual pen/finger single touch | ||
| 50 | * - finger multitouch, pen not working | ||
| 51 | */ | ||
| 52 | |||
| 53 | static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
| 54 | struct hid_field *field, struct hid_usage *usage, | ||
| 55 | unsigned long **bit, int *max) | ||
| 56 | { | ||
| 57 | int delta = field->logical_maximum - field->logical_minimum; | ||
| 58 | |||
| 59 | /* No special mappings needed for the pen and single touch */ | ||
| 60 | if (field->physical) | ||
| 61 | return 0; | ||
| 62 | |||
| 63 | switch (usage->hid & HID_USAGE_PAGE) { | ||
| 64 | case HID_UP_GENDESK: | ||
| 65 | switch (usage->hid) { | ||
| 66 | case HID_GD_X: | ||
| 67 | hid_map_usage(hi, usage, bit, max, | ||
| 68 | EV_ABS, ABS_MT_POSITION_X); | ||
| 69 | input_set_abs_params(hi->input, ABS_MT_POSITION_X, | ||
| 70 | field->logical_minimum, | ||
| 71 | field->logical_maximum, | ||
| 72 | delta / SN_MOVE, 0); | ||
| 73 | input_set_abs_params(hi->input, ABS_X, | ||
| 74 | field->logical_minimum, | ||
| 75 | field->logical_maximum, | ||
| 76 | delta / SN_MOVE, 0); | ||
| 77 | return 1; | ||
| 78 | case HID_GD_Y: | ||
| 79 | hid_map_usage(hi, usage, bit, max, | ||
| 80 | EV_ABS, ABS_MT_POSITION_Y); | ||
| 81 | input_set_abs_params(hi->input, ABS_MT_POSITION_Y, | ||
| 82 | field->logical_minimum, | ||
| 83 | field->logical_maximum, | ||
| 84 | delta / SN_MOVE, 0); | ||
| 85 | input_set_abs_params(hi->input, ABS_Y, | ||
| 86 | field->logical_minimum, | ||
| 87 | field->logical_maximum, | ||
| 88 | delta / SN_MOVE, 0); | ||
| 89 | return 1; | ||
| 90 | } | ||
| 91 | return 0; | ||
| 92 | |||
| 93 | case HID_UP_DIGITIZER: | ||
| 94 | switch (usage->hid) { | ||
| 95 | /* we do not want to map these for now */ | ||
| 96 | case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */ | ||
| 97 | case HID_DG_INPUTMODE: | ||
| 98 | case HID_DG_DEVICEINDEX: | ||
| 99 | case HID_DG_CONTACTMAX: | ||
| 100 | return -1; | ||
| 101 | |||
| 102 | /* width/height mapped on TouchMajor/TouchMinor/Orientation */ | ||
| 103 | case HID_DG_WIDTH: | ||
| 104 | hid_map_usage(hi, usage, bit, max, | ||
| 105 | EV_ABS, ABS_MT_TOUCH_MAJOR); | ||
| 106 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, | ||
| 107 | field->logical_minimum, | ||
| 108 | field->logical_maximum, | ||
| 109 | delta / SN_SIZE, 0); | ||
| 110 | return 1; | ||
| 111 | case HID_DG_HEIGHT: | ||
| 112 | hid_map_usage(hi, usage, bit, max, | ||
| 113 | EV_ABS, ABS_MT_TOUCH_MINOR); | ||
| 114 | input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, | ||
| 115 | field->logical_minimum, | ||
| 116 | field->logical_maximum, | ||
| 117 | delta / SN_SIZE, 0); | ||
| 118 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, | ||
| 119 | 0, 1, 0, 0); | ||
| 120 | return 1; | ||
| 121 | } | ||
| 122 | return 0; | ||
| 123 | |||
| 124 | case 0xff000000: | ||
| 125 | /* we do not want to map these: no input-oriented meaning */ | ||
| 126 | return -1; | ||
| 127 | } | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, | ||
| 133 | struct hid_field *field, struct hid_usage *usage, | ||
| 134 | unsigned long **bit, int *max) | ||
| 135 | { | ||
| 136 | /* No special mappings needed for the pen and single touch */ | ||
| 137 | if (field->physical) | ||
| 138 | return 0; | ||
| 139 | |||
| 140 | if (usage->type == EV_KEY || usage->type == EV_REL | ||
| 141 | || usage->type == EV_ABS) | ||
| 142 | clear_bit(usage->code, *bit); | ||
| 143 | |||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* | ||
| 148 | * this function is called upon all reports | ||
| 149 | * so that we can filter contact point information, | ||
| 150 | * decide whether we are in multi or single touch mode | ||
| 151 | * and call input_mt_sync after each point if necessary | ||
| 152 | */ | ||
| 153 | static int ntrig_event (struct hid_device *hid, struct hid_field *field, | ||
| 154 | struct hid_usage *usage, __s32 value) | ||
| 155 | { | ||
| 156 | struct input_dev *input = field->hidinput->input; | ||
| 157 | struct ntrig_data *nd = hid_get_drvdata(hid); | ||
| 158 | struct mt_data *p = &nd->data[nd->id]; | ||
| 159 | int i, count; | ||
| 160 | |||
| 161 | /* No special handling needed for the pen */ | ||
| 162 | if (field->application == HID_DG_PEN) | ||
| 163 | return 0; | ||
| 164 | |||
| 165 | if (hid->claimed & HID_CLAIMED_INPUT) { | ||
| 166 | switch (usage->hid) { | ||
| 167 | case HID_DG_INRANGE: | ||
| 168 | p->in_range = value; | ||
| 169 | break; | ||
| 170 | /* new mt packet */ | ||
| 171 | case 0xff000001: | ||
| 172 | count = nd->contactcount; | ||
| 173 | for (i = 0; i < count; i++) { | ||
| 174 | p = &nd->data[i]; | ||
| 175 | input_report_abs(input, ABS_MT_POSITION_X, p->x); | ||
| 176 | input_report_abs(input, ABS_MT_POSITION_Y, p->y); | ||
| 177 | input_report_abs(input, ABS_MT_TOUCH_MINOR, p->h); | ||
| 178 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, p->w); | ||
| 179 | input_report_abs(input, ABS_MT_ORIENTATION, p->w > p->h); | ||
| 180 | input_mt_sync(field->hidinput->input); | ||
| 181 | } | ||
| 182 | |||
| 183 | input_mt_sync(input); | ||
| 184 | input_report_key(input, BTN_TOUCH, count > 0); | ||
| 185 | if (count > 0) { | ||
| 186 | p = &nd->data[0]; | ||
| 187 | input_report_abs(input, ABS_X, p->x); | ||
| 188 | input_report_abs(input, ABS_Y, p->y); | ||
| 189 | } | ||
| 190 | //input_sync(input); | ||
| 191 | |||
| 192 | p = &nd->data[nd->id = 0]; | ||
| 193 | memset(p, 0, sizeof(*p)); | ||
| 194 | break; | ||
| 195 | case HID_DG_TIPSWITCH: | ||
| 196 | p->tipswitch = value; | ||
| 197 | break; | ||
| 198 | case HID_DG_CONFIDENCE: | ||
| 199 | p->confidence = value; | ||
| 200 | break; | ||
| 201 | case HID_DG_CONTACTID: | ||
| 202 | if (value >= 6) | ||
| 203 | break; | ||
| 204 | p = &nd->data[nd->id = value]; | ||
| 205 | memset(p, 0, sizeof(*p)); | ||
| 206 | break; | ||
| 207 | case HID_GD_X: | ||
| 208 | p->x = value; | ||
| 209 | break; | ||
| 210 | case HID_GD_Y: | ||
| 211 | p->y = value; | ||
| 212 | break; | ||
| 213 | case HID_DG_WIDTH: | ||
| 214 | p->w = value; | ||
| 215 | break; | ||
| 216 | case HID_DG_HEIGHT: | ||
| 217 | p->h = value; | ||
| 218 | break; | ||
| 219 | case 0xff000002: | ||
| 220 | if (p->nfoot < 4) | ||
| 221 | p->foot[p->nfoot++] = value; | ||
| 222 | else | ||
| 223 | printk(KERN_INFO "NTRIG: lots %d\n", value); | ||
| 224 | break; | ||
| 225 | |||
| 226 | case HID_DG_CONTACTCOUNT: | ||
| 227 | nd->contactcount = value; | ||
| 228 | break; | ||
| 229 | |||
| 230 | default: | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | /* we have handled the hidinput part, now remains hiddev */ | ||
| 236 | if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) | ||
| 237 | hid->hiddev_hid_event(hid, field, usage, value); | ||
| 238 | |||
| 239 | return 1; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
| 243 | { | ||
| 244 | int ret; | ||
| 245 | struct ntrig_data *nd; | ||
| 246 | struct hid_input *hidinput; | ||
| 247 | struct input_dev *input; | ||
| 248 | struct hid_report *report; | ||
| 249 | |||
| 250 | if (id->driver_data & NTRIG_DUPLICATE_USAGES) | ||
| 251 | hdev->quirks |= HID_QUIRK_MULTI_INPUT; | ||
| 252 | //hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; | ||
| 253 | |||
| 254 | nd = kzalloc(sizeof(struct ntrig_data), GFP_KERNEL); | ||
| 255 | if (!nd) { | ||
| 256 | dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); | ||
| 257 | return -ENOMEM; | ||
| 258 | } | ||
| 259 | |||
| 260 | hid_set_drvdata(hdev, nd); | ||
| 261 | |||
| 262 | ret = hid_parse(hdev); | ||
| 263 | if (ret) { | ||
| 264 | dev_err(&hdev->dev, "parse failed\n"); | ||
| 265 | goto err_free; | ||
| 266 | } | ||
| 267 | |||
| 268 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); | ||
| 269 | if (ret) { | ||
| 270 | dev_err(&hdev->dev, "hw start failed\n"); | ||
| 271 | goto err_free; | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | list_for_each_entry(hidinput, &hdev->inputs, list) { | ||
| 276 | if (hidinput->report->maxfield < 1) | ||
| 277 | |||
| 278 | continue; | ||
| 279 | |||
| 280 | input = hidinput->input; | ||
| 281 | switch (hidinput->report->field[0]->application) { | ||
| 282 | case HID_DG_PEN: | ||
| 283 | input->name = "N-Trig Pen"; | ||
| 284 | break; | ||
| 285 | case HID_DG_TOUCHSCREEN: | ||
| 286 | /* These keys are redundant for fingers, clear them | ||
| 287 | * to prevent incorrect identification */ | ||
| 288 | __clear_bit(BTN_TOOL_PEN, input->keybit); | ||
| 289 | __clear_bit(BTN_TOOL_FINGER, input->keybit); | ||
| 290 | __clear_bit(BTN_0, input->keybit); | ||
| 291 | __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); | ||
| 292 | /* | ||
| 293 | * The physical touchscreen (single touch) | ||
| 294 | * input has a value for physical, whereas | ||
| 295 | * the multitouch only has logical input | ||
| 296 | * fields. | ||
| 297 | */ | ||
| 298 | input->name = | ||
| 299 | (hidinput->report->field[0] | ||
| 300 | ->physical) ? | ||
| 301 | "N-Trig Touchscreen" : | ||
| 302 | "N-Trig MultiTouch"; | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /* This is needed for devices with more recent firmware versions */ | ||
| 308 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; | ||
| 309 | if (report) | ||
| 310 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
| 311 | |||
| 312 | return 0; | ||
| 313 | err_free: | ||
| 314 | kfree(nd); | ||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | static void ntrig_remove(struct hid_device *hdev) | ||
| 319 | { | ||
| 320 | hid_hw_stop(hdev); | ||
| 321 | kfree(hid_get_drvdata(hdev)); | ||
| 322 | } | ||
| 323 | |||
| 324 | #define NTRIG_DEVICE(id) \ | ||
| 325 | { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, id), \ | ||
| 326 | .driver_data = NTRIG_DUPLICATE_USAGES } | ||
| 327 | |||
| 328 | static const struct hid_device_id ntrig_devices[] = { | ||
| 329 | NTRIG_DEVICE(0x0001), | ||
| 330 | { } | ||
| 331 | }; | ||
| 332 | MODULE_DEVICE_TABLE(hid, ntrig_devices); | ||
| 333 | |||
| 334 | static const struct hid_usage_id ntrig_grabbed_usages[] = { | ||
| 335 | { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, | ||
| 336 | { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } | ||
| 337 | }; | ||
| 338 | |||
| 339 | static struct hid_driver ntrig_driver = { | ||
| 340 | .name = "ntrig", | ||
| 341 | .id_table = ntrig_devices, | ||
| 342 | .probe = ntrig_probe, | ||
| 343 | .remove = ntrig_remove, | ||
| 344 | .input_mapping = ntrig_input_mapping, | ||
| 345 | .input_mapped = ntrig_input_mapped, | ||
| 346 | .usage_table = ntrig_grabbed_usages, | ||
| 347 | .event = ntrig_event, | ||
| 348 | }; | ||
| 349 | |||
| 350 | static int __init ntrig_init(void) | ||
| 351 | { | ||
| 352 | return hid_register_driver(&ntrig_driver); | ||
| 353 | } | ||
| 354 | |||
| 355 | static void __exit ntrig_exit(void) | ||
| 356 | { | ||
| 357 | hid_unregister_driver(&ntrig_driver); | ||
| 358 | } | ||
| 359 | |||
| 360 | module_init(ntrig_init); | ||
| 361 | module_exit(ntrig_exit); | ||
| 362 | MODULE_LICENSE("GPL"); | ||
