aboutsummaryrefslogtreecommitdiff
path: root/drivers/hid/hid-ntrig.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-ntrig.c')
-rw-r--r--drivers/hid/hid-ntrig.c223
1 files changed, 158 insertions, 65 deletions
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 49ce69d7bba..edcc0c4247b 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -25,11 +25,16 @@
EV_KEY, (c))
struct ntrig_data {
- __s32 x, y, id, w, h;
- char reading_a_point, found_contact_id;
- char pen_active;
- char finger_active;
- char inverted;
+ /* Incoming raw values for a single contact */
+ __u16 x, y, w, h;
+ __u16 id;
+ __u8 confidence;
+
+ bool reading_mt;
+ __u8 first_contact_confidence;
+
+ __u8 mt_footer[4];
+ __u8 mt_foot_count;
};
/*
@@ -42,8 +47,11 @@ 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)
{
- switch (usage->hid & HID_USAGE_PAGE) {
+ /* 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:
@@ -66,18 +74,12 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
/* we do not want to map these for now */
- case HID_DG_CONTACTID: /* value is useless */
+ case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */
case HID_DG_INPUTMODE:
case HID_DG_DEVICEINDEX:
- case HID_DG_CONTACTCOUNT:
case HID_DG_CONTACTMAX:
return -1;
- /* original mapping by Rafi Rubin */
- case HID_DG_CONFIDENCE:
- nt_map_key_clear(BTN_TOOL_DOUBLETAP);
- return 1;
-
/* width/height mapped on TouchMajor/TouchMinor/Orientation */
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
@@ -104,6 +106,10 @@ 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);
@@ -123,31 +129,33 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
struct input_dev *input = field->hidinput->input;
struct ntrig_data *nd = hid_get_drvdata(hid);
+ /* 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:
- if (field->application & 0x3)
- nd->pen_active = (value != 0);
- else
- nd->finger_active = (value != 0);
- return 0;
-
- case HID_DG_INVERT:
- nd->inverted = value;
- return 0;
-
+ case 0xff000001:
+ /* Tag indicating the start of a multitouch group */
+ nd->reading_mt = 1;
+ nd->first_contact_confidence = 0;
+ break;
+ case HID_DG_TIPSWITCH:
+ /* Prevent emission of touch until validated */
+ return 1;
+ case HID_DG_CONFIDENCE:
+ nd->confidence = value;
+ break;
case HID_GD_X:
nd->x = value;
- nd->reading_a_point = 1;
+ /* Clear the contact footer */
+ nd->mt_foot_count = 0;
break;
case HID_GD_Y:
nd->y = value;
break;
case HID_DG_CONTACTID:
nd->id = value;
- /* we receive this only when in multitouch mode */
- nd->found_contact_id = 1;
break;
case HID_DG_WIDTH:
nd->w = value;
@@ -159,33 +167,11 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* report received in a finger event. We want
* to emit a normal (X, Y) position
*/
- if (!nd->found_contact_id) {
- if (nd->pen_active && nd->finger_active) {
- input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
- input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
- }
- input_event(input, EV_ABS, ABS_X, nd->x);
- input_event(input, EV_ABS, ABS_Y, nd->y);
- }
- break;
- case HID_DG_TIPPRESSURE:
- /*
- * when in single touch mode, this is the last
- * report received in a pen event. We want
- * to emit a normal (X, Y) position
- */
- if (! nd->found_contact_id) {
- if (nd->pen_active && nd->finger_active) {
- input_report_key(input,
- nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
- , 0);
- input_report_key(input,
- nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
- , 1);
- }
+ if (!nd->reading_mt) {
+ input_report_key(input, BTN_TOOL_DOUBLETAP,
+ (nd->confidence != 0));
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
- input_event(input, EV_ABS, ABS_PRESSURE, value);
}
break;
case 0xff000002:
@@ -195,10 +181,34 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* this usage tells if the contact point is real
* or a placeholder
*/
- if (!nd->reading_a_point || value != 1)
+
+ /* Shouldn't get more than 4 footer packets, so skip */
+ if (nd->mt_foot_count >= 4)
break;
+
+ nd->mt_footer[nd->mt_foot_count++] = value;
+
+ /* if the footer isn't complete break */
+ if (nd->mt_foot_count != 4)
+ break;
+
+ /* Pen activity signal, trigger end of touch. */
+ if (nd->mt_footer[2]) {
+ nd->confidence = 0;
+ break;
+ }
+
+ /* If the contact was invalid */
+ if (!(nd->confidence && nd->mt_footer[0])
+ || nd->w <= 250
+ || nd->h <= 190) {
+ nd->confidence = 0;
+ break;
+ }
+
/* emit a normal (X, Y) for the first point only */
if (nd->id == 0) {
+ nd->first_contact_confidence = nd->confidence;
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
}
@@ -220,8 +230,40 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
ABS_MT_TOUCH_MINOR, nd->w);
}
input_mt_sync(field->hidinput->input);
- nd->reading_a_point = 0;
- nd->found_contact_id = 0;
+ break;
+
+ case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
+ if (!nd->reading_mt)
+ break;
+
+ nd->reading_mt = 0;
+
+ if (nd->first_contact_confidence) {
+ switch (value) {
+ case 0: /* for single touch devices */
+ case 1:
+ input_report_key(input,
+ BTN_TOOL_DOUBLETAP, 1);
+ break;
+ case 2:
+ input_report_key(input,
+ BTN_TOOL_TRIPLETAP, 1);
+ break;
+ case 3:
+ default:
+ input_report_key(input,
+ BTN_TOOL_QUADTAP, 1);
+ }
+ input_report_key(input, BTN_TOUCH, 1);
+ } else {
+ input_report_key(input,
+ BTN_TOOL_DOUBLETAP, 0);
+ input_report_key(input,
+ BTN_TOOL_TRIPLETAP, 0);
+ input_report_key(input,
+ BTN_TOOL_QUADTAP, 0);
+ input_report_key(input, BTN_TOUCH, 0);
+ }
break;
default:
@@ -231,8 +273,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
}
/* 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);
+ if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
return 1;
}
@@ -241,23 +283,74 @@ 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;
+
+ if (id->driver_data)
+ hdev->quirks |= HID_QUIRK_MULTI_INPUT;
nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL);
if (!nd) {
dev_err(&hdev->dev, "cannot allocate N-Trig data\n");
return -ENOMEM;
}
- nd->reading_a_point = 0;
- nd->found_contact_id = 0;
+
+ nd->reading_mt = 0;
hid_set_drvdata(hdev, nd);
ret = hid_parse(hdev);
- if (!ret)
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err_free;
+ }
- if (ret)
- kfree (nd);
+ 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);
+ /*
+ * A little something special to enable
+ * two and three finger taps.
+ */
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, 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;
+ }
+ }
+
+ return 0;
+err_free:
+ kfree(nd);
return ret;
}
@@ -276,7 +369,7 @@ 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}
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 }
};
static struct hid_driver ntrig_driver = {