1/*
2 * event.h
3 */
4
5/*-
6 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7 *
8 * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/* $FreeBSD: stable/11/usr.sbin/bluetooth/btpand/event.c 330449 2018-03-05 07:26:05Z eadler $ */
34
35/*
36 * Hack to provide libevent (see devel/libevent port) like API.
37 * Should be removed if FreeBSD ever decides to import libevent into base.
38 */
39
40#include <sys/select.h>
41#include <sys/time.h>
42#include <sys/queue.h>
43#include <assert.h>
44#include <stdarg.h>
45#include <stdio.h>
46#include <string.h>
47#include <syslog.h>
48
49#include "event.h"
50#define L2CAP_SOCKET_CHECKED
51#include "btpand.h"
52
53#define		__event_link(ev) \
54do { \
55		TAILQ_INSERT_TAIL(&pending, ev, next); \
56		ev->flags |= EV_PENDING; \
57} while (0)
58
59static void	tv_add(struct timeval *, struct timeval const *);
60static void	tv_sub(struct timeval *, struct timeval const *);
61static int	tv_cmp(struct timeval const *, struct timeval const *);
62static int	__event_dispatch(void);
63static void	__event_add_current(struct event *);
64static void	__event_del_current(struct event *);
65
66
67static TAILQ_HEAD(, event)	pending;
68static TAILQ_HEAD(, event)	current;
69
70void
71event_init(void)
72{
73	TAILQ_INIT(&pending);
74}
75
76int
77event_dispatch(void)
78{
79	while (__event_dispatch() == 0)
80		;
81
82	return (-1);
83}
84
85static int
86__event_dispatch(void)
87{
88	fd_set			r, w;
89	int			nfd;
90	struct event		*ev;
91	struct timeval		now, timeout, t;
92
93	FD_ZERO(&r);
94	FD_ZERO(&w);
95
96	nfd = 0;
97
98	gettimeofday(&now, NULL);
99
100	timeout.tv_sec = 10;	/* arbitrary */
101	timeout.tv_usec = 0;
102
103	TAILQ_INIT(&current);
104
105	/*
106	 * Build fd_set's
107	 */
108
109	event_log_debug("%s: building fd set...", __func__);
110
111	while (!TAILQ_EMPTY(&pending)) {
112		ev = TAILQ_FIRST(&pending);
113		event_del(ev);
114
115		if (ev->flags & EV_HAS_TIMEOUT) {
116			if (tv_cmp(&now, &ev->expire) >= 0)
117				t.tv_sec = t.tv_usec = 0;
118			else {
119				t = ev->expire;
120				tv_sub(&t, &now);
121			}
122
123			if (tv_cmp(&t, &timeout) < 0)
124				timeout = t;
125		}
126
127		if (ev->fd >= 0) {
128			if (ev->flags & EV_READ) {
129				FD_SET(ev->fd, &r);
130				nfd = (nfd > ev->fd) ? nfd : ev->fd;
131			}
132
133			if (ev->flags & EV_WRITE) {
134				FD_SET(ev->fd, &w);
135				nfd = (nfd > ev->fd) ? nfd : ev->fd;
136			}
137		}
138
139		__event_add_current(ev);
140	}
141
142	event_log_debug("%s: waiting for events...", __func__);
143
144	nfd = select(nfd + 1, &r, &w, NULL, &timeout);
145	if (nfd < 0)
146		return (-1);
147
148	/*
149	 * Process current pending
150	 */
151
152	event_log_debug("%s: processing events...", __func__);
153
154	gettimeofday(&now, NULL);
155
156	while (!TAILQ_EMPTY(&current)) {
157		ev = TAILQ_FIRST(&current);
158		__event_del_current(ev);
159
160		/* check if fd is ready for reading/writing */
161		if (nfd > 0 && ev->fd >= 0) {
162			if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) {
163				if (ev->flags & EV_PERSIST) {
164					if (ev->flags & EV_HAS_TIMEOUT)
165						event_add(ev, &ev->timeout);
166					else
167						event_add(ev, NULL);
168				}
169
170				nfd --;
171
172				event_log_debug("%s: calling %p(%d, %p), " \
173					"ev=%p", __func__, ev->cb, ev->fd,
174					ev->cbarg, ev);
175
176				(ev->cb)(ev->fd,
177					(ev->flags & (EV_READ|EV_WRITE)),
178					ev->cbarg);
179
180				continue;
181			}
182		}
183
184		/* if event has no timeout - just requeue */
185		if ((ev->flags & EV_HAS_TIMEOUT) == 0) {
186			event_add(ev, NULL);
187			continue;
188		}
189
190		/* check if event has expired */
191		if (tv_cmp(&now, &ev->expire) >= 0) {
192			if (ev->flags & EV_PERSIST)
193				event_add(ev, &ev->timeout);
194
195			event_log_debug("%s: calling %p(%d, %p), ev=%p",
196				__func__, ev->cb, ev->fd, ev->cbarg, ev);
197
198			(ev->cb)(ev->fd,
199				(ev->flags & (EV_READ|EV_WRITE)),
200				ev->cbarg);
201
202			continue;
203		}
204
205		assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
206		__event_link(ev);
207	}
208
209	return (0);
210}
211
212void
213__event_set(struct event *ev, int fd, short flags,
214	void (*cb)(int, short, void *), void *cbarg)
215{
216	ev->fd = fd;
217	ev->flags = flags;
218	ev->cb = cb;
219	ev->cbarg = cbarg;
220}
221
222int
223__event_add(struct event *ev, const struct timeval *timeout)
224{
225	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
226
227	if (timeout != NULL) {
228		gettimeofday(&ev->expire, NULL);
229		tv_add(&ev->expire, timeout);
230		ev->timeout = *timeout;
231		ev->flags |= EV_HAS_TIMEOUT;
232	} else
233		ev->flags &= ~EV_HAS_TIMEOUT;
234
235	__event_link(ev);
236
237	return (0);
238}
239
240int
241__event_del(struct event *ev)
242{
243	assert((ev->flags & EV_CURRENT) == 0);
244
245	if ((ev->flags & EV_PENDING) != 0) {
246		TAILQ_REMOVE(&pending, ev, next);
247		ev->flags &= ~EV_PENDING;
248	}
249
250	return (0);
251}
252
253static void
254__event_add_current(struct event *ev)
255{
256	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
257
258	TAILQ_INSERT_TAIL(&current, ev, next);
259	ev->flags |= EV_CURRENT;
260}
261
262static void
263__event_del_current(struct event *ev)
264{
265	assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT);
266
267	TAILQ_REMOVE(&current, ev, next);
268	ev->flags &= ~EV_CURRENT;
269}
270
271static void
272tv_add(struct timeval *a, struct timeval const *b)
273{
274	a->tv_sec += b->tv_sec;
275	a->tv_usec += b->tv_usec;
276
277	if(a->tv_usec >= 1000000) {
278		a->tv_usec -= 1000000;
279		a->tv_sec += 1;
280	}
281}
282
283static void
284tv_sub(struct timeval *a, struct timeval const *b)
285{
286	if (a->tv_usec < b->tv_usec) {
287		a->tv_usec += 1000000;
288		a->tv_sec -= 1;
289	}
290
291	a->tv_usec -= b->tv_usec;
292	a->tv_sec -= b->tv_sec;
293}
294
295static int
296tv_cmp(struct timeval const *a, struct timeval const *b)
297{
298 	if (a->tv_sec > b->tv_sec)
299		return (1);
300
301	if (a->tv_sec < b->tv_sec)
302		return (-1);
303
304	if (a->tv_usec > b->tv_usec)
305		return (1);
306
307	if (a->tv_usec < b->tv_usec)
308		return (-1);
309
310	return (0);
311}
312
313