1/*-
2 * Copyright (c) 2016 Vladimir Kondratyev <wulf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/dev/evdev/evdev_mt.c 324768 2017-10-19 20:16:40Z wulf $
27 */
28
29#include <sys/param.h>
30#include <sys/lock.h>
31#include <sys/malloc.h>
32#include <sys/mutex.h>
33#include <sys/systm.h>
34
35#include <dev/evdev/evdev.h>
36#include <dev/evdev/evdev_private.h>
37#include <dev/evdev/input.h>
38
39#ifdef DEBUG
40#define	debugf(fmt, args...)	printf("evdev: " fmt "\n", ##args)
41#else
42#define	debugf(fmt, args...)
43#endif
44
45static uint16_t evdev_fngmap[] = {
46	BTN_TOOL_FINGER,
47	BTN_TOOL_DOUBLETAP,
48	BTN_TOOL_TRIPLETAP,
49	BTN_TOOL_QUADTAP,
50	BTN_TOOL_QUINTTAP,
51};
52
53static uint16_t evdev_mtstmap[][2] = {
54	{ ABS_MT_POSITION_X, ABS_X },
55	{ ABS_MT_POSITION_Y, ABS_Y },
56	{ ABS_MT_PRESSURE, ABS_PRESSURE },
57	{ ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH },
58};
59
60struct evdev_mt_slot {
61	uint64_t ev_report;
62	int32_t ev_mt_states[MT_CNT];
63};
64
65struct evdev_mt {
66	int32_t	ev_mt_last_reported_slot;
67	struct evdev_mt_slot ev_mt_slots[];
68};
69
70void
71evdev_mt_init(struct evdev_dev *evdev)
72{
73	int32_t slot, slots;
74
75	slots = MAXIMAL_MT_SLOT(evdev) + 1;
76
77	evdev->ev_mt = malloc(offsetof(struct evdev_mt, ev_mt_slots) +
78	     sizeof(struct evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO);
79
80	/* Initialize multitouch protocol type B states */
81	for (slot = 0; slot < slots; slot++) {
82		/*
83		 * .ev_report should not be initialized to initial value of
84		 * report counter (0) as it brokes free slot detection in
85		 * evdev_get_mt_slot_by_tracking_id. So initialize it to -1
86		 */
87		evdev->ev_mt->ev_mt_slots[slot] = (struct evdev_mt_slot) {
88			.ev_report = 0xFFFFFFFFFFFFFFFFULL,
89			.ev_mt_states[ABS_MT_INDEX(ABS_MT_TRACKING_ID)] = -1,
90		};
91	}
92
93	if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT))
94		evdev_support_mt_compat(evdev);
95}
96
97void
98evdev_mt_free(struct evdev_dev *evdev)
99{
100
101	free(evdev->ev_mt, M_EVDEV);
102}
103
104int32_t
105evdev_get_last_mt_slot(struct evdev_dev *evdev)
106{
107
108	return (evdev->ev_mt->ev_mt_last_reported_slot);
109}
110
111void
112evdev_set_last_mt_slot(struct evdev_dev *evdev, int32_t slot)
113{
114
115	evdev->ev_mt->ev_mt_slots[slot].ev_report = evdev->ev_report_count;
116	evdev->ev_mt->ev_mt_last_reported_slot = slot;
117}
118
119inline int32_t
120evdev_get_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code)
121{
122
123	return (evdev->ev_mt->
124	    ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)]);
125}
126
127inline void
128evdev_set_mt_value(struct evdev_dev *evdev, int32_t slot, int16_t code,
129    int32_t value)
130{
131
132	evdev->ev_mt->ev_mt_slots[slot].ev_mt_states[ABS_MT_INDEX(code)] =
133	    value;
134}
135
136int32_t
137evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id)
138{
139	int32_t tr_id, slot, free_slot = -1;
140
141	for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) {
142		tr_id = evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID);
143		if (tr_id == tracking_id)
144			return (slot);
145		/*
146		 * Its possible that slot will be reassigned in a place of just
147		 * released one within the same report. To avoid this compare
148		 * report counter with slot`s report number updated with each
149		 * ABS_MT_TRACKING_ID change.
150		 */
151		if (free_slot == -1 && tr_id == -1 &&
152		    evdev->ev_mt->ev_mt_slots[slot].ev_report !=
153		    evdev->ev_report_count)
154			free_slot = slot;
155	}
156
157	return (free_slot);
158}
159
160void
161evdev_support_nfingers(struct evdev_dev *evdev, int32_t nfingers)
162{
163	int32_t i;
164
165	for (i = 0; i < MIN(nitems(evdev_fngmap), nfingers); i++)
166		evdev_support_key(evdev, evdev_fngmap[i]);
167}
168
169void
170evdev_support_mt_compat(struct evdev_dev *evdev)
171{
172	int32_t i;
173
174	if (evdev->ev_absinfo == NULL)
175		return;
176
177	evdev_support_event(evdev, EV_KEY);
178	evdev_support_key(evdev, BTN_TOUCH);
179
180	/* Touchscreens should not advertise tap tool capabilities */
181	if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
182		evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1);
183
184	/* Echo 0-th MT-slot as ST-slot */
185	for (i = 0; i < nitems(evdev_mtstmap); i++)
186		if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][0]))
187			evdev_support_abs(evdev, evdev_mtstmap[i][1],
188			    evdev->ev_absinfo[evdev_mtstmap[i][0]].value,
189			    evdev->ev_absinfo[evdev_mtstmap[i][0]].minimum,
190			    evdev->ev_absinfo[evdev_mtstmap[i][0]].maximum,
191			    evdev->ev_absinfo[evdev_mtstmap[i][0]].fuzz,
192			    evdev->ev_absinfo[evdev_mtstmap[i][0]].flat,
193			    evdev->ev_absinfo[evdev_mtstmap[i][0]].resolution);
194}
195
196static int32_t
197evdev_count_fingers(struct evdev_dev *evdev)
198{
199	int32_t nfingers = 0, i;
200
201	for (i = 0; i <= MAXIMAL_MT_SLOT(evdev); i++)
202		if (evdev_get_mt_value(evdev, i, ABS_MT_TRACKING_ID) != -1)
203			nfingers++;
204
205	return (nfingers);
206}
207
208static void
209evdev_send_nfingers(struct evdev_dev *evdev, int32_t nfingers)
210{
211	int32_t i;
212
213	EVDEV_LOCK_ASSERT(evdev);
214
215	if (nfingers > nitems(evdev_fngmap))
216		nfingers = nitems(evdev_fngmap);
217
218	for (i = 0; i < nitems(evdev_fngmap); i++)
219		evdev_send_event(evdev, EV_KEY, evdev_fngmap[i],
220		    nfingers == i + 1);
221}
222
223void
224evdev_push_nfingers(struct evdev_dev *evdev, int32_t nfingers)
225{
226
227	EVDEV_ENTER(evdev);
228	evdev_send_nfingers(evdev, nfingers);
229	EVDEV_EXIT(evdev);
230}
231
232void
233evdev_send_mt_compat(struct evdev_dev *evdev)
234{
235	int32_t nfingers, i;
236
237	EVDEV_LOCK_ASSERT(evdev);
238
239	nfingers = evdev_count_fingers(evdev);
240	evdev_send_event(evdev, EV_KEY, BTN_TOUCH, nfingers > 0);
241
242	if (evdev_get_mt_value(evdev, 0, ABS_MT_TRACKING_ID) != -1)
243		/* Echo 0-th MT-slot as ST-slot */
244		for (i = 0; i < nitems(evdev_mtstmap); i++)
245			if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][1]))
246				evdev_send_event(evdev, EV_ABS,
247				    evdev_mtstmap[i][1],
248				    evdev_get_mt_value(evdev, 0,
249				    evdev_mtstmap[i][0]));
250
251	/* Touchscreens should not report tool taps */
252	if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
253		evdev_send_nfingers(evdev, nfingers);
254
255	if (nfingers == 0)
256		evdev_send_event(evdev, EV_ABS, ABS_PRESSURE, 0);
257}
258
259void
260evdev_push_mt_compat(struct evdev_dev *evdev)
261{
262
263	EVDEV_ENTER(evdev);
264	evdev_send_mt_compat(evdev);
265	EVDEV_EXIT(evdev);
266}
267
268void
269evdev_send_mt_autorel(struct evdev_dev *evdev)
270{
271	int32_t slot;
272
273	EVDEV_LOCK_ASSERT(evdev);
274
275	for (slot = 0; slot <= MAXIMAL_MT_SLOT(evdev); slot++) {
276		if (evdev->ev_mt->ev_mt_slots[slot].ev_report !=
277		    evdev->ev_report_count &&
278		    evdev_get_mt_value(evdev, slot, ABS_MT_TRACKING_ID) != -1){
279			evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot);
280			evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID,
281			    -1);
282		}
283	}
284}
285