[LWIP]
[reactos.git] / lib / drivers / lwip / src / core / sys.c
1 /**
2 * @file
3 * lwIP Operating System abstraction
4 *
5 */
6
7 /*
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31 * OF SUCH DAMAGE.
32 *
33 * This file is part of the lwIP TCP/IP stack.
34 *
35 * Author: Adam Dunkels <adam@sics.se>
36 *
37 */
38
39 #include "lwip/opt.h"
40
41 #if (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
42
43 #include "lwip/sys.h"
44 #include "lwip/def.h"
45 #include "lwip/memp.h"
46 #include "lwip/tcpip.h"
47
48 /**
49 * Struct used for sys_sem_wait_timeout() to tell wether the time
50 * has run out or the semaphore has really become available.
51 */
52 struct sswt_cb
53 {
54 s16_t timeflag;
55 sys_sem_t *psem;
56 };
57
58 /**
59 * Wait (forever) for a message to arrive in an mbox.
60 * While waiting, timeouts (for this thread) are processed.
61 *
62 * @param mbox the mbox to fetch the message from
63 * @param msg the place to store the message
64 */
65 void
66 sys_mbox_fetch(sys_mbox_t mbox, void **msg)
67 {
68 u32_t time_needed;
69 struct sys_timeouts *timeouts;
70 struct sys_timeo *tmptimeout;
71 sys_timeout_handler h;
72 void *arg;
73
74 again:
75 timeouts = sys_arch_timeouts();
76
77 if (!timeouts || !timeouts->next) {
78 UNLOCK_TCPIP_CORE();
79 time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
80 LOCK_TCPIP_CORE();
81 } else {
82 if (timeouts->next->time > 0) {
83 UNLOCK_TCPIP_CORE();
84 time_needed = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time);
85 LOCK_TCPIP_CORE();
86 } else {
87 time_needed = SYS_ARCH_TIMEOUT;
88 }
89
90 if (time_needed == SYS_ARCH_TIMEOUT) {
91 /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
92 could be fetched. We should now call the timeout handler and
93 deallocate the memory allocated for the timeout. */
94 tmptimeout = timeouts->next;
95 timeouts->next = tmptimeout->next;
96 h = tmptimeout->h;
97 arg = tmptimeout->arg;
98 memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
99 if (h != NULL) {
100 LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", *(void**)&h, arg));
101 h(arg);
102 }
103
104 /* We try again to fetch a message from the mbox. */
105 goto again;
106 } else {
107 /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
108 occured. The time variable is set to the number of
109 milliseconds we waited for the message. */
110 if (time_needed < timeouts->next->time) {
111 timeouts->next->time -= time_needed;
112 } else {
113 timeouts->next->time = 0;
114 }
115 }
116 }
117 }
118
119 /**
120 * Wait (forever) for a semaphore to become available.
121 * While waiting, timeouts (for this thread) are processed.
122 *
123 * @param sem semaphore to wait for
124 */
125 void
126 sys_sem_wait(sys_sem_t sem)
127 {
128 u32_t time_needed;
129 struct sys_timeouts *timeouts;
130 struct sys_timeo *tmptimeout;
131 sys_timeout_handler h;
132 void *arg;
133
134 again:
135
136 timeouts = sys_arch_timeouts();
137
138 if (!timeouts || !timeouts->next) {
139 sys_arch_sem_wait(sem, 0);
140 } else {
141 if (timeouts->next->time > 0) {
142 time_needed = sys_arch_sem_wait(sem, timeouts->next->time);
143 } else {
144 time_needed = SYS_ARCH_TIMEOUT;
145 }
146
147 if (time_needed == SYS_ARCH_TIMEOUT) {
148 /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
149 could be fetched. We should now call the timeout handler and
150 deallocate the memory allocated for the timeout. */
151 tmptimeout = timeouts->next;
152 timeouts->next = tmptimeout->next;
153 h = tmptimeout->h;
154 arg = tmptimeout->arg;
155 memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
156 if (h != NULL) {
157 LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", *(void**)&h, (void *)arg));
158 h(arg);
159 }
160
161 /* We try again to fetch a message from the mbox. */
162 goto again;
163 } else {
164 /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
165 occured. The time variable is set to the number of
166 milliseconds we waited for the message. */
167 if (time_needed < timeouts->next->time) {
168 timeouts->next->time -= time_needed;
169 } else {
170 timeouts->next->time = 0;
171 }
172 }
173 }
174 }
175
176 /**
177 * Create a one-shot timer (aka timeout). Timeouts are processed in the
178 * following cases:
179 * - while waiting for a message using sys_mbox_fetch()
180 * - while waiting for a semaphore using sys_sem_wait() or sys_sem_wait_timeout()
181 * - while sleeping using the inbuilt sys_msleep()
182 *
183 * @param msecs time in milliseconds after that the timer should expire
184 * @param h callback function to call when msecs have elapsed
185 * @param arg argument to pass to the callback function
186 */
187 void
188 sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
189 {
190 struct sys_timeouts *timeouts;
191 struct sys_timeo *timeout, *t;
192
193 timeout = memp_malloc(MEMP_SYS_TIMEOUT);
194 if (timeout == NULL) {
195 LWIP_ASSERT("sys_timeout: timeout != NULL", timeout != NULL);
196 return;
197 }
198 timeout->next = NULL;
199 timeout->h = h;
200 timeout->arg = arg;
201 timeout->time = msecs;
202
203 timeouts = sys_arch_timeouts();
204
205 LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n",
206 (void *)timeout, msecs, *(void**)&h, (void *)arg));
207
208 if (timeouts == NULL) {
209 LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL);
210 return;
211 }
212
213 if (timeouts->next == NULL) {
214 timeouts->next = timeout;
215 return;
216 }
217
218 if (timeouts->next->time > msecs) {
219 timeouts->next->time -= msecs;
220 timeout->next = timeouts->next;
221 timeouts->next = timeout;
222 } else {
223 for(t = timeouts->next; t != NULL; t = t->next) {
224 timeout->time -= t->time;
225 if (t->next == NULL || t->next->time > timeout->time) {
226 if (t->next != NULL) {
227 t->next->time -= timeout->time;
228 }
229 timeout->next = t->next;
230 t->next = timeout;
231 break;
232 }
233 }
234 }
235 }
236
237 /**
238 * Go through timeout list (for this task only) and remove the first matching
239 * entry, even though the timeout has not triggered yet.
240 *
241 * @note This function only works as expected if there is only one timeout
242 * calling 'h' in the list of timeouts.
243 *
244 * @param h callback function that would be called by the timeout
245 * @param arg callback argument that would be passed to h
246 */
247 void
248 sys_untimeout(sys_timeout_handler h, void *arg)
249 {
250 struct sys_timeouts *timeouts;
251 struct sys_timeo *prev_t, *t;
252
253 timeouts = sys_arch_timeouts();
254
255 if (timeouts == NULL) {
256 LWIP_ASSERT("sys_untimeout: timeouts != NULL", timeouts != NULL);
257 return;
258 }
259 if (timeouts->next == NULL) {
260 return;
261 }
262
263 for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
264 if ((t->h == h) && (t->arg == arg)) {
265 /* We have a match */
266 /* Unlink from previous in list */
267 if (prev_t == NULL) {
268 timeouts->next = t->next;
269 } else {
270 prev_t->next = t->next;
271 }
272 /* If not the last one, add time of this one back to next */
273 if (t->next != NULL) {
274 t->next->time += t->time;
275 }
276 memp_free(MEMP_SYS_TIMEOUT, t);
277 return;
278 }
279 }
280 return;
281 }
282
283 /**
284 * Timeout handler function for sys_sem_wait_timeout()
285 *
286 * @param arg struct sswt_cb* used to signal a semaphore and end waiting.
287 */
288 static void
289 sswt_handler(void *arg)
290 {
291 struct sswt_cb *sswt_cb = (struct sswt_cb *) arg;
292
293 /* Timeout. Set flag to TRUE and signal semaphore */
294 sswt_cb->timeflag = 1;
295 sys_sem_signal(*(sswt_cb->psem));
296 }
297
298 /**
299 * Wait for a semaphore with timeout (specified in ms)
300 *
301 * @param sem semaphore to wait
302 * @param timeout timeout in ms (0: wait forever)
303 * @return 0 on timeout, 1 otherwise
304 */
305 int
306 sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout)
307 {
308 struct sswt_cb sswt_cb;
309
310 sswt_cb.psem = &sem;
311 sswt_cb.timeflag = 0;
312
313 /* If timeout is zero, then just wait forever */
314 if (timeout > 0) {
315 /* Create a timer and pass it the address of our flag */
316 sys_timeout(timeout, sswt_handler, &sswt_cb);
317 }
318 sys_sem_wait(sem);
319 /* Was it a timeout? */
320 if (sswt_cb.timeflag) {
321 /* timeout */
322 return 0;
323 } else {
324 /* Not a timeout. Remove timeout entry */
325 sys_untimeout(sswt_handler, &sswt_cb);
326 return 1;
327 }
328 }
329
330 /**
331 * Sleep for some ms. Timeouts are processed while sleeping.
332 *
333 * @param ms number of milliseconds to sleep
334 */
335 void
336 sys_msleep(u32_t ms)
337 {
338 sys_sem_t delaysem = sys_sem_new(0);
339
340 sys_sem_wait_timeout(delaysem, ms);
341
342 sys_sem_free(delaysem);
343 }
344
345
346 #endif /* NO_SYS */