diff --git a/sys/conf/files b/sys/conf/files index 80381192c12..40427287249 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1820,6 +1820,7 @@ dev/gpio/gpio_if.m optional gpio dev/gpio/gpiobus_if.m optional gpio dev/gpio/gpiopps.c optional gpiopps fdt dev/gpio/ofw_gpiobus.c optional fdt gpio +dev/hid/bcm5974.c optional bcm5974 dev/hid/hconf.c optional hconf dev/hid/hcons.c optional hcons dev/hid/hgame.c optional hgame 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..40c44878ef4 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_KEEPID 0x04 /* Do not reassign tracking ID */ +#define EVDEV_FLAG_MT_TRACK 0x05 /* Assign touch to slot by evdev */ #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..4dc8bb475d7 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,92 @@ #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); + 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 +150,498 @@ 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 tracking IDs to the points in the pt array. The tracking ID + * 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); + + if (size == 0) + return; - evdev->ev_mt->ev_mt_slots[slot].ev_report = evdev->ev_report_count; - evdev->ev_mt->ev_mt_last_reported_slot = slot; + 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 = mt->tracking_ids[slot]; + + p = (n == size ? c2r : r2c); + for (i = 0; i < size; i++) + if (*p++ < 0) + pt[i].id = -1; } -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) { + struct evdev_mt *mt = evdev->ev_mt; + union evdev_mt_slot *slot; - return (evdev->ev_mt-> - ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)]); + EVDEV_LOCK_ASSERT(evdev); + MPASS(size >= 0 && size <= MAXIMAL_MT_SLOT(evdev) + 1); + + /* + * While MT-matching assign tracking IDs of new contacts to be equal + * to a slot number to make things simpler. + */ + for (slot = pt; slot < pt + size; slot++) { + if (slot->id < 0) + slot->id = ffc_slot(evdev, mt->touches | mt->frame); + 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; + + MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev)); - for (i = 0; i < MIN(nitems(evdev_fngmap), nfingers); i++) - evdev_support_key(evdev, evdev_fngmap[i]); + 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; + + KASSERT(!mt->type_a, ("Not a MT type B protocol")); - for (i = 0; i <= MAXIMAL_MT_SLOT(evdev); i++) - if (evdev_get_mt_value(evdev, i, ABS_MT_TRACKING_ID) != -1) - 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)); - return (nfingers); + 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); + } + + 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); + } - if (nfingers > nitems(evdev_fngmap)) - nfingers = nitems(evdev_fngmap); + 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; - for (i = 0; i < nitems(evdev_fngmap); i++) - evdev_send_event(evdev, EV_KEY, evdev_fngmap[i], - nfingers == i + 1); + 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 +656,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..951848c25cb 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); +int32_t evdev_mt_reassign_id(struct evdev_dev *, int, int32_t); +bool evdev_mt_record_event(struct evdev_dev *, uint16_t, uint16_t, 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/hid/bcm5974.c b/sys/dev/hid/bcm5974.c new file mode 100644 index 00000000000..807114dfa4f --- /dev/null +++ b/sys/dev/hid/bcm5974.c @@ -0,0 +1,650 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2012 Huang Wen Hui + * Copyright (c) 2021 Vladimir Kondratyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define HID_DEBUG_VAR bcm5974_debug +#include +#include +#include + +#include "usbdevs.h" + +/* Tunables */ +static SYSCTL_NODE(_hw_hid, OID_AUTO, bcm5974, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "HID wellspring touchpad"); + +#ifdef HID_DEBUG +enum wsp_log_level { + BCM5974_LLEVEL_DISABLED = 0, + BCM5974_LLEVEL_ERROR, + BCM5974_LLEVEL_DEBUG, /* for troubleshooting */ + BCM5974_LLEVEL_INFO, /* for diagnostics */ +}; +/* the default is to only log errors */ +static int bcm5974_debug = BCM5974_LLEVEL_ERROR; + +SYSCTL_INT(_hw_hid_bcm5974, OID_AUTO, debug, CTLFLAG_RWTUN, + &bcm5974_debug, BCM5974_LLEVEL_ERROR, "BCM5974 debug level"); +#endif /* HID_DEBUG */ + +/* + * Some tables, structures, definitions and constant values for the + * touchpad protocol has been copied from Linux's + * "drivers/input/mouse/bcm5974.c" which has the following copyright + * holders under GPLv2. All device specific code in this driver has + * been written from scratch. The decoding algorithm is based on + * output from FreeBSD's usbdump. + * + * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) + * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) + * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) + */ + +/* button data structure */ +struct bt_data { + uint8_t unknown1; /* constant */ + uint8_t button; /* left button */ + uint8_t rel_x; /* relative x coordinate */ + uint8_t rel_y; /* relative y coordinate */ +} __packed; + +/* trackpad header types */ +enum tp_type { + TYPE1, /* plain trackpad */ + TYPE2, /* button integrated in trackpad */ + TYPE3, /* additional header fields since June 2013 */ + TYPE4, /* additional header field for pressure data */ + TYPE_CNT +}; + +/* list of device capability bits */ +#define HAS_INTEGRATED_BUTTON 1 + +struct tp { + uint8_t caps; /* device capability bitmask */ + uint8_t button; /* offset to button data */ + uint8_t offset; /* offset to trackpad finger data */ + uint8_t fsize; /* bytes in single finger block */ + uint8_t delta; /* offset from header to finger struct */ +} const static tp[TYPE_CNT] = { + [TYPE1] = { + .caps = 0, + .button = 0, + .offset = 13 * 2, + .fsize = 14 * 2, + .delta = 0, + }, + [TYPE2] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = 15, + .offset = 15 * 2, + .fsize = 14 * 2, + .delta = 0, + }, + [TYPE3] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = 23, + .offset = 19 * 2, + .fsize = 14 * 2, + .delta = 0, + }, + [TYPE4] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = 31, + .offset = 23 * 2, + .fsize = 15 * 2, + .delta = 2, + }, +}; + +/* trackpad finger header - little endian */ +struct tp_header { + uint8_t flag; + uint8_t sn0; + uint16_t wFixed0; + uint32_t dwSn1; + uint32_t dwFixed1; + uint16_t wLength; + uint8_t nfinger; + uint8_t ibt; + int16_t wUnknown[6]; + uint8_t q1; + uint8_t q2; +} __packed; + +/* trackpad finger structure - little endian */ +struct tp_finger { + int16_t origin; /* zero when switching track finger */ + int16_t abs_x; /* absolute x coodinate */ + int16_t abs_y; /* absolute y coodinate */ + int16_t rel_x; /* relative x coodinate */ + int16_t rel_y; /* relative y coodinate */ + int16_t tool_major; /* tool area, major axis */ + int16_t tool_minor; /* tool area, minor axis */ + int16_t orientation; /* 16384 when point, else 15 bit angle */ + int16_t touch_major; /* touch area, major axis */ + int16_t touch_minor; /* touch area, minor axis */ + int16_t unused[2]; /* zeros */ + int16_t pressure; /* pressure on forcetouch touchpad */ + int16_t multi; /* one finger: varies, more fingers: + * constant */ +} __packed; + +/* trackpad finger data size, empirically at least ten fingers */ +#define MAX_FINGERS MAX_MT_SLOTS + +#define MAX_FINGER_ORIENTATION 16384 + +enum { + BCM5974_FLAG_WELLSPRING1, + BCM5974_FLAG_WELLSPRING2, + BCM5974_FLAG_WELLSPRING3, + BCM5974_FLAG_WELLSPRING4, + BCM5974_FLAG_WELLSPRING4A, + BCM5974_FLAG_WELLSPRING5, + BCM5974_FLAG_WELLSPRING6A, + BCM5974_FLAG_WELLSPRING6, + BCM5974_FLAG_WELLSPRING5A, + BCM5974_FLAG_WELLSPRING7, + BCM5974_FLAG_WELLSPRING7A, + BCM5974_FLAG_WELLSPRING8, + BCM5974_FLAG_WELLSPRING9, + BCM5974_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 bcm5974_dev_params { + const struct tp* tp; + 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 bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = { + [BCM5974_FLAG_WELLSPRING1] = { + .tp = tp + TYPE1, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING2] = { + .tp = tp + TYPE1, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING3] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING4] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING4A] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING5] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING6] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING5A] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING6A] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING7] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING7A] = { + .tp = tp + TYPE2, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING8] = { + .tp = tp + TYPE3, + .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 }, + }, + [BCM5974_FLAG_WELLSPRING9] = { + .tp = tp + TYPE4, + .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) { \ + HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), \ + HID_TLC(0xff00, 0x000c), \ +} + +static const struct hid_device_id bcm5974_devs[] = { + /* MacbookAir6,2 (unibody, June 2013) */ + WSP_DEV(APPLE, WELLSPRING8_ANSI, BCM5974_FLAG_WELLSPRING8), + WSP_DEV(APPLE, WELLSPRING8_ISO, BCM5974_FLAG_WELLSPRING8), + WSP_DEV(APPLE, WELLSPRING8_JIS, BCM5974_FLAG_WELLSPRING8), + + /* MacbookPro12,1 MacbookPro11,4 */ + WSP_DEV(APPLE, WELLSPRING9_ANSI, BCM5974_FLAG_WELLSPRING9), + WSP_DEV(APPLE, WELLSPRING9_ISO, BCM5974_FLAG_WELLSPRING9), + WSP_DEV(APPLE, WELLSPRING9_JIS, BCM5974_FLAG_WELLSPRING9), +}; + +struct bcm5974_softc { + device_t sc_dev; + /* device configuration */ + const struct bcm5974_dev_params *sc_params; + struct evdev_dev *sc_evdev; + int tp_datalen; +}; + +/* + * function prototypes + */ +static evdev_open_t bcm5974_ev_open; +static evdev_close_t bcm5974_ev_close; +static const struct evdev_methods bcm5974_evdev_methods = { + .ev_open = &bcm5974_ev_open, + .ev_close = &bcm5974_ev_close, +}; +static hid_intr_t bcm5974_intr; + +/* Device methods. */ +static device_probe_t bcm5974_probe; +static device_attach_t bcm5974_attach; +static device_detach_t bcm5974_detach; + +static int +bcm5974_set_device_mode(struct bcm5974_softc *sc, bool on) +{ + const struct bcm5974_dev_params *params = sc->sc_params; + uint8_t mode_bytes[2]; + int err; + + /* Type 3 does not require a mode switch */ + if (params->tp == tp + TYPE3) + return (0); + + err = hid_get_report(sc->sc_dev, mode_bytes, sizeof(mode_bytes), NULL, + HID_FEATURE_REPORT, 0x02); + if (err != 0) { + DPRINTF("Failed to read device mode (%d)\n", err); + return (err); + } + + /* + * XXX Need to wait at least 250ms for hardware to get + * ready. The device mode handling appears to be handled + * asynchronously and we should not issue these commands too + * quickly. + */ + pause("WHW", hz / 4); + + mode_bytes[1] = on ? 0x01 : 0x00; + + return (hid_set_report(sc->sc_dev, mode_bytes, sizeof(mode_bytes), + HID_FEATURE_REPORT, 0x02)); +} + +static int +bcm5974_probe(device_t dev) +{ + int err; + + err = HIDBUS_LOOKUP_DRIVER_INFO(dev, bcm5974_devs); + if (err != 0) + return (err); + + hidbus_set_desc(dev, "Touchpad"); + + return (BUS_PROBE_DEFAULT); +} + +static int +bcm5974_attach(device_t dev) +{ + struct bcm5974_softc *sc = device_get_softc(dev); + const struct hid_device_info *hw = hid_get_device_info(dev); + void *d_ptr; + hid_size_t d_len; + int err; + + DPRINTFN(WSP_LLEVEL_INFO, "sc=%p\n", sc); + + /* Get HID descriptor */ + err = hid_get_report_descr(dev, &d_ptr, &d_len); + if (err != 0) + return (ENXIO); + + /* Get HID report descriptor length */ + sc->tp_datalen = hid_report_size_max(d_ptr, d_len, hid_input, NULL); + + /* get device specific configuration */ + sc->sc_params = bcm5974_dev_params + hidbus_get_driver_info(dev); + + /* + * By default the touchpad behaves like a HID device, sending + * packets with reportID = 8. Such reports contain only + * limited information. They encode movement deltas and button + * events, but do not include data from the pressure + * sensors. The device input mode can be switched from HID + * reports to raw sensor data using vendor-specific USB + * control commands: + */ + + /* + * During re-enumeration of the device we need to force the + * device back into HID mode before switching it to RAW + * mode. Else the device does not work like expected. + */ + err = bcm5974_set_device_mode(sc, false); + if (err != 0) { + DPRINTF("Failed to set mode to HID MODE (%d)\n", err); + return (ENXIO); + } + + err = bcm5974_set_device_mode(sc, true); + if (err != 0) { + DPRINTF("failed to set mode to RAW MODE (%d)\n", err); + return (ENXIO); + } + + 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, hw->idBus, hw->idVendor, hw->idProduct, + hw->idVersion); + evdev_set_serial(sc->sc_evdev, hw->serial); + evdev_set_methods(sc->sc_evdev, sc, &bcm5974_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 BCM5974_ABS(evdev, code, param) \ + evdev_support_abs((evdev), (code), (param).min, (param).max, \ + ((param).max - (param).min) / (param).snratio, 0, 0); + + /* finger position */ + BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x); + BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y); + /* finger pressure */ + BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p); + /* finger touch area */ + BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w); + BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w); + /* finger approach area */ + BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w); + BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w); + /* finger orientation */ + BCM5974_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->tp->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; + + return (0); + +detach: + bcm5974_detach(dev); + return (ENOMEM); +} + +static int +bcm5974_detach(device_t dev) +{ + struct bcm5974_softc *sc = device_get_softc(dev); + + (void) bcm5974_set_device_mode(sc, 0); + + evdev_free(sc->sc_evdev); + + return (0); +} + +static void +bcm5974_intr(void *context, void *data, hid_size_t len) +{ + struct bcm5974_softc *sc = context; + const struct bcm5974_dev_params *params = sc->sc_params; + union evdev_mt_slot slot_data; + struct tp_finger *f; + struct tp_header *h; + int ntouch = 0; /* the finger number in touch */ + int ibt = 0; /* button status */ + int i; + int slot = 0; + + if ((len < params->tp->offset + params->tp->fsize) || + ((len - params->tp->offset) % params->tp->fsize) != 0) { + DPRINTFN(WSP_LLEVEL_INFO, "Invalid length: %d, %x, %x\n", + len, sc->tp_data[0], sc->tp_data[1]); + return; + } + + if (len < sc->tp_datalen) { + /* make sure we don't process old data */ + memset((char *)data + len, 0, sc->tp_datalen - len); + } + + h = (struct tp_header *)data; + + ibt = ((uint8_t *)data)[params->tp->button]; + ntouch = ((uint8_t *)data)[params->tp->button - 1]; + + /* range check */ + if (ntouch < 0) + ntouch = 0; + else if (ntouch > MAX_FINGERS) + ntouch = MAX_FINGERS; + + for (i = 0; i != ntouch; i++) { + f = (struct tp_finger *)(((uint8_t *)data) + + params->tp->offset + params->tp->delta + + i * params->tp->fsize); + DPRINTFN(WSP_LLEVEL_INFO, + "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, " + "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, " + "tchmaj=%4d, tchmin=%4d, presure=%4d, m=%4x\n", + i, ibt, ntouch, le16toh(f->origin), le16toh(f->abs_x), + le16toh(f->abs_y), le16toh(f->rel_x), le16toh(f->rel_y), + le16toh(f->tool_major), le16toh(f->tool_minor), + le16toh(f->orientation), le16toh(f->touch_major), + le16toh(f->touch_minor), le16toh(f->pressure), + le16toh(f->multi)); + + if (f->touch_major == 0) + continue; + slot_data = (union evdev_mt_slot) { + .id = slot, + .x = le16toh(f->abs_x), + .y = params->y.min + params->y.max - le16toh(f->abs_y), + .p = le16toh(f->pressure), + .maj = le16toh(f->touch_major) << 1, + .min = le16toh(f->touch_minor) << 1, + .w_maj = le16toh(f->tool_major) << 1, + .w_min = le16toh(f->tool_minor) << 1, + .ori = params->o.max - le16toh(f->orientation), + }; + evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data); + slot++; + } + + evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt); + evdev_sync(sc->sc_evdev); +} + +static int +bcm5974_ev_open(struct evdev_dev *evdev) +{ + return (hidbus_intr_start(evdev_get_softc(evdev))); +} + +static int +bcm5974_ev_close(struct evdev_dev *evdev) +{ + return (hidbus_intr_stop(evdev_get_softc(evdev))); +} + +static device_method_t bcm5974_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bcm5974_probe), + DEVMETHOD(device_attach, bcm5974_attach), + DEVMETHOD(device_detach, bcm5974_detach), + DEVMETHOD_END +}; + +static driver_t bcm5974_driver = { + .name = "bcm5974", + .methods = bcm5974_methods, + .size = sizeof(struct bcm5974_softc) +}; + +static devclass_t bcm5974_devclass; + +DRIVER_MODULE(bcm5974, hidbus, bcm5974_driver, bcm5974_devclass, NULL, 0); +MODULE_DEPEND(bcm5974, hidbus, 1, 1, 1); +MODULE_DEPEND(bcm5974, hid, 1, 1, 1); +MODULE_DEPEND(bcm5974, evdev, 1, 1, 1); +MODULE_VERSION(bcm5974, 1); +HID_PNP_INFO(bcm5974_devs); diff --git a/sys/dev/usb/input/wsp.c b/sys/dev/usb/input/wsp.c index c44c4aceb30..1130347ee24 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" @@ -169,7 +176,8 @@ enum tp_type { TYPE1, /* plain trackpad */ TYPE2, /* button integrated in trackpad */ TYPE3, /* additional header fields since June 2013 */ - TYPE4 /* additional header field for pressure data */ + TYPE4, /* additional header field for pressure data */ + TYPE_CNT }; /* trackpad finger data offsets, le16-aligned */ @@ -192,6 +200,67 @@ enum tp_type { #define FSIZE_TYPE3 (14 * 2) #define FSIZE_TYPE4 (15 * 2) +struct tp { + uint8_t caps; /* device capability bitmask */ + uint8_t button; /* offset to button data */ + uint8_t offset; /* offset to trackpad finger data */ + uint8_t fsize; /* bytes in single finger block */ + uint8_t delta; /* offset from header to finger struct */ + uint8_t iface_index; + uint8_t um_size; /* usb control message length */ + uint8_t um_req_idx; /* usb control message index */ + 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 */ +} const static tp[TYPE_CNT] = { + [TYPE1] = { + .caps = 0, + .button = 0, + .offset = FINGER_TYPE1, + .fsize = FSIZE_TYPE1, + .delta = 0, + .iface_index = 0, + .um_size = 8, + .um_req_idx = 0x00, + .um_switch_idx = 0, + .um_switch_on = 0x01, + .um_switch_off = 0x08, + }, + [TYPE2] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = BUTTON_TYPE2, + .offset = FINGER_TYPE2, + .fsize = FSIZE_TYPE2, + .delta = 0, + .iface_index = 0, + .um_size = 8, + .um_req_idx = 0x00, + .um_switch_idx = 0, + .um_switch_on = 0x01, + .um_switch_off = 0x08, + }, + [TYPE3] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = BUTTON_TYPE3, + .offset = FINGER_TYPE3, + .fsize = FSIZE_TYPE3, + .delta = 0, + }, + [TYPE4] = { + .caps = HAS_INTEGRATED_BUTTON, + .button = BUTTON_TYPE4, + .offset = FINGER_TYPE4, + .fsize = FSIZE_TYPE4, + .delta = 2, + .iface_index = 2, + .um_size = 2, + .um_req_idx = 0x02, + .um_switch_idx = 1, + .um_switch_on = 0x01, + .um_switch_off = 0x00, + }, +}; + /* trackpad finger header - little endian */ struct tp_header { uint8_t flag; @@ -226,9 +295,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,217 +325,146 @@ 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 */ - uint8_t tp_type; /* type of trackpad interface */ - uint8_t tp_button; /* offset to button data */ - uint8_t tp_offset; /* offset to trackpad finger data */ - uint8_t tp_fsize; /* bytes in single finger block */ - uint8_t tp_delta; /* offset from header to finger struct */ - uint8_t iface_index; - uint8_t um_size; /* usb control message length */ - uint8_t um_req_val; /* usb control message value */ - uint8_t um_req_idx; /* usb control message index */ - 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 */ + const struct tp* tp; + 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, - .tp_type = TYPE1, - .tp_button = 0, - .tp_offset = FINGER_TYPE1, - .tp_fsize = FSIZE_TYPE1, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE1, + .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, - .tp_type = TYPE1, - .tp_button = 0, - .tp_offset = FINGER_TYPE1, - .tp_fsize = FSIZE_TYPE1, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE1, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE2, - .tp_button = BUTTON_TYPE2, - .tp_offset = FINGER_TYPE2, - .tp_fsize = FSIZE_TYPE2, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE2, + .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, - .tp_type = TYPE3, - .tp_button = BUTTON_TYPE3, - .tp_offset = FINGER_TYPE3, - .tp_fsize = FSIZE_TYPE3, - .tp_delta = 0, - .iface_index = 0, - .um_size = 8, - .um_req_val = 0x03, - .um_req_idx = 0x00, - .um_switch_idx = 0, - .um_switch_on = 0x01, - .um_switch_off = 0x08, + .tp = tp + TYPE3, + .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, - .tp_type = TYPE4, - .tp_button = BUTTON_TYPE4, - .tp_offset = FINGER_TYPE4, - .tp_fsize = FSIZE_TYPE4, - .tp_delta = 2, - .iface_index = 2, - .um_size = 2, - .um_req_val = 0x03, - .um_req_idx = 0x02, - .um_switch_idx = 1, - .um_switch_on = 0x01, - .um_switch_off = 0x00, + .tp = tp + TYPE4, + .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 +553,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 +604,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 +614,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); @@ -647,12 +664,12 @@ wsp_set_device_mode(struct wsp_softc *sc, uint8_t on) usb_error_t err; /* Type 3 does not require a mode switch */ - if (params->tp_type == TYPE3) + if (params->tp == tp + TYPE3) return 0; err = usbd_req_get_report(sc->sc_usb_device, NULL, - mode_bytes, params->um_size, params->iface_index, - params->um_req_val, params->um_req_idx); + mode_bytes, params->tp->um_size, params->tp->iface_index, + UHID_FEATURE_REPORT, params->tp->um_req_idx); if (err != USB_ERR_NORMAL_COMPLETION) { DPRINTF("Failed to read device mode (%d)\n", err); @@ -667,12 +684,12 @@ wsp_set_device_mode(struct wsp_softc *sc, uint8_t on) */ pause("WHW", hz / 4); - mode_bytes[params->um_switch_idx] = - on ? params->um_switch_on : params->um_switch_off; + mode_bytes[params->tp->um_switch_idx] = + on ? params->tp->um_switch_on : params->tp->um_switch_off; return (usbd_req_set_report(sc->sc_usb_device, NULL, - mode_bytes, params->um_size, params->iface_index, - params->um_req_val, params->um_req_idx)); + mode_bytes, params->tp->um_size, params->tp->iface_index, + UHID_FEATURE_REPORT, params->tp->um_req_idx)); } static int @@ -816,6 +833,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->tp->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 +903,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); @@ -863,6 +933,9 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) int rdz = 0; int len; int i; +#ifdef EVDEV_SUPPORT + int slot = 0; +#endif wsp_runing_rangecheck(&tun); @@ -878,8 +951,8 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, sc->tp_data, len); - if ((len < params->tp_offset + params->tp_fsize) || - ((len - params->tp_offset) % params->tp_fsize) != 0) { + if ((len < params->tp->offset + params->tp->fsize) || + ((len - params->tp->offset) % params->tp->fsize) != 0) { DPRINTFN(WSP_LLEVEL_INFO, "Invalid length: %d, %x, %x\n", len, sc->tp_data[0], sc->tp_data[1]); goto tr_setup; @@ -892,10 +965,12 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) h = (struct tp_header *)(sc->tp_data); - if (params->tp_type >= TYPE2) { - ibt = sc->tp_data[params->tp_button]; - ntouch = sc->tp_data[params->tp_button - 1]; - } + if (params->tp != tp + TYPE1) { + ibt = sc->tp_data[params->tp->button]; + ntouch = sc->tp_data[params->tp->button - 1]; + } else + ntouch = (len - params->tp->offset) / params->tp->fsize; + /* range check */ if (ntouch < 0) ntouch = 0; @@ -903,7 +978,7 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) ntouch = MAX_FINGERS; for (i = 0; i != ntouch; i++) { - f = (struct tp_finger *)(sc->tp_data + params->tp_offset + params->tp_delta + i * params->tp_fsize); + f = (struct tp_finger *)(sc->tp_data + params->tp->offset + params->tp->delta + i * params->tp->fsize); /* swap endianness, if any */ if (le16toh(0x1234) != 0x1234) { f->origin = le16toh((uint16_t)f->origin); @@ -929,17 +1004,40 @@ 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 && f->touch_major != 0) { + union evdev_mt_slot slot_data = { + .id = slot, + .x = f->abs_x, + .y = params->y.min + params->y.max - 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, slot, &slot_data); + slot++; + } +#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; sc->sc_status.button = 0; if (ibt != 0) { - if ((params->caps & HAS_INTEGRATED_BUTTON) && ntouch == 2) + if ((params->tp->caps & HAS_INTEGRATED_BUTTON) && ntouch == 2) sc->sc_status.button |= MOUSE_BUTTON3DOWN; - else if ((params->caps & HAS_INTEGRATED_BUTTON) && ntouch == 3) + else if ((params->tp->caps & HAS_INTEGRATED_BUTTON) && ntouch == 3) sc->sc_status.button |= MOUSE_BUTTON2DOWN; else sc->sc_status.button |= MOUSE_BUTTON1DOWN; @@ -986,7 +1084,7 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error) */ switch (sc->ntaps) { case 1: - if (!(params->caps & HAS_INTEGRATED_BUTTON) || tun.enable_single_tap_clicks) { + if (!(params->tp->caps & HAS_INTEGRATED_BUTTON) || tun.enable_single_tap_clicks) { wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); DPRINTFN(WSP_LLEVEL_INFO, "LEFT CLICK!\n"); } @@ -1235,9 +1333,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 +1355,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 +1569,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/hid/Makefile b/sys/modules/hid/Makefile index c4a1f69f249..55c6b4e78f7 100644 --- a/sys/modules/hid/Makefile +++ b/sys/modules/hid/Makefile @@ -8,6 +8,7 @@ SUBDIR = \ hidraw SUBDIR += \ + bcm5974 \ hconf \ hcons \ hgame \ diff --git a/sys/modules/hid/bcm5974/Makefile b/sys/modules/hid/bcm5974/Makefile new file mode 100644 index 00000000000..13a17ec200d --- /dev/null +++ b/sys/modules/hid/bcm5974/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/hid + +KMOD= bcm5974 +SRCS= bcm5974.c +SRCS+= opt_hid.h +SRCS+= bus_if.h device_if.h usbdevs.h + +.include 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