From 09a1cadbf817f4db57c80687465ff98de84b3476 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 12 Oct 2010 10:40:31 +0200 Subject: New version based on patch series and testing on both devices This version works both devices supported by the driver, and is based on a patch set scheduled for upstream. Signed-off-by: Henrik Rydberg --- usr/src/dkms_source_tree/hid-egalax.c | 183 +++++++++++++++++----------------- 1 file changed, 91 insertions(+), 92 deletions(-) (limited to 'usr/src/dkms_source_tree') diff --git a/usr/src/dkms_source_tree/hid-egalax.c b/usr/src/dkms_source_tree/hid-egalax.c index c060dfa..1afc3a5 100644 --- a/usr/src/dkms_source_tree/hid-egalax.c +++ b/usr/src/dkms_source_tree/hid-egalax.c @@ -2,6 +2,8 @@ * HID driver for eGalax dual-touch panels * * Copyright (c) 2010 Stephane Chatty + * Copyright (c) 2010 Henrik Rydberg + * Copyright (c) 2010 Canonical, Ltd. * */ @@ -29,59 +31,59 @@ void usbhid_submit_report #define MAX_SLOTS 2 #define MAX_TRKID USHRT_MAX -#define MAX_EVENTS 360 +#define MAX_EVENTS 120 /* estimated signal-to-noise ratios */ #define SN_MOVE 1024 #define SN_PRESSURE 32 struct egalax_contact { - int id; + int touch; int x, y, z; }; struct egalax_data { struct egalax_contact contact[MAX_SLOTS]; - int down, valid; - int x, y, z; + struct egalax_contact single, tmp; + int valid; int slot; - int mask; int trkid; }; +static void set_abs(struct input_dev *input, unsigned int code, + struct hid_field *field, int snratio) +{ + int fmin = field->logical_minimum; + int fmax = field->logical_maximum; + int fuzz = snratio ? (fmax - fmin) / snratio : 0; + input_set_abs_params(input, code, fmin, fmax, fuzz, 0); +} + static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - int f1 = field->logical_minimum; - int f2 = field->logical_maximum; - int df = f2 - f1; + struct input_dev *input = hi->input; switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - f1 = 0; - f2 = 32768; - df = f2 - f1; + field->logical_maximum <<= 3; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); - input_set_abs_params(hi->input, ABS_MT_POSITION_X, - f1, f2, df / SN_MOVE, 0); - input_set_abs_params(hi->input, ABS_X, - f1, f2, df / SN_MOVE, 0); + set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE); + /* touchscreen emulation */ + set_abs(input, ABS_X, field, SN_MOVE); return 1; case HID_GD_Y: - f1 = 0; - f2 = 32768; - df = f2 - f1; + field->logical_maximum <<= 3; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); - input_set_abs_params(hi->input, ABS_MT_POSITION_Y, - f1, f2, df / SN_MOVE, 0); - input_set_abs_params(hi->input, ABS_Y, - f1, f2, df / SN_MOVE, 0); + set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE); + /* touchscreen emulation */ + set_abs(input, ABS_Y, field, SN_MOVE); return 1; } return 0; @@ -91,7 +93,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_TIPSWITCH: /* touchscreen emulation */ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); - input_set_capability(hi->input, EV_KEY, BTN_TOUCH); + input_set_capability(input, EV_KEY, BTN_TOUCH); return 1; case HID_DG_INRANGE: case HID_DG_CONFIDENCE: @@ -102,19 +104,17 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->logical_maximum = MAX_TRKID; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID); - input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, - 0, MAX_TRKID, 0, 0); - if (!hi->input->mt) - input_mt_create_slots(hi->input, MAX_SLOTS); - input_set_events_per_packet(hi->input, MAX_EVENTS); + set_abs(input, ABS_MT_TRACKING_ID, field, 0); + input_set_events_per_packet(input, MAX_EVENTS); + if (!input->mt) + input_mt_create_slots(input, MAX_SLOTS); return 1; case HID_DG_TIPPRESSURE: + field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_PRESSURE); - input_set_abs_params(hi->input, ABS_MT_PRESSURE, - f1, f2, df / SN_PRESSURE, 0); - input_set_abs_params(hi->input, ABS_PRESSURE, - f1, f2, df / SN_PRESSURE, 0); + EV_ABS, ABS_MT_PRESSURE); + set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE); + set_abs(input, ABS_PRESSURE, field, SN_PRESSURE); return 1; } return 0; @@ -134,43 +134,59 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi, return -1; } -static void report_contact(struct egalax_data *td, struct input_dev *input) +static void emulate_pointer(struct egalax_data *td, struct input_dev *input) { - struct egalax_contact *f = &td->contact[td->slot]; - struct egalax_contact *oldest = 0; - int i; - - if (!td->down) - f->id = -1; - else if (f->id < 0) - f->id = td->trkid++ & MAX_TRKID; - f->x = td->x; - f->y = td->y; - f->z = td->z; + struct egalax_contact *s = &td->single; + struct egalax_contact *best = 0; + int dbest, i; for (i = 0; i < MAX_SLOTS; i++) { - f = &td->contact[i]; - input_mt_slot(input, i); - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id); - if (f->id < 0) - continue; - input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); - input_event(input, EV_ABS, ABS_MT_PRESSURE, f->z); - if (!oldest) - oldest = f; + struct egalax_contact *f = &td->contact[i]; + if (f->touch) { + int d = abs(f->x - s->x) + abs(f->y - s->y); + if (!best || d < dbest) { + best = f; + dbest = d; + } + } } - if (oldest) { + if (best) { input_event(input, EV_KEY, BTN_TOUCH, 1); - input_event(input, EV_ABS, ABS_X, oldest->x); - input_event(input, EV_ABS, ABS_Y, oldest->y); - input_event(input, EV_ABS, ABS_PRESSURE, oldest->z); + input_event(input, EV_ABS, ABS_X, best->x); + input_event(input, EV_ABS, ABS_Y, best->y); + input_event(input, EV_ABS, ABS_PRESSURE, best->z); + *s = *best; } else { input_event(input, EV_KEY, BTN_TOUCH, 0); } +} + - input_sync(input); +/* + * this function is called when a whole finger has been parsed, + * so that it can decide what to send to the input layer. + */ +static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) +{ + struct egalax_contact *old = &td->contact[td->slot]; + struct egalax_contact *f = &td->tmp; + + input_mt_slot(input, td->slot); + if (f->touch) { + if (!old->touch) { + int id = td->trkid++ & MAX_TRKID; + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id); + } + input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); + input_event(input, EV_ABS, ABS_MT_PRESSURE, f->z); + } else { + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); + } + *old = *f; + + emulate_pointer(td, input); } static int egalax_event(struct hid_device *hid, struct hid_field *field, @@ -182,55 +198,43 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field, * uses a standard parallel multitouch protocol (product ID == * 48xx). The second is capacitive and uses an unusual "serial" * protocol with a different message for each multitouch finger - * (product ID == 72xx). We do not yet generate a correct event - * sequence for the capacitive/serial protocol. + * (product ID == 72xx). */ if (hid->claimed & HID_CLAIMED_INPUT) { struct input_dev *input = field->hidinput->input; - //printk(KERN_INFO "CHECK: %08x %d", usage->hid, value); - //return 1; - switch (usage->hid) { + case HID_DG_INRANGE: + td->valid = value; + break; case HID_DG_CONFIDENCE: - case HID_DG_CONTACTCOUNT: + /* avoid interference from generic hidinput handling */ break; - - case HID_DG_TIPSWITCH: - td->down = value; - td->mask |= 0x01; + case HID_DG_TIPSWITCH: + td->tmp.touch = value; break; - case HID_DG_INRANGE: - td->valid = value; - td->mask |= 0x02; + case HID_DG_TIPPRESSURE: + td->tmp.z = value; break; case HID_DG_CONTACTID: td->slot = clamp_val(value, 0, MAX_SLOTS - 1); - td->mask |= 0x04; break; case HID_GD_X: - td->x = value; - td->mask |= 0x08; + td->tmp.x = value; break; case HID_GD_Y: - td->y = value; - td->mask |= 0x10; + td->tmp.y = value; + /* this is the last field in a finger */ + egalax_filter_event(td, input); break; - case HID_DG_TIPPRESSURE: - td->z = value; - td->mask |= 0x20; + case HID_DG_CONTACTCOUNT: + /* touch emulation: this is the last field in a frame */ break; default: /* fallback to the generic hidinput handling */ return 0; } - - if (td->mask == 0x3f) { - td->mask = 0; - if (td->valid) - report_contact(td, input); - } } /* we have handled the hidinput part, now remains hiddev */ @@ -245,17 +249,12 @@ static int egalax_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; struct egalax_data *td; struct hid_report *report; - int i; - - hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; td = kzalloc(sizeof(struct egalax_data), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate eGalax data\n"); return -ENOMEM; } - for (i = 0; i < MAX_SLOTS; i++) - td->contact[i].id = -1; hid_set_drvdata(hdev, td); ret = hid_parse(hdev); -- cgit v1.2.3