xref: /freebsd/usr.sbin/bluetooth/btpand/event.c (revision 780fb4a2)
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$ */
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) \
54 do { \
55 		TAILQ_INSERT_TAIL(&pending, ev, next); \
56 		ev->flags |= EV_PENDING; \
57 } while (0)
58 
59 static void	tv_add(struct timeval *, struct timeval const *);
60 static void	tv_sub(struct timeval *, struct timeval const *);
61 static int	tv_cmp(struct timeval const *, struct timeval const *);
62 static int	__event_dispatch(void);
63 static void	__event_add_current(struct event *);
64 static void	__event_del_current(struct event *);
65 
66 
67 static TAILQ_HEAD(, event)	pending;
68 static TAILQ_HEAD(, event)	current;
69 
70 void
71 event_init(void)
72 {
73 	TAILQ_INIT(&pending);
74 }
75 
76 int
77 event_dispatch(void)
78 {
79 	while (__event_dispatch() == 0)
80 		;
81 
82 	return (-1);
83 }
84 
85 static 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 
212 void
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 
222 int
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 
240 int
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 
253 static 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 
262 static 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 
271 static void
272 tv_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 
283 static void
284 tv_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 
295 static int
296 tv_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