FreeBSD Bugzilla – Attachment 223347 Details for
Bug 252236
atp(4): Need EVDEV support for modern input stack
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
wsp_evdev_support.patch
wsp1.patch (text/plain), 80.56 KB, created by
Vladimir Kondratyev
on 2021-03-16 22:31:22 UTC
(
hide
)
Description:
wsp_evdev_support.patch
Filename:
MIME Type:
Creator:
Vladimir Kondratyev
Created:
2021-03-16 22:31:22 UTC
Size:
80.56 KB
patch
obsolete
>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 <wulf@FreeBSD.org> >+ * Copyright (c) 2016, 2020 Vladimir Kondratyev <wulf@FreeBSD.org> > * 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 <sys/param.h> > #include <sys/lock.h> >@@ -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 <sys/systm.h> > > #include <dev/evdev/evdev.h> >+#include <dev/evdev/evdev_private.h> > #include <dev/evdev/input.h> > > #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 <wulf@FreeBSD.org> >+ * 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 <sys/cdefs.h> >+__FBSDID("$FreeBSD$"); >+ >+#include <sys/param.h> >+#include <sys/bus.h> >+#include <sys/endian.h> >+#include <sys/kernel.h> >+#include <sys/malloc.h> >+#include <sys/module.h> >+#include <sys/sysctl.h> >+#include <sys/systm.h> >+ >+#include <dev/evdev/input.h> >+#include <dev/evdev/evdev.h> >+ >+#define HID_DEBUG_VAR bcm5974_debug >+#include <dev/hid/hid.h> >+#include <dev/hid/hidbus.h> >+#include <dev/hid/hidquirk.h> >+ >+#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 <sys/cdefs.h> > __FBSDID("$FreeBSD$"); > >+#include "opt_evdev.h" >+ > #include <sys/param.h> > #include <sys/systm.h> > #include <sys/kernel.h> >@@ -56,6 +58,11 @@ __FBSDID("$FreeBSD$"); > #define USB_DEBUG_VAR wsp_debug > #include <dev/usb/usb_debug.h> > >+#ifdef EVDEV_SUPPORT >+#include <dev/evdev/input.h> >+#include <dev/evdev/evdev.h> >+#endif >+ > #include <sys/mouse.h> > > #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 <bsd.kmod.mk> >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 <bsd.kmod.mk>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 252236
:
222890
|
223176
|
223223
|
223229
|
223231
|
223232
|
223276
|
223277
|
223347
|
223450
|
223683
|
224948
|
224949
|
224968