xref: /freebsd/sys/dev/evdev/evdev_mt.c (revision fbe17f9017e785dd564ce7fc5553a9136d3a0b03)
12b3f6d66SOleksandr Tymoshenko /*-
2e6502802SVladimir Kondratyev  * Copyright (c) 2016 Vladimir Kondratyev <wulf@FreeBSD.org>
32b3f6d66SOleksandr Tymoshenko  * All rights reserved.
42b3f6d66SOleksandr Tymoshenko  *
52b3f6d66SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
62b3f6d66SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
72b3f6d66SOleksandr Tymoshenko  * are met:
82b3f6d66SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
92b3f6d66SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
102b3f6d66SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
112b3f6d66SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
122b3f6d66SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
132b3f6d66SOleksandr Tymoshenko  *
142b3f6d66SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152b3f6d66SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162b3f6d66SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172b3f6d66SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182b3f6d66SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192b3f6d66SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202b3f6d66SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212b3f6d66SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222b3f6d66SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232b3f6d66SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242b3f6d66SOleksandr Tymoshenko  * SUCH DAMAGE.
252b3f6d66SOleksandr Tymoshenko  *
262b3f6d66SOleksandr Tymoshenko  * $FreeBSD$
272b3f6d66SOleksandr Tymoshenko  */
282b3f6d66SOleksandr Tymoshenko 
292b3f6d66SOleksandr Tymoshenko #include <sys/param.h>
302b3f6d66SOleksandr Tymoshenko #include <sys/lock.h>
31ea2e26b1SVladimir Kondratyev #include <sys/malloc.h>
322b3f6d66SOleksandr Tymoshenko #include <sys/mutex.h>
332b3f6d66SOleksandr Tymoshenko #include <sys/systm.h>
342b3f6d66SOleksandr Tymoshenko 
352b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev.h>
362b3f6d66SOleksandr Tymoshenko #include <dev/evdev/evdev_private.h>
37ea2e26b1SVladimir Kondratyev #include <dev/evdev/input.h>
382b3f6d66SOleksandr Tymoshenko 
392b3f6d66SOleksandr Tymoshenko #ifdef DEBUG
402b3f6d66SOleksandr Tymoshenko #define	debugf(fmt, args...)	printf("evdev: " fmt "\n", ##args)
412b3f6d66SOleksandr Tymoshenko #else
422b3f6d66SOleksandr Tymoshenko #define	debugf(fmt, args...)
432b3f6d66SOleksandr Tymoshenko #endif
442b3f6d66SOleksandr Tymoshenko 
452dc7188eSVladimir Kondratyev typedef	u_int	slotset_t;
462dc7188eSVladimir Kondratyev 
472dc7188eSVladimir Kondratyev _Static_assert(MAX_MT_SLOTS < sizeof(slotset_t) * 8, "MAX_MT_SLOTS too big");
482dc7188eSVladimir Kondratyev 
492dc7188eSVladimir Kondratyev #define FOREACHBIT(v, i) \
502dc7188eSVladimir Kondratyev 	for ((i) = ffs(v) - 1; (i) != -1; (i) = ffs((v) & (~1 << (i))) - 1)
512dc7188eSVladimir Kondratyev 
522b3f6d66SOleksandr Tymoshenko static uint16_t evdev_mtstmap[][2] = {
532b3f6d66SOleksandr Tymoshenko 	{ ABS_MT_POSITION_X, ABS_X },
542b3f6d66SOleksandr Tymoshenko 	{ ABS_MT_POSITION_Y, ABS_Y },
552b3f6d66SOleksandr Tymoshenko 	{ ABS_MT_PRESSURE, ABS_PRESSURE },
562b3f6d66SOleksandr Tymoshenko 	{ ABS_MT_TOUCH_MAJOR, ABS_TOOL_WIDTH },
572b3f6d66SOleksandr Tymoshenko };
582b3f6d66SOleksandr Tymoshenko 
592b3f6d66SOleksandr Tymoshenko struct evdev_mt_slot {
6098a7606bSVladimir Kondratyev 	int32_t		val[MT_CNT];
612b3f6d66SOleksandr Tymoshenko };
622b3f6d66SOleksandr Tymoshenko 
632b3f6d66SOleksandr Tymoshenko struct evdev_mt {
6498a7606bSVladimir Kondratyev 	int			last_reported_slot;
652dc7188eSVladimir Kondratyev 	/* the set of slots with active touches */
662dc7188eSVladimir Kondratyev 	slotset_t		touches;
672dc7188eSVladimir Kondratyev 	/* the set of slots with unsynchronized state */
682dc7188eSVladimir Kondratyev 	slotset_t		frame;
6998a7606bSVladimir Kondratyev 	struct evdev_mt_slot	slots[];
702b3f6d66SOleksandr Tymoshenko };
712b3f6d66SOleksandr Tymoshenko 
7298a7606bSVladimir Kondratyev static void	evdev_mt_send_st_compat(struct evdev_dev *);
7398a7606bSVladimir Kondratyev static void	evdev_mt_send_autorel(struct evdev_dev *);
7498a7606bSVladimir Kondratyev 
752dc7188eSVladimir Kondratyev static inline int
762dc7188eSVladimir Kondratyev ffc_slot(struct evdev_dev *evdev, slotset_t slots)
772dc7188eSVladimir Kondratyev {
782dc7188eSVladimir Kondratyev 	return (ffs(~slots & (2U << MAXIMAL_MT_SLOT(evdev)) - 1) - 1);
792dc7188eSVladimir Kondratyev }
802dc7188eSVladimir Kondratyev 
812b3f6d66SOleksandr Tymoshenko void
822b3f6d66SOleksandr Tymoshenko evdev_mt_init(struct evdev_dev *evdev)
832b3f6d66SOleksandr Tymoshenko {
8498a7606bSVladimir Kondratyev 	int slot, slots;
852b3f6d66SOleksandr Tymoshenko 
862b3f6d66SOleksandr Tymoshenko 	slots = MAXIMAL_MT_SLOT(evdev) + 1;
872b3f6d66SOleksandr Tymoshenko 
8898a7606bSVladimir Kondratyev 	evdev->ev_mt = malloc(offsetof(struct evdev_mt, slots) +
892b3f6d66SOleksandr Tymoshenko 	     sizeof(struct evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO);
902b3f6d66SOleksandr Tymoshenko 
912b3f6d66SOleksandr Tymoshenko 	/* Initialize multitouch protocol type B states */
922dc7188eSVladimir Kondratyev 	for (slot = 0; slot < slots; slot++)
932dc7188eSVladimir Kondratyev 		evdev->ev_mt->slots[slot].val[ABS_MT_INDEX(ABS_MT_TRACKING_ID)]
942dc7188eSVladimir Kondratyev 		    = -1;
952b3f6d66SOleksandr Tymoshenko 
962b3f6d66SOleksandr Tymoshenko 	if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT))
972b3f6d66SOleksandr Tymoshenko 		evdev_support_mt_compat(evdev);
982b3f6d66SOleksandr Tymoshenko }
992b3f6d66SOleksandr Tymoshenko 
1002b3f6d66SOleksandr Tymoshenko void
1012b3f6d66SOleksandr Tymoshenko evdev_mt_free(struct evdev_dev *evdev)
1022b3f6d66SOleksandr Tymoshenko {
1032b3f6d66SOleksandr Tymoshenko 	free(evdev->ev_mt, M_EVDEV);
1042b3f6d66SOleksandr Tymoshenko }
1052b3f6d66SOleksandr Tymoshenko 
10698a7606bSVladimir Kondratyev void
10798a7606bSVladimir Kondratyev evdev_mt_sync_frame(struct evdev_dev *evdev)
1082b3f6d66SOleksandr Tymoshenko {
10998a7606bSVladimir Kondratyev 	if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL))
11098a7606bSVladimir Kondratyev 		evdev_mt_send_autorel(evdev);
11198a7606bSVladimir Kondratyev 	if (evdev->ev_report_opened &&
11298a7606bSVladimir Kondratyev 	    bit_test(evdev->ev_flags, EVDEV_FLAG_MT_STCOMPAT))
11398a7606bSVladimir Kondratyev 		evdev_mt_send_st_compat(evdev);
1142dc7188eSVladimir Kondratyev 	evdev->ev_mt->frame = 0;
11598a7606bSVladimir Kondratyev }
1162b3f6d66SOleksandr Tymoshenko 
11798a7606bSVladimir Kondratyev int
11898a7606bSVladimir Kondratyev evdev_mt_get_last_slot(struct evdev_dev *evdev)
11998a7606bSVladimir Kondratyev {
12098a7606bSVladimir Kondratyev 	return (evdev->ev_mt->last_reported_slot);
1212b3f6d66SOleksandr Tymoshenko }
1222b3f6d66SOleksandr Tymoshenko 
1232b3f6d66SOleksandr Tymoshenko void
12498a7606bSVladimir Kondratyev evdev_mt_set_last_slot(struct evdev_dev *evdev, int slot)
1252b3f6d66SOleksandr Tymoshenko {
12698a7606bSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
1272b3f6d66SOleksandr Tymoshenko 
12898a7606bSVladimir Kondratyev 	MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev));
1292b3f6d66SOleksandr Tymoshenko 
1302dc7188eSVladimir Kondratyev 	mt->frame |= 1U << slot;
13198a7606bSVladimir Kondratyev 	mt->last_reported_slot = slot;
1322b3f6d66SOleksandr Tymoshenko }
1332b3f6d66SOleksandr Tymoshenko 
1342b3f6d66SOleksandr Tymoshenko int32_t
13598a7606bSVladimir Kondratyev evdev_mt_get_value(struct evdev_dev *evdev, int slot, int16_t code)
13698a7606bSVladimir Kondratyev {
13798a7606bSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
13898a7606bSVladimir Kondratyev 
13998a7606bSVladimir Kondratyev 	MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev));
14098a7606bSVladimir Kondratyev 
14198a7606bSVladimir Kondratyev 	return (mt->slots[slot].val[ABS_MT_INDEX(code)]);
14298a7606bSVladimir Kondratyev }
14398a7606bSVladimir Kondratyev 
14498a7606bSVladimir Kondratyev void
14598a7606bSVladimir Kondratyev evdev_mt_set_value(struct evdev_dev *evdev, int slot, int16_t code,
14698a7606bSVladimir Kondratyev     int32_t value)
14798a7606bSVladimir Kondratyev {
14898a7606bSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
14998a7606bSVladimir Kondratyev 
15098a7606bSVladimir Kondratyev 	MPASS(slot >= 0 && slot <= MAXIMAL_MT_SLOT(evdev));
15198a7606bSVladimir Kondratyev 
1522dc7188eSVladimir Kondratyev 	if (code == ABS_MT_TRACKING_ID) {
1532dc7188eSVladimir Kondratyev 		if (value != -1)
1542dc7188eSVladimir Kondratyev 			mt->touches |= 1U << slot;
1552dc7188eSVladimir Kondratyev 		else
1562dc7188eSVladimir Kondratyev 			mt->touches &= ~(1U << slot);
1572dc7188eSVladimir Kondratyev 	}
15898a7606bSVladimir Kondratyev 	mt->slots[slot].val[ABS_MT_INDEX(code)] = value;
15998a7606bSVladimir Kondratyev }
16098a7606bSVladimir Kondratyev 
16198a7606bSVladimir Kondratyev int
1622b3f6d66SOleksandr Tymoshenko evdev_get_mt_slot_by_tracking_id(struct evdev_dev *evdev, int32_t tracking_id)
1632b3f6d66SOleksandr Tymoshenko {
16498a7606bSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
1652dc7188eSVladimir Kondratyev 	int slot;
1662b3f6d66SOleksandr Tymoshenko 
1672dc7188eSVladimir Kondratyev 	FOREACHBIT(mt->touches, slot)
1682dc7188eSVladimir Kondratyev 		if (evdev_mt_get_value(evdev, slot, ABS_MT_TRACKING_ID) ==
1692dc7188eSVladimir Kondratyev 		    tracking_id)
1702b3f6d66SOleksandr Tymoshenko 			return (slot);
1712b3f6d66SOleksandr Tymoshenko 	/*
1722dc7188eSVladimir Kondratyev 	 * Do not allow allocation of new slot in a place of just
1732dc7188eSVladimir Kondratyev 	 * released one within the same report.
1742b3f6d66SOleksandr Tymoshenko 	 */
1752dc7188eSVladimir Kondratyev 	return (ffc_slot(evdev, mt->touches | mt->frame));
1762b3f6d66SOleksandr Tymoshenko }
1772b3f6d66SOleksandr Tymoshenko 
1782b3f6d66SOleksandr Tymoshenko void
1792b3f6d66SOleksandr Tymoshenko evdev_support_mt_compat(struct evdev_dev *evdev)
1802b3f6d66SOleksandr Tymoshenko {
18198a7606bSVladimir Kondratyev 	int i;
1822b3f6d66SOleksandr Tymoshenko 
1832b3f6d66SOleksandr Tymoshenko 	if (evdev->ev_absinfo == NULL)
1842b3f6d66SOleksandr Tymoshenko 		return;
1852b3f6d66SOleksandr Tymoshenko 
1862b3f6d66SOleksandr Tymoshenko 	evdev_support_event(evdev, EV_KEY);
1872b3f6d66SOleksandr Tymoshenko 	evdev_support_key(evdev, BTN_TOUCH);
1882b3f6d66SOleksandr Tymoshenko 
1892b3f6d66SOleksandr Tymoshenko 	/* Touchscreens should not advertise tap tool capabilities */
1902b3f6d66SOleksandr Tymoshenko 	if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
1912b3f6d66SOleksandr Tymoshenko 		evdev_support_nfingers(evdev, MAXIMAL_MT_SLOT(evdev) + 1);
1922b3f6d66SOleksandr Tymoshenko 
1932b3f6d66SOleksandr Tymoshenko 	/* Echo 0-th MT-slot as ST-slot */
1942b3f6d66SOleksandr Tymoshenko 	for (i = 0; i < nitems(evdev_mtstmap); i++)
1952b3f6d66SOleksandr Tymoshenko 		if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][0]))
1962b3f6d66SOleksandr Tymoshenko 			evdev_support_abs(evdev, evdev_mtstmap[i][1],
1972b3f6d66SOleksandr Tymoshenko 			    evdev->ev_absinfo[evdev_mtstmap[i][0]].minimum,
1982b3f6d66SOleksandr Tymoshenko 			    evdev->ev_absinfo[evdev_mtstmap[i][0]].maximum,
1992b3f6d66SOleksandr Tymoshenko 			    evdev->ev_absinfo[evdev_mtstmap[i][0]].fuzz,
2002b3f6d66SOleksandr Tymoshenko 			    evdev->ev_absinfo[evdev_mtstmap[i][0]].flat,
2012b3f6d66SOleksandr Tymoshenko 			    evdev->ev_absinfo[evdev_mtstmap[i][0]].resolution);
2022b3f6d66SOleksandr Tymoshenko }
2032b3f6d66SOleksandr Tymoshenko 
2042b3f6d66SOleksandr Tymoshenko static void
20598a7606bSVladimir Kondratyev evdev_mt_send_st_compat(struct evdev_dev *evdev)
2062b3f6d66SOleksandr Tymoshenko {
2072dc7188eSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
208*fbe17f90SVladimir Kondratyev 	int nfingers, i, st_slot;
2092b3f6d66SOleksandr Tymoshenko 
2102b3f6d66SOleksandr Tymoshenko 	EVDEV_LOCK_ASSERT(evdev);
2112b3f6d66SOleksandr Tymoshenko 
2122dc7188eSVladimir Kondratyev 	nfingers = bitcount(mt->touches);
2132b3f6d66SOleksandr Tymoshenko 	evdev_send_event(evdev, EV_KEY, BTN_TOUCH, nfingers > 0);
2142b3f6d66SOleksandr Tymoshenko 
215*fbe17f90SVladimir Kondratyev 	/* Send first active MT-slot state as single touch report */
216*fbe17f90SVladimir Kondratyev 	st_slot = ffs(mt->touches) - 1;
217*fbe17f90SVladimir Kondratyev 	if (st_slot != -1)
2182b3f6d66SOleksandr Tymoshenko 		for (i = 0; i < nitems(evdev_mtstmap); i++)
2192b3f6d66SOleksandr Tymoshenko 			if (bit_test(evdev->ev_abs_flags, evdev_mtstmap[i][1]))
2202b3f6d66SOleksandr Tymoshenko 				evdev_send_event(evdev, EV_ABS,
2212b3f6d66SOleksandr Tymoshenko 				    evdev_mtstmap[i][1],
222*fbe17f90SVladimir Kondratyev 				    evdev_mt_get_value(evdev, st_slot,
2232b3f6d66SOleksandr Tymoshenko 				    evdev_mtstmap[i][0]));
2242b3f6d66SOleksandr Tymoshenko 
2252b3f6d66SOleksandr Tymoshenko 	/* Touchscreens should not report tool taps */
2262b3f6d66SOleksandr Tymoshenko 	if (!bit_test(evdev->ev_prop_flags, INPUT_PROP_DIRECT))
2272b3f6d66SOleksandr Tymoshenko 		evdev_send_nfingers(evdev, nfingers);
2282b3f6d66SOleksandr Tymoshenko 
2292b3f6d66SOleksandr Tymoshenko 	if (nfingers == 0)
2302b3f6d66SOleksandr Tymoshenko 		evdev_send_event(evdev, EV_ABS, ABS_PRESSURE, 0);
2312b3f6d66SOleksandr Tymoshenko }
2322b3f6d66SOleksandr Tymoshenko 
2332b3f6d66SOleksandr Tymoshenko void
2342b3f6d66SOleksandr Tymoshenko evdev_push_mt_compat(struct evdev_dev *evdev)
2352b3f6d66SOleksandr Tymoshenko {
2362b3f6d66SOleksandr Tymoshenko 
2374c0a4665SVladimir Kondratyev 	EVDEV_ENTER(evdev);
23898a7606bSVladimir Kondratyev 	evdev_mt_send_st_compat(evdev);
2394c0a4665SVladimir Kondratyev 	EVDEV_EXIT(evdev);
2402b3f6d66SOleksandr Tymoshenko }
241c736a757SOleksandr Tymoshenko 
24298a7606bSVladimir Kondratyev static void
24398a7606bSVladimir Kondratyev evdev_mt_send_autorel(struct evdev_dev *evdev)
244c736a757SOleksandr Tymoshenko {
24598a7606bSVladimir Kondratyev 	struct evdev_mt *mt = evdev->ev_mt;
24698a7606bSVladimir Kondratyev 	int slot;
247c736a757SOleksandr Tymoshenko 
248c736a757SOleksandr Tymoshenko 	EVDEV_LOCK_ASSERT(evdev);
249c736a757SOleksandr Tymoshenko 
2502dc7188eSVladimir Kondratyev 	FOREACHBIT(mt->touches & ~mt->frame, slot) {
251c736a757SOleksandr Tymoshenko 		evdev_send_event(evdev, EV_ABS, ABS_MT_SLOT, slot);
2522dc7188eSVladimir Kondratyev 		evdev_send_event(evdev, EV_ABS, ABS_MT_TRACKING_ID, -1);
253c736a757SOleksandr Tymoshenko 	}
254c736a757SOleksandr Tymoshenko }
25598a7606bSVladimir Kondratyev 
25698a7606bSVladimir Kondratyev void
25798a7606bSVladimir Kondratyev evdev_mt_push_autorel(struct evdev_dev *evdev)
25898a7606bSVladimir Kondratyev {
25998a7606bSVladimir Kondratyev 	EVDEV_ENTER(evdev);
26098a7606bSVladimir Kondratyev 	evdev_mt_send_autorel(evdev);
26198a7606bSVladimir Kondratyev 	EVDEV_EXIT(evdev);
26298a7606bSVladimir Kondratyev }
263