From 14ff3671081190349b51d0183fa7c24be1b852aa Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 25 Aug 2010 13:02:14 +0200 Subject: initial version with Dell XT2 support only --- usr/src/dkms_source_tree/hid-ntrig.c | 362 +++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 usr/src/dkms_source_tree/hid-ntrig.c (limited to 'usr/src/dkms_source_tree/hid-ntrig.c') 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 @@ +/* + * HID driver for N-Trig touchscreens + * + * Copyright (c) 2008-2010 Rafi Rubin + * Copyright (c) 2009-2010 Stephane Chatty + * + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include + +#include "hid-ids.h" + +void usbhid_submit_report +(struct hid_device *hid, struct hid_report *report, unsigned char dir); + +#define NTRIG_DUPLICATE_USAGES 0x001 + +struct mt_data { + int x, y, w, h; + int tipswitch; + int confidence; + int in_range; + int foot[4]; + unsigned int nfoot; +}; + +struct ntrig_data { + unsigned int id; + struct mt_data data[16]; + int contactcount; +}; + +#define SN_MOVE 100 +#define SN_SIZE 50 + +/* + * this driver is aimed at two firmware versions in circulation: + * - dual pen/finger single touch + * - finger multitouch, pen not working + */ + +static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + int delta = field->logical_maximum - field->logical_minimum; + + /* No special mappings needed for the pen and single touch */ + if (field->physical) + return 0; + + switch (usage->hid & HID_USAGE_PAGE) { + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + input_set_abs_params(hi->input, ABS_MT_POSITION_X, + field->logical_minimum, + field->logical_maximum, + delta / SN_MOVE, 0); + input_set_abs_params(hi->input, ABS_X, + field->logical_minimum, + field->logical_maximum, + delta / SN_MOVE, 0); + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + input_set_abs_params(hi->input, ABS_MT_POSITION_Y, + field->logical_minimum, + field->logical_maximum, + delta / SN_MOVE, 0); + input_set_abs_params(hi->input, ABS_Y, + field->logical_minimum, + field->logical_maximum, + delta / SN_MOVE, 0); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + /* we do not want to map these for now */ + case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */ + case HID_DG_INPUTMODE: + case HID_DG_DEVICEINDEX: + case HID_DG_CONTACTMAX: + return -1; + + /* width/height mapped on TouchMajor/TouchMinor/Orientation */ + case HID_DG_WIDTH: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MAJOR); + input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, + field->logical_minimum, + field->logical_maximum, + delta / SN_SIZE, 0); + return 1; + case HID_DG_HEIGHT: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MINOR); + input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, + field->logical_minimum, + field->logical_maximum, + delta / SN_SIZE, 0); + input_set_abs_params(hi->input, ABS_MT_ORIENTATION, + 0, 1, 0, 0); + return 1; + } + return 0; + + case 0xff000000: + /* we do not want to map these: no input-oriented meaning */ + return -1; + } + + return 0; +} + +static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* No special mappings needed for the pen and single touch */ + if (field->physical) + return 0; + + if (usage->type == EV_KEY || usage->type == EV_REL + || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} + +/* + * this function is called upon all reports + * so that we can filter contact point information, + * decide whether we are in multi or single touch mode + * and call input_mt_sync after each point if necessary + */ +static int ntrig_event (struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct input_dev *input = field->hidinput->input; + struct ntrig_data *nd = hid_get_drvdata(hid); + struct mt_data *p = &nd->data[nd->id]; + int i, count; + + /* No special handling needed for the pen */ + if (field->application == HID_DG_PEN) + return 0; + + if (hid->claimed & HID_CLAIMED_INPUT) { + switch (usage->hid) { + case HID_DG_INRANGE: + p->in_range = value; + break; + /* new mt packet */ + case 0xff000001: + count = nd->contactcount; + for (i = 0; i < count; i++) { + p = &nd->data[i]; + input_report_abs(input, ABS_MT_POSITION_X, p->x); + input_report_abs(input, ABS_MT_POSITION_Y, p->y); + input_report_abs(input, ABS_MT_TOUCH_MINOR, p->h); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, p->w); + input_report_abs(input, ABS_MT_ORIENTATION, p->w > p->h); + input_mt_sync(field->hidinput->input); + } + + input_mt_sync(input); + input_report_key(input, BTN_TOUCH, count > 0); + if (count > 0) { + p = &nd->data[0]; + input_report_abs(input, ABS_X, p->x); + input_report_abs(input, ABS_Y, p->y); + } + //input_sync(input); + + p = &nd->data[nd->id = 0]; + memset(p, 0, sizeof(*p)); + break; + case HID_DG_TIPSWITCH: + p->tipswitch = value; + break; + case HID_DG_CONFIDENCE: + p->confidence = value; + break; + case HID_DG_CONTACTID: + if (value >= 6) + break; + p = &nd->data[nd->id = value]; + memset(p, 0, sizeof(*p)); + break; + case HID_GD_X: + p->x = value; + break; + case HID_GD_Y: + p->y = value; + break; + case HID_DG_WIDTH: + p->w = value; + break; + case HID_DG_HEIGHT: + p->h = value; + break; + case 0xff000002: + if (p->nfoot < 4) + p->foot[p->nfoot++] = value; + else + printk(KERN_INFO "NTRIG: lots %d\n", value); + break; + + case HID_DG_CONTACTCOUNT: + nd->contactcount = value; + break; + + default: + return 0; + } + } + + /* we have handled the hidinput part, now remains hiddev */ + if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct ntrig_data *nd; + struct hid_input *hidinput; + struct input_dev *input; + struct hid_report *report; + + if (id->driver_data & NTRIG_DUPLICATE_USAGES) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + //hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + + nd = kzalloc(sizeof(struct ntrig_data), GFP_KERNEL); + if (!nd) { + dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); + return -ENOMEM; + } + + hid_set_drvdata(hdev, nd); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + + list_for_each_entry(hidinput, &hdev->inputs, list) { + if (hidinput->report->maxfield < 1) + + continue; + + input = hidinput->input; + switch (hidinput->report->field[0]->application) { + case HID_DG_PEN: + input->name = "N-Trig Pen"; + break; + case HID_DG_TOUCHSCREEN: + /* These keys are redundant for fingers, clear them + * to prevent incorrect identification */ + __clear_bit(BTN_TOOL_PEN, input->keybit); + __clear_bit(BTN_TOOL_FINGER, input->keybit); + __clear_bit(BTN_0, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + /* + * The physical touchscreen (single touch) + * input has a value for physical, whereas + * the multitouch only has logical input + * fields. + */ + input->name = + (hidinput->report->field[0] + ->physical) ? + "N-Trig Touchscreen" : + "N-Trig MultiTouch"; + break; + } + } + + /* This is needed for devices with more recent firmware versions */ + report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; + if (report) + usbhid_submit_report(hdev, report, USB_DIR_OUT); + + return 0; +err_free: + kfree(nd); + return ret; +} + +static void ntrig_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); +} + +#define NTRIG_DEVICE(id) \ + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, id), \ + .driver_data = NTRIG_DUPLICATE_USAGES } + +static const struct hid_device_id ntrig_devices[] = { + NTRIG_DEVICE(0x0001), + { } +}; +MODULE_DEVICE_TABLE(hid, ntrig_devices); + +static const struct hid_usage_id ntrig_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } +}; + +static struct hid_driver ntrig_driver = { + .name = "ntrig", + .id_table = ntrig_devices, + .probe = ntrig_probe, + .remove = ntrig_remove, + .input_mapping = ntrig_input_mapping, + .input_mapped = ntrig_input_mapped, + .usage_table = ntrig_grabbed_usages, + .event = ntrig_event, +}; + +static int __init ntrig_init(void) +{ + return hid_register_driver(&ntrig_driver); +} + +static void __exit ntrig_exit(void) +{ + hid_unregister_driver(&ntrig_driver); +} + +module_init(ntrig_init); +module_exit(ntrig_exit); +MODULE_LICENSE("GPL"); -- cgit v1.2.3