From 4fe7e36aaa8465dd058287ebe0b22fa2bee55fad Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Fri, 10 Sep 2010 18:08:57 +0200 Subject: Update to maverick driver version Signed-off-by: Henrik Rydberg --- usr/src/dkms_source_tree/hid-ntrig.c | 339 +++++++++++++++++++++++------------ 1 file changed, 229 insertions(+), 110 deletions(-) (limited to 'usr/src/dkms_source_tree') diff --git a/usr/src/dkms_source_tree/hid-ntrig.c b/usr/src/dkms_source_tree/hid-ntrig.c index 70a2ccd..635b44d 100644 --- a/usr/src/dkms_source_tree/hid-ntrig.c +++ b/usr/src/dkms_source_tree/hid-ntrig.c @@ -3,6 +3,7 @@ * * Copyright (c) 2008-2010 Rafi Rubin * Copyright (c) 2009-2010 Stephane Chatty + * Copyright (c) 2010 Canonical, Ltd. * */ @@ -24,25 +25,87 @@ void usbhid_submit_report (struct hid_device *hid, struct hid_report *report, unsigned char dir); +#define hid_to_usb_dev(hid_dev) \ + container_of(hid_dev->dev.parent->parent, struct usb_device, dev) + #define NTRIG_DUPLICATE_USAGES 0x001 -struct mt_data { +#define MAX_SLOTS 20 +#define MAX_EVENTS 120 + +#define SN_MOVE_X 128 +#define SN_MOVE_Y 92 +#define SN_MAJOR 48 + +#define HOLD_MIN 3 +#define HOLD_MED 7 +#define HOLD_MAX 10 + +#define DIV_MIN 8 +#define DIV_MED 40 +#define DIV_MAX 100 + +struct ntrig_contact { 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; + struct ntrig_contact row[MAX_SLOTS], col[MAX_SLOTS]; + int dmin, dmed, dmax; + int nrow, ncol; + int index, nindex; + int nhold; }; -#define SN_MOVE 100 -#define SN_SIZE 50 + +/* + * This function converts the 4 byte raw firmware code into + * a string containing 5 comma separated numbers. + */ +static int ntrig_version_string(unsigned char *raw, char *buf) +{ + __u8 a = (raw[1] & 0x0e) >> 1; + __u8 b = (raw[0] & 0x3c) >> 2; + __u8 c = ((raw[0] & 0x03) << 3) | ((raw[3] & 0xe0) >> 5); + __u8 d = ((raw[3] & 0x07) << 3) | ((raw[2] & 0xe0) >> 5); + __u8 e = raw[2] & 0x07; + + /* + * As yet unmapped bits: + * 0b11000000 0b11110001 0b00011000 0b00011000 + */ + + return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); +} + +static void ntrig_report_version(struct hid_device *hdev) +{ + int ret; + char buf[20]; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + unsigned char *data = kmalloc(8, GFP_KERNEL); + + if (!data) + goto err_free; + + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, + 0x30c, 1, data, 8, + USB_CTRL_SET_TIMEOUT); + + if (ret == 8) { + ret = ntrig_version_string(&data[2], buf); + + dev_info(&hdev->dev, + "Firmware version: %s (%02x%02x %02x%02x)\n", + buf, data[2], data[3], data[4], data[5]); + } + +err_free: + kfree(data); +} /* * this driver is aimed at two firmware versions in circulation: @@ -54,7 +117,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) { - int delta = field->logical_maximum - field->logical_minimum; + struct ntrig_data *nd = hid_get_drvdata(hdev); + struct input_dev *input = hi->input; + int f1 = field->logical_minimum; + int f2 = field->logical_maximum; + int df = f2 - f1; /* No special mappings needed for the pen and single touch */ if (field->physical) @@ -64,28 +131,21 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, 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); + hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_X); + input_set_abs_params(input, ABS_X, + f1, f2, df / SN_MOVE_X, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + f1, f2, df / SN_MOVE_X, 0); + nd->dmin = df / DIV_MIN; + nd->dmed = df / DIV_MED; + nd->dmax = df / DIV_MAX; 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); + hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_Y); + input_set_abs_params(input, ABS_Y, + f1, f2, df / SN_MOVE_Y, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + f1, f2, df / SN_MOVE_Y, 0); return 1; } return 0; @@ -97,26 +157,31 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_INPUTMODE: case HID_DG_DEVICEINDEX: case HID_DG_CONTACTMAX: + case HID_DG_CONTACTCOUNT: + case HID_DG_INRANGE: + case HID_DG_CONFIDENCE: return -1; + case HID_DG_TIPSWITCH: + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(input, EV_KEY, BTN_TOUCH); + 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); + EV_ABS, ABS_MT_TOUCH_MAJOR); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + f1, f2, df / SN_MAJOR, 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); + EV_ABS, ABS_MT_TOUCH_MINOR); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + f1, f2, df / SN_MAJOR, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, + 0, 1, 0, 0); + input_set_events_per_packet(input, MAX_EVENTS); return 1; } return 0; @@ -137,34 +202,123 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, if (field->physical) return 0; - if (usage->type == EV_KEY || usage->type == EV_REL - || usage->type == EV_ABS) - clear_bit(usage->code, *bit); + /* tell hid-input to skip setup of these event types */ + if (usage->type == EV_KEY || usage->type == EV_ABS) + set_bit(usage->type, hi->input->evbit); + return -1; +} - return 0; +static bool copy_best_column(struct ntrig_data *nd, unsigned *used, int i) +{ + int j, jbest = -1, d, dbest; + for (j = 0; j < nd->ncol; ++j) { + if (*used & (1 << j)) + continue; + d = abs(nd->row[i].x - nd->col[j].x) + + abs(nd->row[i].y - nd->col[j].y); + if (jbest < 0 || d < dbest) { + jbest = j; + dbest = d; + } + } + if (jbest < 0) + return false; + *used |= (1 << jbest); + if (nd->nrow > 2) + nd->col[jbest].y = (nd->row[i].y + nd->col[jbest].y) >> 1; + nd->row[i] = nd->col[jbest]; + return true; +} + +static bool copy_next_column(struct ntrig_data *nd, unsigned *used, int i) +{ + int j; + for (j = 0; j < nd->ncol; ++j) { + if (*used & (1 << j)) + continue; + *used |= (1 << j); + nd->row[i] = nd->col[j]; + return true; + } + return false; +} + +static int ghost_distance(const struct ntrig_data *nd, int j) +{ + int i, d, dbest = INT_MAX; + for (i = 0; i < nd->nrow; ++i) { + d = abs(nd->row[i].x - nd->col[j].x); + dbest = min(dbest, d); + d = abs(nd->row[i].y - nd->col[j].y); + dbest = min(dbest, d); + } + return dbest; +} + +static void discard_ghosts(struct ntrig_data *nd, unsigned *used) +{ + int j, d; + for (j = 0; j < nd->ncol; ++j) { + if (*used & (1 << j)) + continue; + d = ghost_distance(nd, j); + if ((nd->nhold < HOLD_MIN && d < nd->dmin) || + (nd->nhold < HOLD_MED && d < nd->dmed) || + (nd->nhold < HOLD_MAX && d < nd->dmax)) + *used |= (1 << j); + } } static void report_frame(struct input_dev *input, struct ntrig_data *nd) { - int i, count = nd->contactcount; - for (i = 0; i < count; i++) { - struct mt_data *p = &nd->data[i]; - if (i == 0) { - input_report_key(input, BTN_TOUCH, 1); - input_report_abs(input, ABS_X, p->x); - input_report_abs(input, ABS_Y, p->y); - } - 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); + struct ntrig_contact *oldest = 0; + unsigned used = 0; + int i; + + if (nd->nrow < nd->ncol) { + for (i = 0; i < nd->nrow; ++i) + copy_best_column(nd, &used, i); + if (nd->ncol > 2) + discard_ghosts(nd, &used); + while (copy_next_column(nd, &used, i)) + i++; + nd->nrow = i; + nd->nhold++; + } else if (nd->nrow > nd->ncol) { + for (i = 0; i < nd->ncol; ++i) + nd->row[i] = nd->col[i]; + nd->nrow = nd->ncol; + nd->nhold = 0; + } else { + unsigned used = 0; + for (i = 0; i < nd->nrow; ++i) + copy_best_column(nd, &used, i); + nd->nhold = 0; + } + + for (i = 0; i < nd->nrow; ++i) { + struct ntrig_contact *f = &nd->row[i]; + int wide = (f->w > f->h); + int major = max(f->w, f->h); + int minor = min(f->w, f->h); + if (!oldest) + oldest = f; + 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_ORIENTATION, wide); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); input_mt_sync(input); } - if (!count) { - input_report_key(input, BTN_TOUCH, 0); + + /* touchscreen emulation */ + if (oldest) { + 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); + } else { + input_event(input, EV_KEY, BTN_TOUCH, 0); } - input_sync(input); } /* @@ -178,7 +332,6 @@ 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); - struct mt_data *p = &nd->data[nd->id]; /* No special handling needed for the pen */ if (field->application == HID_DG_PEN) @@ -186,49 +339,26 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { - case HID_DG_INRANGE: - p->in_range = value; - break; - /* new mt packet */ - case 0xff000001: - 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: - p = &nd->data[nd->id = value]; - memset(p, 0, sizeof(*p)); + nd->index = nd->nindex++; break; case HID_GD_X: - p->x = value; + nd->col[nd->index].x = value; break; case HID_GD_Y: - p->y = value; + nd->col[nd->index].y = value; break; case HID_DG_WIDTH: - p->w = value; + nd->col[nd->index].w = value; break; case HID_DG_HEIGHT: - p->h = value; + nd->col[nd->index].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; + case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ + nd->nindex = 0; + nd->ncol = value; report_frame(input, nd); break; - - default: - return 0; } } @@ -249,7 +379,6 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) 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) { @@ -274,7 +403,6 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) list_for_each_entry(hidinput, &hdev->inputs, list) { if (hidinput->report->maxfield < 1) - continue; input = hidinput->input; @@ -283,18 +411,6 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) 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) ? @@ -309,6 +425,9 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) if (report) usbhid_submit_report(hdev, report, USB_DIR_OUT); + ntrig_report_version(hdev); + + return 0; err_free: kfree(nd); -- cgit v1.2.3