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