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