xref: /freebsd/usr.sbin/bluetooth/btpand/event.c (revision 6b13d60bf49ee40626d7e3a5d5a80519f0067307)
1 /*
2  * event.h
3  */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-2-Clause
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 
34 /*
35  * Hack to provide libevent (see devel/libevent port) like API.
36  * Should be removed if FreeBSD ever decides to import libevent into base.
37  */
38 
39 #include <sys/select.h>
40 #include <sys/time.h>
41 #include <sys/queue.h>
42 #include <assert.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <syslog.h>
47 
48 #include "event.h"
49 #define L2CAP_SOCKET_CHECKED
50 #include "btpand.h"
51 
52 #define		__event_link(ev) \
53 do { \
54 		TAILQ_INSERT_TAIL(&pending, ev, next); \
55 		ev->flags |= EV_PENDING; \
56 } while (0)
57 
58 static void	tv_add(struct timeval *, struct timeval const *);
59 static void	tv_sub(struct timeval *, struct timeval const *);
60 static int	tv_cmp(struct timeval const *, struct timeval const *);
61 static int	__event_dispatch(void);
62 static void	__event_add_current(struct event *);
63 static void	__event_del_current(struct event *);
64 
65 
66 static TAILQ_HEAD(, event)	pending;
67 static TAILQ_HEAD(, event)	current;
68 
69 void
70 event_init(void)
71 {
72 	TAILQ_INIT(&pending);
73 }
74 
75 int
76 event_dispatch(void)
77 {
78 	while (__event_dispatch() == 0)
79 		;
80 
81 	return (-1);
82 }
83 
84 static int
85 __event_dispatch(void)
86 {
87 	fd_set			r, w;
88 	int			nfd;
89 	struct event		*ev;
90 	struct timeval		now, timeout, t;
91 
92 	FD_ZERO(&r);
93 	FD_ZERO(&w);
94 
95 	nfd = 0;
96 
97 	gettimeofday(&now, NULL);
98 
99 	timeout.tv_sec = 10;	/* arbitrary */
100 	timeout.tv_usec = 0;
101 
102 	TAILQ_INIT(&current);
103 
104 	/*
105 	 * Build fd_set's
106 	 */
107 
108 	event_log_debug("%s: building fd set...", __func__);
109 
110 	while (!TAILQ_EMPTY(&pending)) {
111 		ev = TAILQ_FIRST(&pending);
112 		event_del(ev);
113 
114 		if (ev->flags & EV_HAS_TIMEOUT) {
115 			if (tv_cmp(&now, &ev->expire) >= 0)
116 				t.tv_sec = t.tv_usec = 0;
117 			else {
118 				t = ev->expire;
119 				tv_sub(&t, &now);
120 			}
121 
122 			if (tv_cmp(&t, &timeout) < 0)
123 				timeout = t;
124 		}
125 
126 		if (ev->fd >= 0) {
127 			if (ev->flags & EV_READ) {
128 				FD_SET(ev->fd, &r);
129 				nfd = (nfd > ev->fd) ? nfd : ev->fd;
130 			}
131 
132 			if (ev->flags & EV_WRITE) {
133 				FD_SET(ev->fd, &w);
134 				nfd = (nfd > ev->fd) ? nfd : ev->fd;
135 			}
136 		}
137 
138 		__event_add_current(ev);
139 	}
140 
141 	event_log_debug("%s: waiting for events...", __func__);
142 
143 	nfd = select(nfd + 1, &r, &w, NULL, &timeout);
144 	if (nfd < 0)
145 		return (-1);
146 
147 	/*
148 	 * Process current pending
149 	 */
150 
151 	event_log_debug("%s: processing events...", __func__);
152 
153 	gettimeofday(&now, NULL);
154 
155 	while (!TAILQ_EMPTY(&current)) {
156 		ev = TAILQ_FIRST(&current);
157 		__event_del_current(ev);
158 
159 		/* check if fd is ready for reading/writing */
160 		if (nfd > 0 && ev->fd >= 0) {
161 			if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) {
162 				if (ev->flags & EV_PERSIST) {
163 					if (ev->flags & EV_HAS_TIMEOUT)
164 						event_add(ev, &ev->timeout);
165 					else
166 						event_add(ev, NULL);
167 				}
168 
169 				nfd --;
170 
171 				event_log_debug("%s: calling %p(%d, %p), " \
172 					"ev=%p", __func__, ev->cb, ev->fd,
173 					ev->cbarg, ev);
174 
175 				(ev->cb)(ev->fd,
176 					(ev->flags & (EV_READ|EV_WRITE)),
177 					ev->cbarg);
178 
179 				continue;
180 			}
181 		}
182 
183 		/* if event has no timeout - just requeue */
184 		if ((ev->flags & EV_HAS_TIMEOUT) == 0) {
185 			event_add(ev, NULL);
186 			continue;
187 		}
188 
189 		/* check if event has expired */
190 		if (tv_cmp(&now, &ev->expire) >= 0) {
191 			if (ev->flags & EV_PERSIST)
192 				event_add(ev, &ev->timeout);
193 
194 			event_log_debug("%s: calling %p(%d, %p), ev=%p",
195 				__func__, ev->cb, ev->fd, ev->cbarg, ev);
196 
197 			(ev->cb)(ev->fd,
198 				(ev->flags & (EV_READ|EV_WRITE)),
199 				ev->cbarg);
200 
201 			continue;
202 		}
203 
204 		assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
205 		__event_link(ev);
206 	}
207 
208 	return (0);
209 }
210 
211 void
212 __event_set(struct event *ev, int fd, short flags,
213 	void (*cb)(int, short, void *), void *cbarg)
214 {
215 	ev->fd = fd;
216 	ev->flags = flags;
217 	ev->cb = cb;
218 	ev->cbarg = cbarg;
219 }
220 
221 int
222 __event_add(struct event *ev, const struct timeval *timeout)
223 {
224 	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
225 
226 	if (timeout != NULL) {
227 		gettimeofday(&ev->expire, NULL);
228 		tv_add(&ev->expire, timeout);
229 		ev->timeout = *timeout;
230 		ev->flags |= EV_HAS_TIMEOUT;
231 	} else
232 		ev->flags &= ~EV_HAS_TIMEOUT;
233 
234 	__event_link(ev);
235 
236 	return (0);
237 }
238 
239 int
240 __event_del(struct event *ev)
241 {
242 	assert((ev->flags & EV_CURRENT) == 0);
243 
244 	if ((ev->flags & EV_PENDING) != 0) {
245 		TAILQ_REMOVE(&pending, ev, next);
246 		ev->flags &= ~EV_PENDING;
247 	}
248 
249 	return (0);
250 }
251 
252 static void
253 __event_add_current(struct event *ev)
254 {
255 	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
256 
257 	TAILQ_INSERT_TAIL(&current, ev, next);
258 	ev->flags |= EV_CURRENT;
259 }
260 
261 static void
262 __event_del_current(struct event *ev)
263 {
264 	assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT);
265 
266 	TAILQ_REMOVE(&current, ev, next);
267 	ev->flags &= ~EV_CURRENT;
268 }
269 
270 static void
271 tv_add(struct timeval *a, struct timeval const *b)
272 {
273 	a->tv_sec += b->tv_sec;
274 	a->tv_usec += b->tv_usec;
275 
276 	if(a->tv_usec >= 1000000) {
277 		a->tv_usec -= 1000000;
278 		a->tv_sec += 1;
279 	}
280 }
281 
282 static void
283 tv_sub(struct timeval *a, struct timeval const *b)
284 {
285 	if (a->tv_usec < b->tv_usec) {
286 		a->tv_usec += 1000000;
287 		a->tv_sec -= 1;
288 	}
289 
290 	a->tv_usec -= b->tv_usec;
291 	a->tv_sec -= b->tv_sec;
292 }
293 
294 static int
295 tv_cmp(struct timeval const *a, struct timeval const *b)
296 {
297  	if (a->tv_sec > b->tv_sec)
298 		return (1);
299 
300 	if (a->tv_sec < b->tv_sec)
301 		return (-1);
302 
303 	if (a->tv_usec > b->tv_usec)
304 		return (1);
305 
306 	if (a->tv_usec < b->tv_usec)
307 		return (-1);
308 
309 	return (0);
310 }
311 
312