xref: /freebsd/usr.sbin/bluetooth/btpand/event.c (revision 2a63c3be158216222d89a073dcbd6a72ee4aab5a)
17718ced0SMaksim Yevmenkin /*
27718ced0SMaksim Yevmenkin  * event.h
37718ced0SMaksim Yevmenkin  */
47718ced0SMaksim Yevmenkin 
57718ced0SMaksim Yevmenkin /*-
6*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
71de7b4b8SPedro F. Giffuni  *
87718ced0SMaksim Yevmenkin  * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
97718ced0SMaksim Yevmenkin  * All rights reserved.
107718ced0SMaksim Yevmenkin  *
117718ced0SMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
127718ced0SMaksim Yevmenkin  * modification, are permitted provided that the following conditions
137718ced0SMaksim Yevmenkin  * are met:
147718ced0SMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
157718ced0SMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
167718ced0SMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
177718ced0SMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
187718ced0SMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
197718ced0SMaksim Yevmenkin  *
207718ced0SMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
217718ced0SMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
227718ced0SMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
237718ced0SMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
247718ced0SMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
257718ced0SMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
267718ced0SMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
277718ced0SMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
287718ced0SMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
297718ced0SMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
307718ced0SMaksim Yevmenkin  * SUCH DAMAGE.
317718ced0SMaksim Yevmenkin  */
327718ced0SMaksim Yevmenkin 
337718ced0SMaksim Yevmenkin 
347718ced0SMaksim Yevmenkin /*
357718ced0SMaksim Yevmenkin  * Hack to provide libevent (see devel/libevent port) like API.
367718ced0SMaksim Yevmenkin  * Should be removed if FreeBSD ever decides to import libevent into base.
377718ced0SMaksim Yevmenkin  */
387718ced0SMaksim Yevmenkin 
397718ced0SMaksim Yevmenkin #include <sys/select.h>
407718ced0SMaksim Yevmenkin #include <sys/time.h>
417718ced0SMaksim Yevmenkin #include <sys/queue.h>
427718ced0SMaksim Yevmenkin #include <assert.h>
437718ced0SMaksim Yevmenkin #include <stdarg.h>
447718ced0SMaksim Yevmenkin #include <stdio.h>
457718ced0SMaksim Yevmenkin #include <string.h>
467718ced0SMaksim Yevmenkin #include <syslog.h>
477718ced0SMaksim Yevmenkin 
487718ced0SMaksim Yevmenkin #include "event.h"
498d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
507718ced0SMaksim Yevmenkin #include "btpand.h"
517718ced0SMaksim Yevmenkin 
527718ced0SMaksim Yevmenkin #define		__event_link(ev) \
537718ced0SMaksim Yevmenkin do { \
547718ced0SMaksim Yevmenkin 		TAILQ_INSERT_TAIL(&pending, ev, next); \
557718ced0SMaksim Yevmenkin 		ev->flags |= EV_PENDING; \
567718ced0SMaksim Yevmenkin } while (0)
577718ced0SMaksim Yevmenkin 
587718ced0SMaksim Yevmenkin static void	tv_add(struct timeval *, struct timeval const *);
597718ced0SMaksim Yevmenkin static void	tv_sub(struct timeval *, struct timeval const *);
607718ced0SMaksim Yevmenkin static int	tv_cmp(struct timeval const *, struct timeval const *);
617718ced0SMaksim Yevmenkin static int	__event_dispatch(void);
627718ced0SMaksim Yevmenkin static void	__event_add_current(struct event *);
637718ced0SMaksim Yevmenkin static void	__event_del_current(struct event *);
647718ced0SMaksim Yevmenkin 
657718ced0SMaksim Yevmenkin 
667718ced0SMaksim Yevmenkin static TAILQ_HEAD(, event)	pending;
677718ced0SMaksim Yevmenkin static TAILQ_HEAD(, event)	current;
687718ced0SMaksim Yevmenkin 
697718ced0SMaksim Yevmenkin void
event_init(void)707718ced0SMaksim Yevmenkin event_init(void)
717718ced0SMaksim Yevmenkin {
727718ced0SMaksim Yevmenkin 	TAILQ_INIT(&pending);
737718ced0SMaksim Yevmenkin }
747718ced0SMaksim Yevmenkin 
757718ced0SMaksim Yevmenkin int
event_dispatch(void)767718ced0SMaksim Yevmenkin event_dispatch(void)
777718ced0SMaksim Yevmenkin {
787718ced0SMaksim Yevmenkin 	while (__event_dispatch() == 0)
797718ced0SMaksim Yevmenkin 		;
807718ced0SMaksim Yevmenkin 
817718ced0SMaksim Yevmenkin 	return (-1);
827718ced0SMaksim Yevmenkin }
837718ced0SMaksim Yevmenkin 
847718ced0SMaksim Yevmenkin static int
__event_dispatch(void)857718ced0SMaksim Yevmenkin __event_dispatch(void)
867718ced0SMaksim Yevmenkin {
877718ced0SMaksim Yevmenkin 	fd_set			r, w;
887718ced0SMaksim Yevmenkin 	int			nfd;
897718ced0SMaksim Yevmenkin 	struct event		*ev;
907718ced0SMaksim Yevmenkin 	struct timeval		now, timeout, t;
917718ced0SMaksim Yevmenkin 
927718ced0SMaksim Yevmenkin 	FD_ZERO(&r);
937718ced0SMaksim Yevmenkin 	FD_ZERO(&w);
947718ced0SMaksim Yevmenkin 
957718ced0SMaksim Yevmenkin 	nfd = 0;
967718ced0SMaksim Yevmenkin 
977718ced0SMaksim Yevmenkin 	gettimeofday(&now, NULL);
987718ced0SMaksim Yevmenkin 
997718ced0SMaksim Yevmenkin 	timeout.tv_sec = 10;	/* arbitrary */
1007718ced0SMaksim Yevmenkin 	timeout.tv_usec = 0;
1017718ced0SMaksim Yevmenkin 
1027718ced0SMaksim Yevmenkin 	TAILQ_INIT(&current);
1037718ced0SMaksim Yevmenkin 
1047718ced0SMaksim Yevmenkin 	/*
1057718ced0SMaksim Yevmenkin 	 * Build fd_set's
1067718ced0SMaksim Yevmenkin 	 */
1077718ced0SMaksim Yevmenkin 
1087718ced0SMaksim Yevmenkin 	event_log_debug("%s: building fd set...", __func__);
1097718ced0SMaksim Yevmenkin 
1107718ced0SMaksim Yevmenkin 	while (!TAILQ_EMPTY(&pending)) {
1117718ced0SMaksim Yevmenkin 		ev = TAILQ_FIRST(&pending);
1127718ced0SMaksim Yevmenkin 		event_del(ev);
1137718ced0SMaksim Yevmenkin 
1147718ced0SMaksim Yevmenkin 		if (ev->flags & EV_HAS_TIMEOUT) {
115dba59455SMaksim Yevmenkin 			if (tv_cmp(&now, &ev->expire) >= 0)
1167718ced0SMaksim Yevmenkin 				t.tv_sec = t.tv_usec = 0;
117dba59455SMaksim Yevmenkin 			else {
118dba59455SMaksim Yevmenkin 				t = ev->expire;
119dba59455SMaksim Yevmenkin 				tv_sub(&t, &now);
120dba59455SMaksim Yevmenkin 			}
1217718ced0SMaksim Yevmenkin 
1227718ced0SMaksim Yevmenkin 			if (tv_cmp(&t, &timeout) < 0)
1237718ced0SMaksim Yevmenkin 				timeout = t;
1247718ced0SMaksim Yevmenkin 		}
1257718ced0SMaksim Yevmenkin 
1267718ced0SMaksim Yevmenkin 		if (ev->fd >= 0) {
1277718ced0SMaksim Yevmenkin 			if (ev->flags & EV_READ) {
1287718ced0SMaksim Yevmenkin 				FD_SET(ev->fd, &r);
1297718ced0SMaksim Yevmenkin 				nfd = (nfd > ev->fd) ? nfd : ev->fd;
1307718ced0SMaksim Yevmenkin 			}
1317718ced0SMaksim Yevmenkin 
1327718ced0SMaksim Yevmenkin 			if (ev->flags & EV_WRITE) {
1337718ced0SMaksim Yevmenkin 				FD_SET(ev->fd, &w);
1347718ced0SMaksim Yevmenkin 				nfd = (nfd > ev->fd) ? nfd : ev->fd;
1357718ced0SMaksim Yevmenkin 			}
1367718ced0SMaksim Yevmenkin 		}
1377718ced0SMaksim Yevmenkin 
1387718ced0SMaksim Yevmenkin 		__event_add_current(ev);
1397718ced0SMaksim Yevmenkin 	}
1407718ced0SMaksim Yevmenkin 
1417718ced0SMaksim Yevmenkin 	event_log_debug("%s: waiting for events...", __func__);
1427718ced0SMaksim Yevmenkin 
1437718ced0SMaksim Yevmenkin 	nfd = select(nfd + 1, &r, &w, NULL, &timeout);
1447718ced0SMaksim Yevmenkin 	if (nfd < 0)
1457718ced0SMaksim Yevmenkin 		return (-1);
1467718ced0SMaksim Yevmenkin 
1477718ced0SMaksim Yevmenkin 	/*
1487718ced0SMaksim Yevmenkin 	 * Process current pending
1497718ced0SMaksim Yevmenkin 	 */
1507718ced0SMaksim Yevmenkin 
1517718ced0SMaksim Yevmenkin 	event_log_debug("%s: processing events...", __func__);
1527718ced0SMaksim Yevmenkin 
1537718ced0SMaksim Yevmenkin 	gettimeofday(&now, NULL);
1547718ced0SMaksim Yevmenkin 
1557718ced0SMaksim Yevmenkin 	while (!TAILQ_EMPTY(&current)) {
1567718ced0SMaksim Yevmenkin 		ev = TAILQ_FIRST(&current);
1577718ced0SMaksim Yevmenkin 		__event_del_current(ev);
1587718ced0SMaksim Yevmenkin 
1597718ced0SMaksim Yevmenkin 		/* check if fd is ready for reading/writing */
1607718ced0SMaksim Yevmenkin 		if (nfd > 0 && ev->fd >= 0) {
1617718ced0SMaksim Yevmenkin 			if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) {
1627718ced0SMaksim Yevmenkin 				if (ev->flags & EV_PERSIST) {
1637718ced0SMaksim Yevmenkin 					if (ev->flags & EV_HAS_TIMEOUT)
1647718ced0SMaksim Yevmenkin 						event_add(ev, &ev->timeout);
1657718ced0SMaksim Yevmenkin 					else
1667718ced0SMaksim Yevmenkin 						event_add(ev, NULL);
1677718ced0SMaksim Yevmenkin 				}
1687718ced0SMaksim Yevmenkin 
1697718ced0SMaksim Yevmenkin 				nfd --;
1707718ced0SMaksim Yevmenkin 
1717718ced0SMaksim Yevmenkin 				event_log_debug("%s: calling %p(%d, %p), " \
1727718ced0SMaksim Yevmenkin 					"ev=%p", __func__, ev->cb, ev->fd,
1737718ced0SMaksim Yevmenkin 					ev->cbarg, ev);
1747718ced0SMaksim Yevmenkin 
1757718ced0SMaksim Yevmenkin 				(ev->cb)(ev->fd,
1767718ced0SMaksim Yevmenkin 					(ev->flags & (EV_READ|EV_WRITE)),
1777718ced0SMaksim Yevmenkin 					ev->cbarg);
1787718ced0SMaksim Yevmenkin 
1797718ced0SMaksim Yevmenkin 				continue;
1807718ced0SMaksim Yevmenkin 			}
1817718ced0SMaksim Yevmenkin 		}
1827718ced0SMaksim Yevmenkin 
1837718ced0SMaksim Yevmenkin 		/* if event has no timeout - just requeue */
1847718ced0SMaksim Yevmenkin 		if ((ev->flags & EV_HAS_TIMEOUT) == 0) {
1857718ced0SMaksim Yevmenkin 			event_add(ev, NULL);
1867718ced0SMaksim Yevmenkin 			continue;
1877718ced0SMaksim Yevmenkin 		}
1887718ced0SMaksim Yevmenkin 
1897718ced0SMaksim Yevmenkin 		/* check if event has expired */
1907718ced0SMaksim Yevmenkin 		if (tv_cmp(&now, &ev->expire) >= 0) {
1917718ced0SMaksim Yevmenkin 			if (ev->flags & EV_PERSIST)
1927718ced0SMaksim Yevmenkin 				event_add(ev, &ev->timeout);
1937718ced0SMaksim Yevmenkin 
1947718ced0SMaksim Yevmenkin 			event_log_debug("%s: calling %p(%d, %p), ev=%p",
1957718ced0SMaksim Yevmenkin 				__func__, ev->cb, ev->fd, ev->cbarg, ev);
1967718ced0SMaksim Yevmenkin 
1977718ced0SMaksim Yevmenkin 			(ev->cb)(ev->fd,
1987718ced0SMaksim Yevmenkin 				(ev->flags & (EV_READ|EV_WRITE)),
1997718ced0SMaksim Yevmenkin 				ev->cbarg);
2007718ced0SMaksim Yevmenkin 
2017718ced0SMaksim Yevmenkin 			continue;
2027718ced0SMaksim Yevmenkin 		}
2037718ced0SMaksim Yevmenkin 
2047718ced0SMaksim Yevmenkin 		assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
2057718ced0SMaksim Yevmenkin 		__event_link(ev);
2067718ced0SMaksim Yevmenkin 	}
2077718ced0SMaksim Yevmenkin 
2087718ced0SMaksim Yevmenkin 	return (0);
2097718ced0SMaksim Yevmenkin }
2107718ced0SMaksim Yevmenkin 
2117718ced0SMaksim Yevmenkin void
__event_set(struct event * ev,int fd,short flags,void (* cb)(int,short,void *),void * cbarg)2127718ced0SMaksim Yevmenkin __event_set(struct event *ev, int fd, short flags,
2137718ced0SMaksim Yevmenkin 	void (*cb)(int, short, void *), void *cbarg)
2147718ced0SMaksim Yevmenkin {
2157718ced0SMaksim Yevmenkin 	ev->fd = fd;
2167718ced0SMaksim Yevmenkin 	ev->flags = flags;
2177718ced0SMaksim Yevmenkin 	ev->cb = cb;
2187718ced0SMaksim Yevmenkin 	ev->cbarg = cbarg;
2197718ced0SMaksim Yevmenkin }
2207718ced0SMaksim Yevmenkin 
2217718ced0SMaksim Yevmenkin int
__event_add(struct event * ev,const struct timeval * timeout)2227718ced0SMaksim Yevmenkin __event_add(struct event *ev, const struct timeval *timeout)
2237718ced0SMaksim Yevmenkin {
2247718ced0SMaksim Yevmenkin 	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
2257718ced0SMaksim Yevmenkin 
2267718ced0SMaksim Yevmenkin 	if (timeout != NULL) {
2277718ced0SMaksim Yevmenkin 		gettimeofday(&ev->expire, NULL);
2287718ced0SMaksim Yevmenkin 		tv_add(&ev->expire, timeout);
2297718ced0SMaksim Yevmenkin 		ev->timeout = *timeout;
2307718ced0SMaksim Yevmenkin 		ev->flags |= EV_HAS_TIMEOUT;
2317718ced0SMaksim Yevmenkin 	} else
2327718ced0SMaksim Yevmenkin 		ev->flags &= ~EV_HAS_TIMEOUT;
2337718ced0SMaksim Yevmenkin 
2347718ced0SMaksim Yevmenkin 	__event_link(ev);
2357718ced0SMaksim Yevmenkin 
2367718ced0SMaksim Yevmenkin 	return (0);
2377718ced0SMaksim Yevmenkin }
2387718ced0SMaksim Yevmenkin 
2397718ced0SMaksim Yevmenkin int
__event_del(struct event * ev)2407718ced0SMaksim Yevmenkin __event_del(struct event *ev)
2417718ced0SMaksim Yevmenkin {
2427718ced0SMaksim Yevmenkin 	assert((ev->flags & EV_CURRENT) == 0);
2437718ced0SMaksim Yevmenkin 
2447718ced0SMaksim Yevmenkin 	if ((ev->flags & EV_PENDING) != 0) {
2457718ced0SMaksim Yevmenkin 		TAILQ_REMOVE(&pending, ev, next);
2467718ced0SMaksim Yevmenkin 		ev->flags &= ~EV_PENDING;
2477718ced0SMaksim Yevmenkin 	}
2487718ced0SMaksim Yevmenkin 
2497718ced0SMaksim Yevmenkin 	return (0);
2507718ced0SMaksim Yevmenkin }
2517718ced0SMaksim Yevmenkin 
2527718ced0SMaksim Yevmenkin static void
__event_add_current(struct event * ev)2537718ced0SMaksim Yevmenkin __event_add_current(struct event *ev)
2547718ced0SMaksim Yevmenkin {
2557718ced0SMaksim Yevmenkin 	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
2567718ced0SMaksim Yevmenkin 
2577718ced0SMaksim Yevmenkin 	TAILQ_INSERT_TAIL(&current, ev, next);
2587718ced0SMaksim Yevmenkin 	ev->flags |= EV_CURRENT;
2597718ced0SMaksim Yevmenkin }
2607718ced0SMaksim Yevmenkin 
2617718ced0SMaksim Yevmenkin static void
__event_del_current(struct event * ev)2627718ced0SMaksim Yevmenkin __event_del_current(struct event *ev)
2637718ced0SMaksim Yevmenkin {
2647718ced0SMaksim Yevmenkin 	assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT);
2657718ced0SMaksim Yevmenkin 
2667718ced0SMaksim Yevmenkin 	TAILQ_REMOVE(&current, ev, next);
2677718ced0SMaksim Yevmenkin 	ev->flags &= ~EV_CURRENT;
2687718ced0SMaksim Yevmenkin }
2697718ced0SMaksim Yevmenkin 
2707718ced0SMaksim Yevmenkin static void
tv_add(struct timeval * a,struct timeval const * b)2717718ced0SMaksim Yevmenkin tv_add(struct timeval *a, struct timeval const *b)
2727718ced0SMaksim Yevmenkin {
2737718ced0SMaksim Yevmenkin 	a->tv_sec += b->tv_sec;
2747718ced0SMaksim Yevmenkin 	a->tv_usec += b->tv_usec;
2757718ced0SMaksim Yevmenkin 
2767718ced0SMaksim Yevmenkin 	if(a->tv_usec >= 1000000) {
2777718ced0SMaksim Yevmenkin 		a->tv_usec -= 1000000;
2787718ced0SMaksim Yevmenkin 		a->tv_sec += 1;
2797718ced0SMaksim Yevmenkin 	}
2807718ced0SMaksim Yevmenkin }
2817718ced0SMaksim Yevmenkin 
2827718ced0SMaksim Yevmenkin static void
tv_sub(struct timeval * a,struct timeval const * b)2837718ced0SMaksim Yevmenkin tv_sub(struct timeval *a, struct timeval const *b)
2847718ced0SMaksim Yevmenkin {
2857718ced0SMaksim Yevmenkin 	if (a->tv_usec < b->tv_usec) {
2867718ced0SMaksim Yevmenkin 		a->tv_usec += 1000000;
2877718ced0SMaksim Yevmenkin 		a->tv_sec -= 1;
2887718ced0SMaksim Yevmenkin 	}
2897718ced0SMaksim Yevmenkin 
2907718ced0SMaksim Yevmenkin 	a->tv_usec -= b->tv_usec;
2917718ced0SMaksim Yevmenkin 	a->tv_sec -= b->tv_sec;
2927718ced0SMaksim Yevmenkin }
2937718ced0SMaksim Yevmenkin 
2947718ced0SMaksim Yevmenkin static int
tv_cmp(struct timeval const * a,struct timeval const * b)2957718ced0SMaksim Yevmenkin tv_cmp(struct timeval const *a, struct timeval const *b)
2967718ced0SMaksim Yevmenkin {
2977718ced0SMaksim Yevmenkin  	if (a->tv_sec > b->tv_sec)
2987718ced0SMaksim Yevmenkin 		return (1);
2997718ced0SMaksim Yevmenkin 
3007718ced0SMaksim Yevmenkin 	if (a->tv_sec < b->tv_sec)
3017718ced0SMaksim Yevmenkin 		return (-1);
3027718ced0SMaksim Yevmenkin 
3037718ced0SMaksim Yevmenkin 	if (a->tv_usec > b->tv_usec)
3047718ced0SMaksim Yevmenkin 		return (1);
3057718ced0SMaksim Yevmenkin 
3067718ced0SMaksim Yevmenkin 	if (a->tv_usec < b->tv_usec)
3077718ced0SMaksim Yevmenkin 		return (-1);
3087718ced0SMaksim Yevmenkin 
3097718ced0SMaksim Yevmenkin 	return (0);
3107718ced0SMaksim Yevmenkin }
3117718ced0SMaksim Yevmenkin 
312