diff --git a/sys/dev/evdev/cdev.c b/sys/dev/evdev/cdev.c index 66d00ad16ae..d124e691a7c 100644 --- a/sys/dev/evdev/cdev.c +++ b/sys/dev/evdev/cdev.c @@ -621,7 +621,7 @@ evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1); for (int i = 0; i < nvalues; i++) ((int32_t *)data)[i + 1] = - evdev_get_mt_value(evdev, i, code); + evdev_mt_get_value(evdev, i, code); return (0); case EVIOCGKEY(0): diff --git a/sys/dev/evdev/evdev.c b/sys/dev/evdev/evdev.c index 6c7dec284ef..ea1d595586a 100644 --- a/sys/dev/evdev/evdev.c +++ b/sys/dev/evdev/evdev.c @@ -93,15 +93,6 @@ static void evdev_start_repeat(struct evdev_dev *, uint16_t); static void evdev_stop_repeat(struct evdev_dev *); static int evdev_check_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); -static inline void -bit_change(bitstr_t *bitstr, int bit, int value) -{ - if (value) - bit_set(bitstr, bit); - else - bit_clear(bitstr, bit); -} - struct evdev_dev * evdev_alloc(void) { @@ -315,9 +306,12 @@ evdev_register_common(struct evdev_dev *evdev) } } - /* Initialize multitouch protocol type B states */ - if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT) && - evdev->ev_absinfo != NULL && MAXIMAL_MT_SLOT(evdev) > 0) + /* Initialize multitouch protocol type B states or A to B convertor */ + if ((bit_test(evdev->ev_abs_flags, ABS_MT_SLOT) && + evdev->ev_absinfo != NULL && MAXIMAL_MT_SLOT(evdev) > 0) || + (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK) && + bit_test(evdev->ev_abs_flags, ABS_MT_POSITION_X) && + bit_test(evdev->ev_abs_flags, ABS_MT_POSITION_Y))) evdev_mt_init(evdev); /* Estimate maximum report size */ @@ -720,15 +714,23 @@ evdev_modify_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, break; case EV_ABS: - fuzz = evdev->ev_absinfo[code].fuzz; - if (fuzz == 0 || code == ABS_MT_SLOT) + if (code == ABS_MT_SLOT) break; else if (!ABS_IS_MT(code)) old_value = evdev->ev_absinfo[code].value; - else if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) - old_value = evdev_get_mt_value(evdev, - evdev_get_last_mt_slot(evdev), code); - else /* Pass MT protocol type A events as is */ + else if (!bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) + /* Pass MT protocol type A events as is */ + break; + else if (code == ABS_MT_TRACKING_ID) { + *value = evdev_mt_reassign_id(evdev, + evdev_mt_get_last_slot(evdev), *value); + break; + } else + old_value = evdev_mt_get_value(evdev, + evdev_mt_get_last_slot(evdev), code); + + fuzz = evdev->ev_absinfo[code].fuzz; + if (fuzz == 0) break; abs_change = abs(*value - old_value); @@ -807,7 +809,7 @@ evdev_sparse_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, switch (code) { case ABS_MT_SLOT: /* Postpone ABS_MT_SLOT till next event */ - evdev_set_last_mt_slot(evdev, value); + evdev_mt_set_last_slot(evdev, value); return (EV_SKIP_EVENT); case ABS_MT_FIRST ... ABS_MT_LAST: @@ -815,11 +817,11 @@ evdev_sparse_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, if (!bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) break; /* Don`t repeat MT protocol type B events */ - last_mt_slot = evdev_get_last_mt_slot(evdev); - if (evdev_get_mt_value(evdev, last_mt_slot, code) + last_mt_slot = evdev_mt_get_last_slot(evdev); + if (evdev_mt_get_value(evdev, last_mt_slot, code) == value) return (EV_SKIP_EVENT); - evdev_set_mt_value(evdev, last_mt_slot, code, value); + evdev_mt_set_value(evdev, last_mt_slot, code, value); if (last_mt_slot != CURRENT_MT_SLOT(evdev)) { CURRENT_MT_SLOT(evdev) = last_mt_slot; evdev->ev_report_opened = true; @@ -895,6 +897,7 @@ evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, EVDEV_LOCK_ASSERT(evdev); + evdev_modify_event(evdev, type, code, &value); sparse = evdev_sparse_event(evdev, type, code, value); switch (sparse) { case EV_REPORT_MT_SLOT: @@ -918,20 +921,16 @@ evdev_restore_after_kdb(struct evdev_dev *evdev) EVDEV_LOCK_ASSERT(evdev); /* Report postponed leds */ - for (code = 0; code < LED_CNT; code++) - if (bit_test(evdev->ev_kdb_led_states, code)) - evdev_send_event(evdev, EV_LED, code, - !bit_test(evdev->ev_led_states, code)); + bit_foreach(evdev->ev_kdb_led_states, LED_CNT, code) + evdev_send_event(evdev, EV_LED, code, + !bit_test(evdev->ev_led_states, code)); bit_nclear(evdev->ev_kdb_led_states, 0, LED_MAX); /* Release stuck keys (CTRL + ALT + ESC) */ evdev_stop_repeat(evdev); - for (code = 0; code < KEY_CNT; code++) { - if (bit_test(evdev->ev_key_states, code)) { - evdev_send_event(evdev, EV_KEY, code, KEY_EVENT_UP); - evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); - } - } + bit_foreach(evdev->ev_key_states, KEY_CNT, code) + evdev_send_event(evdev, EV_KEY, code, KEY_EVENT_UP); + evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1); } int @@ -962,15 +961,16 @@ evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, evdev_restore_after_kdb(evdev); } - evdev_modify_event(evdev, type, code, &value); if (type == EV_SYN && code == SYN_REPORT && - bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) - evdev_send_mt_autorel(evdev); - if (type == EV_SYN && code == SYN_REPORT && evdev->ev_report_opened && - bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) - evdev_send_mt_compat(evdev); - evdev_send_event(evdev, type, code, value); + bit_test(evdev->ev_abs_flags, ABS_MT_SLOT)) + evdev_mt_sync_frame(evdev); + else + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK) && + evdev_mt_record_event(evdev, type, code, value)) + goto exit; + evdev_send_event(evdev, type, code, value); +exit: EVDEV_EXIT(evdev); return (0); diff --git a/sys/dev/evdev/evdev.h b/sys/dev/evdev/evdev.h index 30d6a106d8b..207fd80b67d 100644 --- a/sys/dev/evdev/evdev.h +++ b/sys/dev/evdev/evdev.h @@ -90,6 +90,8 @@ extern int evdev_sysmouse_t_axis; * current MT protocol type B report */ #define EVDEV_FLAG_EXT_EPOCH 0x03 /* evdev_push_* is allways called with * input (global) epoch entered */ +#define EVDEV_FLAG_MT_TRACK 0x04 /* Assign touch to slot by evdev */ +#define EVDEV_FLAG_MT_KEEPID 0x05 /* Do not reassign tracking ID */ #define EVDEV_FLAG_MAX 0x1F #define EVDEV_FLAG_CNT (EVDEV_FLAG_MAX + 1) @@ -102,6 +104,29 @@ struct evdev_methods evdev_keycode_t *ev_set_keycode; }; +union evdev_mt_slot { + int32_t val[MT_CNT]; + struct { + int32_t maj; /* ABS_MT_TOUCH_MAJOR */ + int32_t min; /* ABS_MT_TOUCH_MINOR */ + int32_t w_maj; /* ABS_MT_WIDTH_MAJOR */ + int32_t w_min; /* ABS_MT_WIDTH_MINOR */ + int32_t ori; /* ABS_MT_ORIENTATION */ + int32_t x; /* ABS_MT_POSITION_X */ + int32_t y; /* ABS_MT_POSITION_Y */ + int32_t type; /* ABS_MT_TOOL_TYPE */ + int32_t blob_id; /* ABS_MT_BLOB_ID */ + int32_t id; /* ABS_MT_TRACKING_ID */ + int32_t p; /* ABS_MT_PRESSURE */ + int32_t dist; /* ABS_MT_DISTANCE */ + int32_t tool_x; /* ABS_MT_TOOL_X */ + int32_t tool_y; /* ABS_MT_TOOL_Y */ + }; +}; +_Static_assert(offsetof(union evdev_mt_slot, tool_y) == + offsetof(union evdev_mt_slot, val[ABS_MT_INDEX(ABS_MT_TOOL_Y)]), + "evdev_mt_slot array members does not match their structure aliases"); + /* Input device interface: */ struct evdev_dev *evdev_alloc(void); void evdev_free(struct evdev_dev *); @@ -131,11 +156,19 @@ void evdev_set_flag(struct evdev_dev *, uint16_t); void *evdev_get_softc(struct evdev_dev *); /* Multitouch related functions: */ -int32_t evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t); -void evdev_support_nfingers(struct evdev_dev *, int32_t); +int evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t); void evdev_support_mt_compat(struct evdev_dev *); -void evdev_push_nfingers(struct evdev_dev *, int32_t); void evdev_push_mt_compat(struct evdev_dev *); +int evdev_mt_push_slot(struct evdev_dev *, int, union evdev_mt_slot *); +int evdev_mt_push_frame(struct evdev_dev *, union evdev_mt_slot *, int); +void evdev_mt_match_frame(struct evdev_dev *, union evdev_mt_slot *, int); +union evdev_mt_slot *evdev_mt_get_match_slots(struct evdev_dev *); +void evdev_mt_push_autorel(struct evdev_dev *); +static __inline int +evdev_mt_id_to_slot(struct evdev_dev *evdev, int32_t id) +{ + return (evdev_get_mt_slot_by_tracking_id(evdev, id)); +} /* Utility functions: */ uint16_t evdev_hid2key(int); @@ -144,6 +177,8 @@ uint16_t evdev_scancode2key(int *, int); void evdev_push_mouse_btn(struct evdev_dev *, int); void evdev_push_leds(struct evdev_dev *, int); void evdev_push_repeats(struct evdev_dev *, keyboard_t *); +void evdev_support_nfingers(struct evdev_dev *, int); +void evdev_push_nfingers(struct evdev_dev *, int); /* Event reporting shortcuts: */ static __inline int diff --git a/sys/dev/evdev/evdev_mt.c b/sys/dev/evdev/evdev_mt.c index 1f9c9756db0..303aa9aaeaa 100644 --- a/sys/dev/evdev/evdev_mt.c +++ b/sys/dev/evdev/evdev_mt.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2016 Vladimir Kondratyev + * Copyright (c) 2016, 2020 Vladimir Kondratyev * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,6 +25,21 @@ * * $FreeBSD$ */ +/*- + * Copyright (c) 2015, 2016 Ulf Brosziewski + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ #include #include @@ -42,54 +57,93 @@ #define debugf(fmt, args...) #endif -static uint16_t evdev_fngmap[] = { - BTN_TOOL_FINGER, - BTN_TOOL_DOUBLETAP, - BTN_TOOL_TRIPLETAP, - BTN_TOOL_QUADTAP, - BTN_TOOL_QUINTTAP, -}; +typedef u_int slotset_t; -static uint16_t evdev_mtstmap[][2] = { - { ABS_MT_POSITION_X, ABS_X }, - { ABS_MT_POSITION_Y, ABS_Y }, - { ABS_MT_PRESSURE, ABS_PRESSURE }, - { ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH }, -}; +_Static_assert(MAX_MT_SLOTS < sizeof(slotset_t) * 8, "MAX_MT_SLOTS too big"); + +#define FOREACHBIT(v, i) \ + for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1) -struct evdev_mt_slot { - uint64_t ev_report; - int32_t ev_mt_states[MT_CNT]; +struct { + uint16_t mt; + uint16_t st; + int32_t max; +} static evdev_mtstmap[] = { + { ABS_MT_POSITION_X, ABS_X, 0 }, + { ABS_MT_POSITION_Y, ABS_Y, 0 }, + { ABS_MT_PRESSURE, ABS_PRESSURE, 255 }, + { ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH, 15 }, }; struct evdev_mt { - int32_t ev_mt_last_reported_slot; - struct evdev_mt_slot ev_mt_slots[]; + int last_reported_slot; + uint16_t tracking_id; + int32_t tracking_ids[MAX_MT_SLOTS]; + bool type_a; + u_int mtst_events; + /* the set of slots with active touches */ + slotset_t touches; + /* the set of slots with unsynchronized state */ + slotset_t frame; + /* the set of slots to match with active touches */ + slotset_t match_frame; + int match_slot; + union evdev_mt_slot *match_slots; + int *matrix; + union evdev_mt_slot slots[]; }; +static void evdev_mt_send_st_compat(struct evdev_dev *); +static void evdev_mt_send_autorel(struct evdev_dev *); +static void evdev_mt_replay_events(struct evdev_dev *); + +static inline int +ffc_slot(struct evdev_dev *evdev, slotset_t slots) +{ + return (ffs(~slots & (2U << MAXIMAL_MT_SLOT(evdev)) - 1) - 1); +} + void evdev_mt_init(struct evdev_dev *evdev) { - int32_t slot, slots; + struct evdev_mt *mt; + size_t size = offsetof(struct evdev_mt, slots); + int slot, slots; + bool type_a; + + type_a = !bit_test(evdev->ev_abs_flags, ABS_MT_SLOT); + if (type_a) { + /* Add events produced by MT type A to type B convertor */ + evdev_support_abs(evdev, + ABS_MT_SLOT, 0, MAX_MT_SLOTS - 1, 0, 0, 0); + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_KEEPID)) + evdev_support_abs(evdev, + ABS_MT_TRACKING_ID, -1, MAX_MT_SLOTS - 1, 0, 0, 0); + } slots = MAXIMAL_MT_SLOT(evdev) + 1; + size += sizeof(mt->slots[0]) * slots; + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) { + size += sizeof(mt->match_slots[0]) * slots; + size += sizeof(mt->matrix[0]) * (slots + 6) * slots; + } - evdev->ev_mt = malloc(offsetof(struct evdev_mt, ev_mt_slots) + - sizeof(struct evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO); + mt = malloc(size, M_EVDEV, M_WAITOK | M_ZERO); + evdev->ev_mt = mt; + mt->type_a = type_a; - /* Initialize multitouch protocol type B states */ - for (slot = 0; slot < slots; slot++) { - /* - * .ev_report should not be initialized to initial value of - * report counter (0) as it brokes free slot detection in - * evdev_get_mt_slot_by_tracking_id. So initialize it to -1 - */ - evdev->ev_mt->ev_mt_slots[slot] = (struct evdev_mt_slot) { - .ev_report = 0xFFFFFFFFFFFFFFFFULL, - .ev_mt_states[ABS_MT_INDEX(ABS_MT_TRACKING_ID)] = -1, - }; + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) { + mt->match_slots = mt->slots + slots; + mt->matrix = (int *)(mt->match_slots + slots); } + /* Initialize multitouch protocol type B states */ + for (slot = 0; slot < slots; slot++) + mt->slots[slot].id = -1; + + if (!bit_test(evdev->ev_flags, EVDEV_FLAG_MT_KEEPID)) + evdev_support_abs(evdev, + ABS_MT_TRACKING_ID, -1, UINT16_MAX, 0, 0, 0); if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) evdev_support_mt_compat(evdev); } @@ -97,155 +151,492 @@ evdev_mt_init(struct evdev_dev *evdev) void evdev_mt_free(struct evdev_dev *evdev) { - free(evdev->ev_mt, M_EVDEV); } -int32_t -evdev_get_last_mt_slot(struct evdev_dev *evdev) +void +evdev_mt_sync_frame(struct evdev_dev *evdev) +{ + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) + evdev_mt_replay_events(evdev); + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL)) + evdev_mt_send_autorel(evdev); + if (evdev->ev_report_opened && + bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT)) + evdev_mt_send_st_compat(evdev); + evdev->ev_mt->frame = 0; +} + +static void +evdev_mt_send_slot(struct evdev_dev *evdev, int slot, + union evdev_mt_slot *state) { + int i; + bool type_a = !bit_test(evdev->ev_abs_flags, ABS_MT_SLOT); + + EVDEV_LOCK_ASSERT(evdev); + MPASS(type_a || (slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev))); + MPASS(!type_a || state != NULL); + + if (!type_a) { + evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot); + if (state == NULL) { + evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID, -1); + return; + } + } + bit_foreach_at(evdev->ev_abs_flags, ABS_MT_FIRST, ABS_MT_LAST + 1, i) + evdev_send_event(evdev, EV_ABS, i, + state->val[ABS_MT_INDEX(i)]); + if (type_a) + evdev_send_event(evdev, EV_SYN, SYN_MT_REPORT, 1); +} + +int +evdev_mt_push_slot(struct evdev_dev *evdev, int slot, + union evdev_mt_slot *state) +{ + struct evdev_mt *mt = evdev->ev_mt; + bool type_a = !bit_test(evdev->ev_abs_flags, ABS_MT_SLOT); + + if ((type_a || (mt != NULL && mt->type_a)) && state == NULL) + return (EINVAL); + if (!type_a && (slot < 0 || slot > MAXIMAL_MT_SLOT(evdev))) + return (EINVAL); + + EVDEV_ENTER(evdev); + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK) && mt->type_a) { + mt->match_slots[mt->match_slot] = *state; + evdev_mt_record_event(evdev, EV_SYN, SYN_MT_REPORT, 1); + } else if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) { + evdev_mt_record_event(evdev, EV_ABS, ABS_MT_SLOT, slot); + if (state != NULL) + mt->match_slots[mt->match_slot] = *state; + else + evdev_mt_record_event(evdev, EV_ABS, + ABS_MT_TRACKING_ID, -1); + } else + evdev_mt_send_slot(evdev, slot, state); + EVDEV_EXIT(evdev); + + return (0); +} - return (evdev->ev_mt->ev_mt_last_reported_slot); +/* + * Find a minimum-weight matching for an m-by-n matrix. + * + * m must be greater than or equal to n. The size of the buffer must be + * at least 3m + 3n. + * + * On return, the first m elements of the buffer contain the row-to- + * column mappings, i.e., buffer[i] is the column index for row i, or -1 + * if there is no assignment for that row (which may happen if n < m). + * + * Wrong results because of overflows will not occur with input values + * in the range of 0 to INT_MAX / 2 inclusive. + * + * The function applies the Dinic-Kronrod algorithm. It is not modern or + * popular, but it seems to be a good choice for small matrices at least. + * The original form of the algorithm is modified as follows: There is no + * initial search for row minima, the initial assignments are in a + * "virtual" column with the index -1 and zero values. This permits inputs + * with n < m, and it simplifies the reassignments. + */ +static void +evdev_mt_matching(int *matrix, int m, int n, int *buffer) +{ + int i, j, k, d, e, row, col, delta; + int *p; + int *r2c = buffer; /* row-to-column assignments */ + int *red = r2c + m; /* reduced values of the assignments */ + int *mc = red + m; /* row-wise minimal elements of cs */ + int *cs = mc + m; /* the column set */ + int *c2r = cs + n; /* column-to-row assignments in cs */ + int *cd = c2r + n; /* column deltas (reduction) */ + + for (p = r2c; p < red; *p++ = -1) {} + for (; p < mc; *p++ = 0) {} + for (col = 0; col < n; col++) { + delta = INT_MAX; + for (i = 0, p = matrix + col; i < m; i++, p += n) { + d = *p - red[i]; + if (d < delta || (d == delta && r2c[i] < 0)) { + delta = d; + row = i; + } + } + cd[col] = delta; + if (r2c[row] < 0) { + r2c[row] = col; + continue; + } + for (p = mc; p < cs; *p++ = col) {} + for (k = 0; (j = r2c[row]) >= 0;) { + cs[k++] = j; + c2r[j] = row; + mc[row] -= n; + delta = INT_MAX; + for (i = 0, p = matrix; i < m; i++, p += n) + if (mc[i] >= 0) { + d = p[mc[i]] - cd[mc[i]]; + e = p[j] - cd[j]; + if (e < d) { + d = e; + mc[i] = j; + } + d -= red[i]; + if (d < delta || (d == delta + && r2c[i] < 0)) { + delta = d; + row = i; + } + } + cd[col] += delta; + for (i = 0; i < k; i++) { + cd[cs[i]] += delta; + red[c2r[cs[i]]] -= delta; + } + } + for (j = mc[row]; (r2c[row] = j) != col;) { + row = c2r[j]; + j = mc[row] + n; + } + } } +/* + * Assign slot numbers to the points in the pt array. The slot numbers are + * passed to the caller in the pt->id fields. + * + * The slot assignment pairs the points with points of the previous frame in + * such a way that the sum of the squared distances is minimal. Using + * squares instead of simple distances favours assignments with more uniform + * distances, and it is faster. + */ void -evdev_set_last_mt_slot(struct evdev_dev *evdev, int32_t slot) +evdev_mt_match_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, + int size) { + struct evdev_mt *mt = evdev->ev_mt; + int i, j, m, n, dx, dy, slot, num_touches; + int *p, *r2c, *c2r; + u_int touches; + + EVDEV_LOCK_ASSERT(evdev); + MPASS(mt->matrix != NULL); + MPASS(size >= 0 && size <= MAXIMAL_MT_SLOT(evdev) + 1); - evdev->ev_mt->ev_mt_slots[slot].ev_report = evdev->ev_report_count; - evdev->ev_mt->ev_mt_last_reported_slot = slot; + if (size == 0) + return; + + p = mt->matrix; + touches = mt->touches; + num_touches = bitcount(touches); + if (num_touches >= size) { + FOREACHBIT(touches, slot) + for (i = 0; i < size; i++) { + dx = pt[i].x - mt->slots[slot].x; + dy = pt[i].y - mt->slots[slot].y; + *p++ = dx * dx + dy * dy; + } + m = num_touches; + n = size; + } else { + for (i = 0; i < size; i++) + FOREACHBIT(touches, slot) { + dx = pt[i].x - mt->slots[slot].x; + dy = pt[i].y - mt->slots[slot].y; + *p++ = dx * dx + dy * dy; + } + m = size; + n = num_touches; + } + evdev_mt_matching(mt->matrix, m, n, p); + + r2c = p; + c2r = p + m; + for (i = 0; i < m; i++) + if ((j = r2c[i]) >= 0) + c2r[j] = i; + + p = (n == size ? r2c : c2r); + FOREACHBIT(touches, slot) + if ((i = *p++) >= 0) + pt[i].id = slot; + + p = (n == size ? c2r : r2c); + for (i = 0; i < size; i++) + if (*p++ < 0 && (pt[i].id = ffc_slot(evdev, touches)) >= 0) + touches |= 1U << pt[i].id; } -inline int32_t -evdev_get_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code) +static void +evdev_mt_send_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, int size) { + union evdev_mt_slot *slot; + + EVDEV_LOCK_ASSERT(evdev); + MPASS(size >= 0 && size <= MAXIMAL_MT_SLOT(evdev) + 1); - return (evdev->ev_mt-> - ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)]); + for (slot = pt; slot < pt + size; slot++) + if (slot->id >= 0) + evdev_mt_send_slot(evdev, slot->id, slot); } -inline void -evdev_set_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code, +int +evdev_mt_push_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, int size) +{ + if (size < 0 || size > MAXIMAL_MT_SLOT(evdev) + 1) + return (EINVAL); + + EVDEV_ENTER(evdev); + evdev_mt_send_frame(evdev, pt, size); + EVDEV_EXIT(evdev); + + return (0); +} + +bool +evdev_mt_record_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, int32_t value) { + struct evdev_mt *mt = evdev->ev_mt; + + EVDEV_LOCK_ASSERT(evdev); + + switch (type) { + case EV_SYN: + if (code == SYN_MT_REPORT) { + /* MT protocol type A support */ + KASSERT(mt->type_a, ("Not a MT type A protocol")); + mt->match_frame |= 1U << mt->match_slot; + mt->match_slot++; + return (true); + } + break; + case EV_ABS: + if (code == ABS_MT_SLOT) { + /* MT protocol type B support */ + KASSERT(!mt->type_a, ("Not a MT type B protocol")); + KASSERT(value >= 0, ("Negative slot number")); + mt->match_slot = value; + mt->match_frame |= 1U << mt->match_slot; + return (true); + } else if (code == ABS_MT_TRACKING_ID) { + KASSERT(!mt->type_a, ("Not a MT type B protocol")); + if (value == -1) + mt->match_frame &= ~(1U << mt->match_slot); + return (true); + } else if (ABS_IS_MT(code)) { + KASSERT(mt->match_slot >= 0, ("Negative slot")); + KASSERT(mt->match_slot <= MAXIMAL_MT_SLOT(evdev), + ("Slot number too big")); + mt->match_slots[mt->match_slot]. + val[ABS_MT_INDEX(code)] = value; + return (true); + } + break; + default: + break; + } - evdev->ev_mt->ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)] = - value; + return (false); } -int32_t -evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id) +static void +evdev_mt_replay_events(struct evdev_dev *evdev) { - int32_t tr_id, slot, free_slot = -1; + struct evdev_mt *mt = evdev->ev_mt; + int slot, size = 0; - for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) { - tr_id = evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID); - if (tr_id == tracking_id) - return (slot); - /* - * Its possible that slot will be reassigned in a place of just - * released one within the same report. To avoid this compare - * report counter with slot`s report number updated with each - * ABS_MT_TRACKING_ID change. - */ - if (free_slot == -1 && tr_id == -1 && - evdev->ev_mt->ev_mt_slots[slot].ev_report != - evdev->ev_report_count) - free_slot = slot; + EVDEV_LOCK_ASSERT(evdev); + + FOREACHBIT(mt->match_frame, slot) { + if (slot != size) + mt->match_slots[size] = mt->match_slots[slot]; + size++; } + evdev_mt_match_frame(evdev, mt->match_slots, size); + evdev_mt_send_frame(evdev, mt->match_slots, size); + mt->match_slot = 0; + mt->match_frame = 0; +} + +union evdev_mt_slot * +evdev_mt_get_match_slots(struct evdev_dev *evdev) +{ + return (evdev->ev_mt->match_slots); +} - return (free_slot); +int +evdev_mt_get_last_slot(struct evdev_dev *evdev) +{ + return (evdev->ev_mt->last_reported_slot); } void -evdev_support_nfingers(struct evdev_dev *evdev, int32_t nfingers) +evdev_mt_set_last_slot(struct evdev_dev *evdev, int slot) { - int32_t i; + struct evdev_mt *mt = evdev->ev_mt; - for (i = 0; i < MIN(nitems(evdev_fngmap), nfingers); i++) - evdev_support_key(evdev, evdev_fngmap[i]); + MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); + + mt->frame |= 1U << slot; + mt->last_reported_slot = slot; } -void -evdev_support_mt_compat(struct evdev_dev *evdev) +int32_t +evdev_mt_get_value(struct evdev_dev *evdev, int slot, int16_t code) { - int32_t i; + struct evdev_mt *mt = evdev->ev_mt; - if (evdev->ev_absinfo == NULL) - return; + MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); - evdev_support_event(evdev, EV_KEY); - evdev_support_key(evdev, BTN_TOUCH); + return (mt->slots[slot].val[ABS_MT_INDEX(code)]); +} - /* Touchscreens should not advertise tap tool capabilities */ - if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT)) - evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1); +void +evdev_mt_set_value(struct evdev_dev *evdev, int slot, int16_t code, + int32_t value) +{ + struct evdev_mt *mt = evdev->ev_mt; - /* Echo 0-th MT-slot as ST-slot */ - for (i = 0; i < nitems(evdev_mtstmap); i++) - if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][0])) - evdev_support_abs(evdev, evdev_mtstmap[i][1], - evdev->ev_absinfo[evdev_mtstmap[i][0]].minimum, - evdev->ev_absinfo[evdev_mtstmap[i][0]].maximum, - evdev->ev_absinfo[evdev_mtstmap[i][0]].fuzz, - evdev->ev_absinfo[evdev_mtstmap[i][0]].flat, - evdev->ev_absinfo[evdev_mtstmap[i][0]].resolution); + MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); + + if (code == ABS_MT_TRACKING_ID) { + if (value != -1) + mt->touches |= 1U << slot; + else + mt->touches &= ~(1U << slot); + } + mt->slots[slot].val[ABS_MT_INDEX(code)] = value; } -static int32_t -evdev_count_fingers(struct evdev_dev *evdev) +int +evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id) { - int32_t nfingers = 0, i; + struct evdev_mt *mt = evdev->ev_mt; + int slot; - for (i = 0; i <= MAXIMAL_MT_SLOT(evdev); i++) - if (evdev_get_mt_value(evdev, i, ABS_MT_TRACKING_ID) != -1) - nfingers++; + KASSERT(!mt->type_a, ("Not a MT type B protocol")); - return (nfingers); + /* + * Ignore tracking_id if slot assignment is performed by evdev. + * Events are written sequentially to temporary matching buffer. + */ + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) + return (ffc_slot(evdev, mt->match_frame)); + + FOREACHBIT(mt->touches, slot) + if (mt->tracking_ids[slot] == tracking_id) + return (slot); + /* + * Do not allow allocation of new slot in a place of just + * released one within the same report. + */ + return (ffc_slot(evdev, mt->touches | mt->frame)); } -static void -evdev_send_nfingers(struct evdev_dev *evdev, int32_t nfingers) +int32_t +evdev_mt_reassign_id(struct evdev_dev *evdev, int slot, int32_t id) { - int32_t i; + struct evdev_mt *mt = evdev->ev_mt; + int32_t nid; - EVDEV_LOCK_ASSERT(evdev); + if (id == -1 || bit_test(evdev->ev_flags, EVDEV_FLAG_MT_KEEPID)) { + mt->tracking_ids[slot] = id; + return (id); + } - if (nfingers > nitems(evdev_fngmap)) - nfingers = nitems(evdev_fngmap); + nid = evdev_mt_get_value(evdev, slot, ABS_MT_TRACKING_ID); + if (nid != -1) { + KASSERT(id == mt->tracking_ids[slot], + ("MT-slot tracking id has changed")); + return (nid); + } - for (i = 0; i < nitems(evdev_fngmap); i++) - evdev_send_event(evdev, EV_KEY, evdev_fngmap[i], - nfingers == i + 1); + mt->tracking_ids[slot] = id; +again: + nid = mt->tracking_id++; + FOREACHBIT(mt->touches, slot) + if (evdev_mt_get_value(evdev, slot, ABS_MT_TRACKING_ID) == nid) + goto again; + + return (nid); +} + +static inline int32_t +evdev_mt_normalize(int32_t value, int32_t mtmin, int32_t mtmax, int32_t stmax) +{ + if (stmax != 0 && mtmax != mtmin) { + value = (value - mtmin) * stmax / (mtmax - mtmin); + value = MAX(MIN(value, stmax), 0); + } + return (value); } void -evdev_push_nfingers(struct evdev_dev *evdev, int32_t nfingers) +evdev_support_mt_compat(struct evdev_dev *evdev) { + struct input_absinfo *ai; + int i; - EVDEV_ENTER(evdev); - evdev_send_nfingers(evdev, nfingers); - EVDEV_EXIT(evdev); + if (evdev->ev_absinfo == NULL) + return; + + evdev_support_event(evdev, EV_KEY); + evdev_support_key(evdev, BTN_TOUCH); + + /* Touchscreens should not advertise tap tool capabilities */ + if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT)) + evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1); + + /* Echo 0-th MT-slot as ST-slot */ + for (i = 0; i < nitems(evdev_mtstmap); i++) { + if (!bit_test(evdev->ev_abs_flags, evdev_mtstmap[i].mt) || + bit_test(evdev->ev_abs_flags, evdev_mtstmap[i].st)) + continue; + ai = evdev->ev_absinfo + evdev_mtstmap[i].mt; + evdev->ev_mt->mtst_events |= 1U << i; + if (evdev_mtstmap[i].max != 0) + evdev_support_abs(evdev, evdev_mtstmap[i].st, + 0, + evdev_mtstmap[i].max, + 0, + evdev_mt_normalize( + ai->flat, 0, ai->maximum, evdev_mtstmap[i].max), + 0); + else + evdev_support_abs(evdev, evdev_mtstmap[i].st, + ai->minimum, + ai->maximum, + 0, + ai->flat, + ai->resolution); + } } -void -evdev_send_mt_compat(struct evdev_dev *evdev) +static void +evdev_mt_send_st_compat(struct evdev_dev *evdev) { - int32_t nfingers, i; + struct evdev_mt *mt = evdev->ev_mt; + int nfingers, i, st_slot; EVDEV_LOCK_ASSERT(evdev); - nfingers = evdev_count_fingers(evdev); + nfingers = bitcount(mt->touches); evdev_send_event(evdev, EV_KEY, BTN_TOUCH, nfingers > 0); - if (evdev_get_mt_value(evdev, 0, ABS_MT_TRACKING_ID) != -1) - /* Echo 0-th MT-slot as ST-slot */ - for (i = 0; i < nitems(evdev_mtstmap); i++) - if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][1])) - evdev_send_event(evdev, EV_ABS, - evdev_mtstmap[i][1], - evdev_get_mt_value(evdev, 0, - evdev_mtstmap[i][0])); + /* Send first active MT-slot state as single touch report */ + st_slot = ffs(mt->touches) - 1; + if (st_slot != -1) + FOREACHBIT(mt->mtst_events, i) + evdev_send_event(evdev, EV_ABS, evdev_mtstmap[i].st, + evdev_mt_normalize(evdev_mt_get_value(evdev, + st_slot, evdev_mtstmap[i].mt), + evdev->ev_absinfo[evdev_mtstmap[i].mt].minimum, + evdev->ev_absinfo[evdev_mtstmap[i].mt].maximum, + evdev_mtstmap[i].max)); /* Touchscreens should not report tool taps */ if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT)) @@ -260,24 +651,27 @@ evdev_push_mt_compat(struct evdev_dev *evdev) { EVDEV_ENTER(evdev); - evdev_send_mt_compat(evdev); + evdev_mt_send_st_compat(evdev); EVDEV_EXIT(evdev); } -void -evdev_send_mt_autorel(struct evdev_dev *evdev) +static void +evdev_mt_send_autorel(struct evdev_dev *evdev) { - int32_t slot; + struct evdev_mt *mt = evdev->ev_mt; + int slot; EVDEV_LOCK_ASSERT(evdev); + KASSERT(mt->match_frame == 0, ("Unmatched events exist")); - for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) { - if (evdev->ev_mt->ev_mt_slots[slot].ev_report != - evdev->ev_report_count && - evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID) != -1){ - evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot); - evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID, - -1); - } - } + FOREACHBIT(mt->touches & ~mt->frame, slot) + evdev_mt_send_slot(evdev, slot, NULL); +} + +void +evdev_mt_push_autorel(struct evdev_dev *evdev) +{ + EVDEV_ENTER(evdev); + evdev_mt_send_autorel(evdev); + EVDEV_EXIT(evdev); } diff --git a/sys/dev/evdev/evdev_private.h b/sys/dev/evdev/evdev_private.h index 19636823b80..8890d8ae2bb 100644 --- a/sys/dev/evdev/evdev_private.h +++ b/sys/dev/evdev/evdev_private.h @@ -247,6 +247,22 @@ struct evdev_client (((client)->ec_buffer_ready + (client)->ec_buffer_size - \ (client)->ec_buffer_head) % (client)->ec_buffer_size) +/* bitstring(3) helpers */ +static inline void +bit_change(bitstr_t *bitstr, int bit, int value) +{ + if (value) + bit_set(bitstr, bit); + else + bit_clear(bitstr, bit); +} +#define bit_foreach_at(_bitstr, _start, _nbits, _iter) \ + for (bit_ffs_at((_bitstr), (_start), (_nbits), &(_iter)); \ + (_iter) != -1; \ + bit_ffs_at((_bitstr), (_iter) + 1, (_nbits), &(_iter))) +#define bit_foreach(_bitstr, _nbits, _iter) \ + bit_foreach_at(_bitstr, /*start*/0, _nbits, _iter) + /* Input device interface: */ void evdev_send_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); int evdev_inject_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); @@ -269,14 +285,16 @@ void evdev_revoke_client(struct evdev_client *); /* Multitouch related functions: */ void evdev_mt_init(struct evdev_dev *); void evdev_mt_free(struct evdev_dev *); -int32_t evdev_get_last_mt_slot(struct evdev_dev *); -void evdev_set_last_mt_slot(struct evdev_dev *, int32_t); -int32_t evdev_get_mt_value(struct evdev_dev *, int32_t, int16_t); -void evdev_set_mt_value(struct evdev_dev *, int32_t, int16_t, int32_t); -void evdev_send_mt_compat(struct evdev_dev *); -void evdev_send_mt_autorel(struct evdev_dev *); +void evdev_mt_sync_frame(struct evdev_dev *); +int evdev_mt_get_last_slot(struct evdev_dev *); +void evdev_mt_set_last_slot(struct evdev_dev *, int); +int32_t evdev_mt_get_value(struct evdev_dev *, int, int16_t); +void evdev_mt_set_value(struct evdev_dev *, int, int16_t, int32_t); +bool evdev_mt_record_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); +int32_t evdev_mt_reassign_id(struct evdev_dev *, int, int32_t); /* Utility functions: */ void evdev_client_dumpqueue(struct evdev_client *); +void evdev_send_nfingers(struct evdev_dev *, int); #endif /* _DEV_EVDEV_EVDEV_PRIVATE_H */ diff --git a/sys/dev/evdev/evdev_utils.c b/sys/dev/evdev/evdev_utils.c index fd696dc6ac2..754846b8d6e 100644 --- a/sys/dev/evdev/evdev_utils.c +++ b/sys/dev/evdev/evdev_utils.c @@ -38,6 +38,7 @@ #include #include +#include #include #define NONE KEY_RESERVED @@ -205,6 +206,14 @@ static uint16_t evdev_led_codes[] = { LED_SCROLLL, /* SLKED */ }; +static uint16_t evdev_nfinger_codes[] = { + BTN_TOOL_FINGER, + BTN_TOOL_DOUBLETAP, + BTN_TOOL_TRIPLETAP, + BTN_TOOL_QUADTAP, + BTN_TOOL_QUINTTAP, +}; + uint16_t evdev_hid2key(int scancode) { @@ -300,3 +309,35 @@ evdev_push_repeats(struct evdev_dev *evdev, keyboard_t *kbd) evdev_push_event(evdev, EV_REP, REP_DELAY, kbd->kb_delay1); evdev_push_event(evdev, EV_REP, REP_PERIOD, kbd->kb_delay2); } + +void +evdev_support_nfingers(struct evdev_dev *evdev, int nfingers) +{ + int i; + + for (i = 0; i < MIN(nitems(evdev_nfinger_codes), nfingers); i++) + evdev_support_key(evdev, evdev_nfinger_codes[i]); +} + +void +evdev_send_nfingers(struct evdev_dev *evdev, int nfingers) +{ + int i; + + EVDEV_LOCK_ASSERT(evdev); + + if (nfingers > nitems(evdev_nfinger_codes)) + nfingers = nitems(evdev_nfinger_codes); + + for (i = 0; i < nitems(evdev_nfinger_codes); i++) + evdev_send_event(evdev, EV_KEY, evdev_nfinger_codes[i], + nfingers == i + 1); +} + +void +evdev_push_nfingers(struct evdev_dev *evdev, int nfingers) +{ + EVDEV_ENTER(evdev); + evdev_send_nfingers(evdev, nfingers); + EVDEV_EXIT(evdev); +} diff --git a/sys/dev/evdev/uinput.c b/sys/dev/evdev/uinput.c index ceecee652ac..e7854e89f64 100644 --- a/sys/dev/evdev/uinput.c +++ b/sys/dev/evdev/uinput.c @@ -495,6 +495,7 @@ uinput_ioctl_sub(struct uinput_cdev_state *state, u_long cmd, caddr_t data) evdev_set_methods(state->ucs_evdev, state, &uinput_ev_methods); evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_SOFTREPEAT); + evdev_set_flag(state->ucs_evdev, EVDEV_FLAG_MT_KEEPID); ret = evdev_register(state->ucs_evdev); if (ret == 0) state->ucs_state = UINPUT_RUNNING; diff --git a/sys/dev/usb/input/wsp.c b/sys/dev/usb/input/wsp.c index c44c4aceb30..5381a0ea20f 100644 --- a/sys/dev/usb/input/wsp.c +++ b/sys/dev/usb/input/wsp.c @@ -29,6 +29,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_evdev.h" + #include #include #include @@ -56,6 +58,11 @@ __FBSDID("$FreeBSD$"); #define USB_DEBUG_VAR wsp_debug #include +#ifdef EVDEV_SUPPORT +#include +#include +#endif + #include #define WSP_DRIVER_NAME "wsp" @@ -226,9 +233,14 @@ struct tp_finger { } __packed; /* trackpad finger data size, empirically at least ten fingers */ +#ifdef EVDEV_SUPPORT +#define MAX_FINGERS MAX_MT_SLOTS +#else #define MAX_FINGERS 16 +#endif #define SIZEOF_FINGER sizeof(struct tp_finger) #define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER) +#define MAX_FINGER_ORIENTATION 16384 #if (WSP_BUFFER_MAX < ((MAX_FINGERS * FSIZE_TYPE4) + FINGER_TYPE4)) #error "WSP_BUFFER_MAX is too small" @@ -251,6 +263,13 @@ enum { WSP_FLAG_MAX, }; +/* device-specific parameters */ +struct wsp_param { + int snratio; /* signal-to-noise ratio */ + int min; /* device minimum reading */ + int max; /* device maximum reading */ +}; + /* device-specific configuration */ struct wsp_dev_params { uint8_t caps; /* device capability bitmask */ @@ -266,8 +285,19 @@ struct wsp_dev_params { uint8_t um_switch_idx; /* usb control message mode switch index */ uint8_t um_switch_on; /* usb control message mode switch on */ uint8_t um_switch_off; /* usb control message mode switch off */ + struct wsp_param p; /* finger pressure limits */ + struct wsp_param w; /* finger width limits */ + struct wsp_param x; /* horizontal limits */ + struct wsp_param y; /* vertical limits */ + struct wsp_param o; /* orientation limits */ }; +/* logical signal quality */ +#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ +#define SN_WIDTH 25 /* width signal-to-noise ratio */ +#define SN_COORD 250 /* coordinate signal-to-noise ratio */ +#define SN_ORIENT 10 /* orientation signal-to-noise ratio */ + static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { [WSP_FLAG_WELLSPRING1] = { .caps = 0, @@ -283,6 +313,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 256 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4824, 5342 }, + .y = { SN_COORD, -172, 5820 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING2] = { .caps = 0, @@ -298,6 +334,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 256 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4824, 4824 }, + .y = { SN_COORD, -172, 4290 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING3] = { .caps = HAS_INTEGRATED_BUTTON, @@ -313,6 +355,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4460, 5166 }, + .y = { SN_COORD, -75, 6700 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING4] = { .caps = HAS_INTEGRATED_BUTTON, @@ -328,6 +376,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4620, 5140 }, + .y = { SN_COORD, -150, 6600 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING4A] = { .caps = HAS_INTEGRATED_BUTTON, @@ -343,6 +397,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4616, 5112 }, + .y = { SN_COORD, -142, 5234 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING5] = { .caps = HAS_INTEGRATED_BUTTON, @@ -358,6 +418,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4415, 5050 }, + .y = { SN_COORD, -55, 6680 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING6] = { .caps = HAS_INTEGRATED_BUTTON, @@ -373,6 +439,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4620, 5140 }, + .y = { SN_COORD, -150, 6600 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING5A] = { .caps = HAS_INTEGRATED_BUTTON, @@ -388,6 +460,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4750, 5280 }, + .y = { SN_COORD, -150, 6730 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING6A] = { .caps = HAS_INTEGRATED_BUTTON, @@ -402,6 +480,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4620, 5140 }, + .y = { SN_COORD, -150, 6600 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING7] = { .caps = HAS_INTEGRATED_BUTTON, @@ -417,6 +501,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4750, 5280 }, + .y = { SN_COORD, -150, 6730 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING7A] = { .caps = HAS_INTEGRATED_BUTTON, @@ -432,6 +522,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4750, 5280 }, + .y = { SN_COORD, -150, 6730 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING8] = { .caps = HAS_INTEGRATED_BUTTON, @@ -447,6 +543,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 0, .um_switch_on = 0x01, .um_switch_off = 0x08, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4620, 5140 }, + .y = { SN_COORD, -150, 6600 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, [WSP_FLAG_WELLSPRING9] = { .caps = HAS_INTEGRATED_BUTTON, @@ -462,6 +564,12 @@ static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = { .um_switch_idx = 1, .um_switch_on = 0x01, .um_switch_off = 0x00, + .p = { SN_PRESSURE, 0, 300 }, + .w = { SN_WIDTH, 0, 2048 }, + .x = { SN_COORD, -4828, 5345 }, + .y = { SN_COORD, -203, 6803 }, + .o = { SN_ORIENT, + -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }, }, }; #define WSP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } @@ -550,12 +658,17 @@ struct wsp_softc { const struct wsp_dev_params *sc_params; /* device configuration */ +#ifdef EVDEV_SUPPORT + struct evdev_dev *sc_evdev; +#endif mousehw_t sc_hw; mousemode_t sc_mode; u_int sc_pollrate; mousestatus_t sc_status; + int sc_fflags; u_int sc_state; -#define WSP_ENABLED 0x01 +#define WSP_ENABLED 0x01 +#define WSP_EVDEV_OPENED 0x02 struct tp_finger *index[MAX_FINGERS]; /* finger index data */ int16_t pos_x[MAX_FINGERS]; /* position array */ @@ -596,8 +709,8 @@ struct wsp_softc { /* * function prototypes */ -static usb_fifo_cmd_t wsp_start_read; -static usb_fifo_cmd_t wsp_stop_read; +static usb_fifo_cmd_t wsp_fifo_start_read; +static usb_fifo_cmd_t wsp_fifo_stop_read; static usb_fifo_open_t wsp_open; static usb_fifo_close_t wsp_close; static usb_fifo_ioctl_t wsp_ioctl; @@ -606,11 +719,20 @@ static struct usb_fifo_methods wsp_fifo_methods = { .f_open = &wsp_open, .f_close = &wsp_close, .f_ioctl = &wsp_ioctl, - .f_start_read = &wsp_start_read, - .f_stop_read = &wsp_stop_read, + .f_start_read = &wsp_fifo_start_read, + .f_stop_read = &wsp_fifo_stop_read, .basename[0] = WSP_DRIVER_NAME, }; +#ifdef EVDEV_SUPPORT +static evdev_open_t wsp_ev_open; +static evdev_close_t wsp_ev_close; +static const struct evdev_methods wsp_evdev_methods = { + .ev_open = &wsp_ev_open, + .ev_close = &wsp_ev_close, +}; +#endif + /* device initialization and shutdown */ static int wsp_enable(struct wsp_softc *sc); static void wsp_disable(struct wsp_softc *sc); @@ -816,6 +938,55 @@ wsp_attach(device_t dev) sc->sc_touch = WSP_UNTOUCH; sc->scr_mode = WSP_SCR_NONE; +#ifdef EVDEV_SUPPORT + sc->sc_evdev = evdev_alloc(); + evdev_set_name(sc->sc_evdev, device_get_desc(dev)); + evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev)); + evdev_set_id(sc->sc_evdev, BUS_USB, uaa->info.idVendor, + uaa->info.idProduct, 0); + evdev_set_serial(sc->sc_evdev, usb_get_serial(uaa->device)); + evdev_set_methods(sc->sc_evdev, sc, &wsp_evdev_methods); + evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER); + evdev_support_event(sc->sc_evdev, EV_SYN); + evdev_support_event(sc->sc_evdev, EV_ABS); + evdev_support_event(sc->sc_evdev, EV_KEY); + +#define WSP_SUPPORT_ABS(evdev, code, param) \ + evdev_support_abs((evdev), (code), (param).min, (param).max, \ + ((param).max - (param).min) / (param).snratio, 0, 0); + + /* finger position */ + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x); + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y); + /* finger pressure */ + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p); + /* finger touch area */ + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w); + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w); + /* finger approach area */ + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w); + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w); + /* finger orientation */ + WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o); + /* button properties */ + evdev_support_key(sc->sc_evdev, BTN_LEFT); + if ((sc->sc_params->caps & HAS_INTEGRATED_BUTTON) != 0) + evdev_support_prop(sc->sc_evdev, INPUT_PROP_BUTTONPAD); + /* Enable automatic touch assignment for type B MT protocol */ + evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT, + 0, MAX_FINGERS - 1, 0, 0, 0); + evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID, + -1, MAX_FINGERS - 1, 0, 0, 0); + evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK); + evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL); + /* Synaptics compatibility events */ + evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT); + + err = evdev_register(sc->sc_evdev); + if (err) + goto detach; +#endif + return (0); detach: @@ -837,6 +1008,10 @@ wsp_detach(device_t dev) usb_fifo_detach(&sc->sc_fifo); +#ifdef EVDEV_SUPPORT + evdev_free(sc->sc_evdev); +#endif + usbd_transfer_unsetup(sc->sc_xfer, WSP_N_TRANSFER); mtx_destroy(&sc->sc_mutex); @@ -929,8 +1104,30 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) sc->pos_x[i] = f->abs_x; sc->pos_y[i] = -f->abs_y; sc->index[i] = f; +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + union evdev_mt_slot slot_data = { + .id = i, + .x = f->abs_x, + .y = params->y.max + params->y.min - f->abs_y, + .p = f->pressure, + .maj = f->touch_major << 1, + .min = f->touch_minor << 1, + .w_maj = f->tool_major << 1, + .w_min = f->tool_minor << 1, + .ori = params->o.max - f->orientation, + }; + evdev_mt_push_slot(sc->sc_evdev, i, &slot_data); + } +#endif } +#ifdef EVDEV_SUPPORT + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt); + evdev_sync(sc->sc_evdev); + } +#endif sc->sc_status.flags &= ~MOUSE_POSCHANGED; sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED; sc->sc_status.obutton = sc->sc_status.button; @@ -1235,9 +1432,8 @@ wsp_reset_buf(struct wsp_softc *sc) } static void -wsp_start_read(struct usb_fifo *fifo) +wsp_start_read(struct wsp_softc *sc) { - struct wsp_softc *sc = usb_fifo_softc(fifo); int rate; /* Check if we should override the default polling interval */ @@ -1258,49 +1454,109 @@ wsp_start_read(struct usb_fifo *fifo) } static void -wsp_stop_read(struct usb_fifo *fifo) +wsp_stop_read(struct wsp_softc *sc) { - struct wsp_softc *sc = usb_fifo_softc(fifo); - usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]); } static int wsp_open(struct usb_fifo *fifo, int fflags) { - DPRINTFN(WSP_LLEVEL_INFO, "\n"); + struct wsp_softc *sc = usb_fifo_softc(fifo); + int rc = 0; - if (fflags & FREAD) { - struct wsp_softc *sc = usb_fifo_softc(fifo); - int rc; + DPRINTFN(WSP_LLEVEL_INFO, "\n"); - if (sc->sc_state & WSP_ENABLED) - return (EBUSY); + if (sc->sc_fflags & fflags) + return (EBUSY); + if (fflags & FREAD) { if (usb_fifo_alloc_buffer(fifo, WSP_FIFO_BUF_SIZE, WSP_FIFO_QUEUE_MAXLEN)) { return (ENOMEM); } - rc = wsp_enable(sc); +#ifdef EVDEV_SUPPORT + if ((sc->sc_state & WSP_EVDEV_OPENED) == 0) +#endif + rc = wsp_enable(sc); if (rc != 0) { usb_fifo_free_buffer(fifo); return (rc); } } + sc->sc_fflags |= fflags & (FREAD | FWRITE); return (0); } static void wsp_close(struct usb_fifo *fifo, int fflags) { - if (fflags & FREAD) { - struct wsp_softc *sc = usb_fifo_softc(fifo); + struct wsp_softc *sc = usb_fifo_softc(fifo); - wsp_disable(sc); + if (fflags & FREAD) { +#ifdef EVDEV_SUPPORT + if ((sc->sc_state & WSP_EVDEV_OPENED) == 0) +#endif + wsp_disable(sc); usb_fifo_free_buffer(fifo); } + + sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); +} + +static void +wsp_fifo_start_read(struct usb_fifo *fifo) +{ + struct wsp_softc *sc = usb_fifo_softc(fifo); + + wsp_start_read(sc); +} + +static void +wsp_fifo_stop_read(struct usb_fifo *fifo) +{ + struct wsp_softc *sc = usb_fifo_softc(fifo); + +#ifdef EVDEV_SUPPORT + if ((sc->sc_state & WSP_EVDEV_OPENED) == 0) +#endif + wsp_stop_read(sc); } +#ifdef EVDEV_SUPPORT +static int +wsp_ev_open(struct evdev_dev *evdev) +{ + struct wsp_softc *sc = evdev_get_softc(evdev); + int rc = 0; + + mtx_lock(&sc->sc_mutex); + if (sc->sc_fflags == 0) + rc = wsp_enable(sc); + if (rc == 0) { + wsp_start_read(sc); + sc->sc_state |= WSP_EVDEV_OPENED; + } + mtx_unlock(&sc->sc_mutex); + + return (rc); +} + +static int +wsp_ev_close(struct evdev_dev *evdev) +{ + struct wsp_softc *sc = evdev_get_softc(evdev); + + mtx_lock(&sc->sc_mutex); + sc->sc_state &= ~WSP_EVDEV_OPENED; + if (sc->sc_fflags == 0) + wsp_stop_read(sc); + mtx_unlock(&sc->sc_mutex); + + return (0); +} +#endif + int wsp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { @@ -1412,5 +1668,8 @@ static devclass_t wsp_devclass; DRIVER_MODULE(wsp, uhub, wsp_driver, wsp_devclass, NULL, 0); MODULE_DEPEND(wsp, usb, 1, 1, 1); MODULE_DEPEND(wsp, hid, 1, 1, 1); +#ifdef EVDEV_SUPPORT +MODULE_DEPEND(wsp, evdev, 1, 1, 1); +#endif MODULE_VERSION(wsp, 1); USB_PNP_HOST_INFO(wsp_devs); diff --git a/sys/modules/usb/wsp/Makefile b/sys/modules/usb/wsp/Makefile index a5215c0150f..73a289a0954 100644 --- a/sys/modules/usb/wsp/Makefile +++ b/sys/modules/usb/wsp/Makefile @@ -30,7 +30,7 @@ S= ${SRCTOP}/sys .PATH: $S/dev/usb/input KMOD= wsp -SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h vnode_if.h usbdevs.h \ - wsp.c +SRCS= opt_bus.h opt_evdev.h opt_usb.h device_if.h bus_if.h usb_if.h \ + vnode_if.h usbdevs.h wsp.c .include