2 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
3 * Modifications Copyright (c) 2006 Christian Walter <wolti@sil.at>
6 * Redistribution and use in source and binary forms, with or without modification,
7 * are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
20 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
28 * This file is part of the lwIP TCP/IP stack.
30 * Author: Adam Dunkels <adam@sics.se>
31 * Modifcations: Christian Walter <wolti@sil.at>
33 * $Id: sys_arch.c,v 1.6 2006/09/24 22:04:53 wolti Exp $
36 /* ------------------------ System includes ------------------------------- */
42 /* ------------------------ FreeRTOS includes ----------------------------- */
47 /* ------------------------ lwIP includes --------------------------------- */
48 #include "lwip/debug.h"
53 #include "lwip/stats.h"
55 /* ------------------------ Project includes ------------------------------ */
57 /* ------------------------ Defines --------------------------------------- */
58 /* This is the number of threads that can be started with sys_thead_new() */
59 #define SYS_MBOX_SIZE ( 16 )
60 #define MS_TO_TICKS( ms ) \
61 ( portTickType )( ( portTickType ) ( ms ) / portTICK_RATE_MS )
62 #define TICKS_TO_MS( ticks ) \
63 ( unsigned portLONG )( ( portTickType ) ( ticks ) * portTICK_RATE_MS )
64 #define THREAD_STACK_SIZE ( 1024 )
65 #define THREAD_NAME "lwIP"
67 #define THREAD_INIT( tcb ) \
70 tcb->pid = ( xTaskHandle )0; \
71 tcb->timeouts.next = NULL; \
74 /* ------------------------ Type definitions ------------------------------ */
75 typedef struct sys_tcb
78 struct sys_timeouts timeouts;
82 /* ------------------------ Prototypes ------------------------------------ */
84 /* ------------------------ Static functions ------------------------------ */
85 sys_tcb_t *sys_thread_current( void );
87 /* ------------------------ Static variables ------------------------------ */
88 static sys_tcb_t *tasks = NULL;
90 /* ------------------------ Start implementation -------------------------- */
94 LWIP_ASSERT( "sys_init: not called first", tasks == NULL );
99 * This optional function does a "fast" critical region protection and returns
100 * the previous protection level. This function is only called during very short
101 * critical regions. An embedded system which supports ISR-based drivers might
102 * want to implement this function by disabling interrupts. Task-based systems
103 * might want to implement this by using a mutex or disabling tasking. This
104 * function should support recursive calls from the same task or interrupt. In
105 * other words, sys_arch_protect() could be called while already protected. In
106 * that case the return value indicates that it is already protected.
108 * sys_arch_protect() is only required if your port is supporting an operating
112 sys_arch_protect( void )
114 vPortEnterCritical( );
119 * This optional function does a "fast" set of critical region protection to the
120 * value specified by pval. See the documentation for sys_arch_protect() for
121 * more information. This function is only required if your port is supporting
122 * an operating system.
125 sys_arch_unprotect( sys_prot_t pval )
128 vPortExitCritical( );
132 * Prints an assertion messages and aborts execution.
135 sys_assert( const char *msg )
137 fputs( msg, stderr );
138 fputs( "\n\r", stderr );
139 vPortEnterCritical( );
144 sys_debug( const char *const fmt, ... )
149 ( void )vprintf( fmt, ap );
150 ( void )putchar( '\r' );
154 /* ------------------------ Start implementation ( Threads ) -------------- */
157 sys_thread_new( void ( *thread ) ( void *arg ), void *arg, int prio )
159 return sys_arch_thread_new( thread, arg, prio, THREAD_STACK_SIZE );
163 * Starts a new thread with priority "prio" that will begin its execution in the
164 * function "thread()". The "arg" argument will be passed as an argument to the
165 * thread() function. The argument "ssize" is the requested stack size for the
166 * new thread. The id of the new thread is returned. Both the id and the
167 * priority are system dependent.
170 sys_arch_thread_new( void ( *thread ) ( void *arg ), void *arg, int prio, size_t ssize )
172 sys_thread_t thread_hdl = SYS_THREAD_NULL;
175 char thread_name[ configMAX_TASK_NAME_LEN ];
177 /* We disable the FreeRTOS scheduler because it might be the case that the new
178 * tasks gets scheduled inside the xTaskCreate function. To prevent this we
179 * disable the scheduling. Note that this can happen although we have interrupts
180 * disabled because xTaskCreate contains a call to taskYIELD( ).
182 vPortEnterCritical( );
186 /* We are called the first time. Initialize it. */
189 p = pvPortMalloc( sizeof( sys_tcb_t ) );
197 /* First task already counter. */
199 /* Cycle to the end of the list. */
200 while( p->next != NULL )
205 p->next = pvPortMalloc( sizeof( sys_tcb_t ) );
211 /* Memory allocated. Initialize the data structure. */
213 ( void )snprintf( thread_name, configMAX_TASK_NAME_LEN, "lwIP%d", i );
215 /* Now q points to a free element in the list. */
216 if( xTaskCreate( thread, thread_name, ssize, arg, prio, &p->pid ) == pdPASS )
226 vPortExitCritical( );
231 sys_arch_thread_remove( sys_thread_t hdl )
233 sys_tcb_t *current = tasks, *prev;
234 sys_tcb_t *toremove = hdl;
235 xTaskHandle pid = ( xTaskHandle ) 0;
237 LWIP_ASSERT( "sys_arch_thread_remove: assertion hdl != NULL failed!", hdl != NULL );
239 /* If we have to remove the first task we must update the global "tasks"
241 vPortEnterCritical( );
245 while( ( current != NULL ) && ( current != toremove ) )
248 current = current->next;
251 if( current == toremove )
253 /* Not the first entry in the list. */
256 prev->next = toremove->next;
260 tasks = toremove->next;
262 LWIP_ASSERT( "sys_arch_thread_remove: can't remove thread with timeouts!",
263 toremove->timeouts.next == NULL );
265 THREAD_INIT( toremove );
266 vPortFree( toremove );
269 /* We are done with accessing the shared datastructure. Release the
272 vPortExitCritical( );
273 if( pid != ( xTaskHandle ) 0 )
281 * Returns the thread control block for the currently active task. In case
282 * of an error the functions returns NULL.
285 sys_arch_thread_current( void )
287 sys_tcb_t *p = tasks;
288 xTaskHandle pid = xTaskGetCurrentTaskHandle( );
290 vPortEnterCritical( );
291 while( ( p != NULL ) && ( p->pid != pid ) )
295 vPortExitCritical( );
300 * Returns a pointer to the per-thread sys_timeouts structure. In lwIP,
301 * each thread has a list of timeouts which is represented as a linked
302 * list of sys_timeout structures. The sys_timeouts structure holds a
303 * pointer to a linked list of timeouts. This function is called by
304 * the lwIP timeout scheduler and must not return a NULL value.
306 * In a single threaded sys_arch implementation, this function will
307 * simply return a pointer to a global sys_timeouts variable stored in
308 * the sys_arch module.
310 struct sys_timeouts *
311 sys_arch_timeouts( void )
315 ptask = sys_arch_thread_current( );
316 LWIP_ASSERT( "sys_arch_timeouts: ptask != NULL", ptask != NULL );
317 return ptask != NULL ? &( ptask->timeouts ) : NULL;
320 /* ------------------------ Start implementation ( Semaphores ) ----------- */
322 /* Creates and returns a new semaphore. The "count" argument specifies
323 * the initial state of the semaphore.
326 sys_sem_new( u8_t count )
328 xSemaphoreHandle xSemaphore;
330 vSemaphoreCreateBinary( xSemaphore );
331 if( xSemaphore != SYS_SEM_NULL )
335 xSemaphoreTake( xSemaphore, 1 );
338 vPortEnterCritical( );
339 lwip_stats.sys.sem.used++;
340 if( lwip_stats.sys.sem.used > lwip_stats.sys.sem.max )
342 lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
344 vPortExitCritical( );
349 LWIP_ASSERT( "sys_sem_new: xSemaphore == SYS_SEM_NULL", xSemaphore != SYS_SEM_NULL );
355 /* Deallocates a semaphore */
357 sys_sem_free( sys_sem_t sem )
359 LWIP_ASSERT( "sys_sem_free: sem != SYS_SEM_NULL", sem != SYS_SEM_NULL );
360 if( sem != SYS_SEM_NULL )
363 vPortEnterCritical( );
364 lwip_stats.sys.sem.used--;
365 vPortExitCritical( );
371 /* Signals a semaphore */
373 sys_sem_signal( sys_sem_t sem )
375 LWIP_ASSERT( "sys_sem_signal: sem != SYS_SEM_NULL", sem != SYS_SEM_NULL );
376 xSemaphoreGive( sem );
380 * Blocks the thread while waiting for the semaphore to be
381 * signaled. If the "timeout" argument is non-zero, the thread should
382 * only be blocked for the specified time (measured in
385 * If the timeout argument is non-zero, the return value is the number of
386 * milliseconds spent waiting for the semaphore to be signaled. If the
387 * semaphore wasn't signaled within the specified time, the return value is
388 * SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
389 * (i.e., it was already signaled), the function may return zero.
391 * Notice that lwIP implements a function with a similar name,
392 * sys_sem_wait(), that uses the sys_arch_sem_wait() function.
395 sys_arch_sem_wait( sys_sem_t sem, u32_t timeout )
397 portBASE_TYPE xStatus;
398 portTickType xTicksStart, xTicksEnd, xTicksElapsed;
401 LWIP_ASSERT( "sys_arch_sem_wait: sem != SYS_SEM_NULL", sem != SYS_SEM_NULL );
402 xTicksStart = xTaskGetTickCount( );
407 xStatus = xSemaphoreTake( sem, MS_TO_TICKS( 100 ) );
409 while( xStatus != pdTRUE );
413 xStatus = xSemaphoreTake( sem, MS_TO_TICKS( timeout ) );
416 /* Semaphore was signaled. */
417 if( xStatus == pdTRUE )
419 xTicksEnd = xTaskGetTickCount( );
420 xTicksElapsed = xTicksEnd - xTicksStart;
421 timespent = TICKS_TO_MS( xTicksElapsed );
425 timespent = SYS_ARCH_TIMEOUT;
431 /* ------------------------ Start implementation ( Mailboxes ) ------------ */
433 /* Creates an empty mailbox. */
439 mbox = xQueueCreate( SYS_MBOX_SIZE, sizeof( void * ) );
440 if( mbox != SYS_MBOX_NULL )
443 vPortEnterCritical( );
444 lwip_stats.sys.mbox.used++;
445 if( lwip_stats.sys.mbox.used > lwip_stats.sys.mbox.max )
447 lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
449 vPortExitCritical( );
456 Deallocates a mailbox. If there are messages still present in the
457 mailbox when the mailbox is deallocated, it is an indication of a
458 programming error in lwIP and the developer should be notified.
461 sys_mbox_free( sys_mbox_t mbox )
465 LWIP_ASSERT( "sys_mbox_free: mbox != SYS_MBOX_NULL", mbox != SYS_MBOX_NULL );
466 if( mbox != SYS_MBOX_NULL )
468 while( uxQueueMessagesWaiting( mbox ) != 0 )
470 if( sys_arch_mbox_fetch( mbox, &msg, 1 ) != SYS_ARCH_TIMEOUT )
472 LWIP_ASSERT( "sys_mbox_free: memory leak (msg != NULL)", msg == NULL );
475 vQueueDelete( mbox );
477 vPortEnterCritical( );
478 lwip_stats.sys.mbox.used--;
479 vPortExitCritical( );
485 * This function sends a message to a mailbox. It is unusual in that no error
486 * return is made. This is because the caller is responsible for ensuring that
487 * the mailbox queue will not fail. The caller does this by limiting the number
488 * of msg structures which exist for a given mailbox.
491 sys_mbox_post( sys_mbox_t mbox, void *data )
493 portBASE_TYPE xQueueSent;
495 /* Queue must not be full - Otherwise it is an error. */
496 xQueueSent = xQueueSend( mbox, &data, 0 );
497 LWIP_ASSERT( "sys_mbox_post: xQueueSent == pdPASS", xQueueSent == pdPASS );
501 * Blocks the thread until a message arrives in the mailbox, but does
502 * not block the thread longer than "timeout" milliseconds (similar to
503 * the sys_arch_sem_wait() function). The "msg" argument is a result
504 * parameter that is set by the function (i.e., by doing "*msg =
505 * ptr"). The "msg" parameter maybe NULL to indicate that the message
508 * Note that a function with a similar name, sys_mbox_fetch(), is
509 * implemented by lwIP.
512 sys_arch_mbox_fetch( sys_mbox_t mbox, void **msg, u32_t timeout )
515 portBASE_TYPE xStatus;
516 portTickType xTicksStart, xTicksEnd, xTicksElapsed;
519 LWIP_ASSERT( "sys_arch_mbox_fetch: mbox != SYS_MBOX_NULL", mbox != SYS_MBOX_NULL );
520 xTicksStart = xTaskGetTickCount( );
525 xStatus = xQueueReceive( mbox, &ret_msg, MS_TO_TICKS( 100 ) );
527 while( xStatus != pdTRUE );
531 xStatus = xQueueReceive( mbox, &ret_msg, MS_TO_TICKS( timeout ) );
534 if( xStatus == pdTRUE )
540 xTicksEnd = xTaskGetTickCount( );
541 xTicksElapsed = xTicksEnd - xTicksStart;
542 timespent = TICKS_TO_MS( xTicksElapsed );
550 timespent = SYS_ARCH_TIMEOUT;
558 portTickType xTicks = xTaskGetTickCount( );
560 return ( u32_t )TICKS_TO_MS( xTicks );