/** * @file - sys_arch.c * System Architecture support routines for TI Tiva devices. * */ /* * Copyright (c) 2001-2003 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels * */ /* Copyright (c) 2008 Texas Instruments Incorporated */ /* lwIP includes. */ #include "lwip/opt.h" #include "lwip/sys.h" #include "lwip/mem.h" #if NO_SYS #if SYS_LIGHTWEIGHT_PROT /* TivaWare header files required for this interface driver. */ #include #include #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" /** * This global is defined in lwiplib.c and contains a count of the number of * elapsed milliseconds since lwIP started. */ extern uint32_t g_ui32LocalTimer; /** * This function returns the system time in milliseconds. */ u32_t sys_now(void) { return(g_ui32LocalTimer); } /** * This function is used to lock access to critical sections when lwipopt.h * defines SYS_LIGHTWEIGHT_PROT. It disables interrupts and returns a value * indicating the interrupt enable state when the function entered. This * value must be passed back on the matching call to sys_arch_unprotect(). * * @return the interrupt level when the function was entered. */ sys_prot_t sys_arch_protect(void) { return((sys_prot_t)MAP_IntMasterDisable()); } /** * This function is used to unlock access to critical sections when lwipopt.h * defines SYS_LIGHTWEIGHT_PROT. It enables interrupts if the value of the lev * parameter indicates that they were enabled when the matching call to * sys_arch_protect() was made. * * @param lev is the interrupt level when the matching protect function was * called */ void sys_arch_unprotect(sys_prot_t lev) { /* Only turn interrupts back on if they were originally on when the matching sys_arch_protect() call was made. */ if(!(lev & 1)) { MAP_IntMasterEnable(); } } #endif /* SYS_LIGHTWEIGHT_PROT */ #else /* NO_SYS */ /* A structure to contain the variables for a sys_thread_t. */ typedef struct { void *stackstart; void *stackend; void (*thread)(void *arg); void *arg; #if RTOS_FREERTOS xTaskHandle taskhandle; #endif /* RTOS_FREERTOS */ } thread_t; /* Provide a default maximum number of threads. */ #ifndef SYS_THREAD_MAX #define SYS_THREAD_MAX 4 #endif /* SYS_THREAD_MAX */ /* Provide a default maximum number of semaphores. */ #ifndef SYS_SEM_MAX #define SYS_SEM_MAX 4 #endif /* SYS_SEM_MAX */ /* Provide a default maximum number of mailboxes. */ #ifndef SYS_MBOX_MAX #define SYS_MBOX_MAX 4 #endif /* SYS_MBOX_MAX */ /* An array to hold the memory for the available semaphores. */ static sem_t sems[SYS_SEM_MAX]; /* An array to hold the memory for the available mailboxes. */ static mbox_t mboxes[SYS_MBOX_MAX]; /* An array to hold the memory for the available threads. */ static thread_t threads[SYS_THREAD_MAX]; /** * Initializes the system architecture layer. * */ void sys_init(void) { u32_t i; /* Clear out the mailboxes. */ for(i = 0; i < SYS_MBOX_MAX; i++) { mboxes[i].queue = 0; } /* Clear out the semaphores. */ for(i = 0; i < SYS_SEM_MAX; i++) { sems[i].queue = 0; } /* Clear out the threads. */ for(i = 0; i < SYS_THREAD_MAX; i++) { threads[i].stackstart = NULL; threads[i].stackend = NULL; } } /** * Creates a new semaphore. * * @param count is non-zero if the semaphore should be acquired initially. * @return the handle of the created semaphore. */ err_t sys_sem_new(sys_sem_t *sem, u8_t count) { void *temp; u32_t i; /* Find a semaphore that is not in use. */ for(i = 0; i < SYS_SEM_MAX; i++) { if(sems[i].queue == 0) { break; } } if(i == SYS_SEM_MAX) { #if SYS_STATS STATS_INC(sys.sem.err); #endif /* SYS_STATS */ return ERR_MEM; } /* Create a single-entry queue to act as a semaphore. */ #if RTOS_FREERTOS sem->queue = xQueueCreate(1, sizeof(void *)); if(sem->queue == NULL) { #endif /* RTOS_FREERTOS */ #if SYS_STATS STATS_INC(sys.sem.err); #endif /* SYS_STATS */ return ERR_MEM; } /* Acquired the semaphore if necessary. */ if(count == 0) { temp = 0; xQueueSend(sem->queue, &temp, 0); } /* Update the semaphore statistics. */ #if SYS_STATS STATS_INC(sys.sem.used); #if LWIP_STATS if(lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; } #endif #endif /* SYS_STATS */ /* Save the queue handle. */ sems[i].queue = sem->queue; /* Return this semaphore. */ return (ERR_OK); } /** * Signal a semaphore. * * @param sem is the semaphore to signal. */ void sys_sem_signal(sys_sem_t *sem) { void *msg; /* Receive a message from the semaphore's queue. */ xQueueReceive(sem->queue, &msg, 0); } /** * Wait for a semaphore to be signalled. * * @param sem is the semaphore * @param timeout is the maximum number of milliseconds to wait for the * semaphore to be signalled * @return the number of milliseconds that passed before the semaphore was * acquired, or SYS_ARCH_TIMEOUT if the timeout occurred */ u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) { portTickType starttime; void *msg = 0; /* Get the starting time. */ starttime = xTaskGetTickCount(); /* See if there is a timeout. */ if(timeout != 0) { /* Send a message to the queue. */ if(xQueueSend(sem->queue, &msg, timeout / portTICK_RATE_MS) == pdPASS) { /* Return the amount of time it took for the semaphore to be signalled. */ return (xTaskGetTickCount() - starttime) * portTICK_RATE_MS; } else { /* The semaphore failed to signal in the allotted time. */ return SYS_ARCH_TIMEOUT; } } else { /* Try to send a message to the queue until it succeeds. */ while(xQueueSend(sem->queue, &msg, portMAX_DELAY) != pdPASS); /* Return the amount of time it took for the semaphore to be signalled. */ return (xTaskGetTickCount() - starttime) * portTICK_RATE_MS; } } /** * Destroys a semaphore. * * @param sem is the semaphore to be destroyed. */ void sys_sem_free(sys_sem_t *sem) { /* Clear the queue handle. */ sem->queue = 0; /* Update the semaphore statistics. */ #if SYS_STATS STATS_DEC(sys.sem.used); #endif /* SYS_STATS */ } /** * Creates a new mailbox. * * @param size is the number of entries in the mailbox. * @return the handle of the created mailbox. */ err_t sys_mbox_new(sys_mbox_t *mbox, int size) { u32_t i; /* Fail if the mailbox size is too large. */ if(size > MBOX_MAX) { #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ return ERR_MEM; } /* Find a mailbox that is not in use. */ for(i = 0; i < SYS_MBOX_MAX; i++) { if(mboxes[i].queue == 0) { break; } } if(i == SYS_MBOX_MAX) { #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ return ERR_MEM; } #if RTOS_FREERTOS /* Create a queue for this mailbox. */ mbox->queue = xQueueCreate(size, sizeof(void *)); if(mbox == NULL) { #endif /* RTOS_FREERTOS */ #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ return ERR_MEM; } /* Update the mailbox statistics. */ #if SYS_STATS STATS_INC(sys.mbox.used); #if LWIP_STATS if(lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; } #endif #endif /* SYS_STATS */ /* Save the queue handle. */ mboxes[i].queue = mbox->queue; /* Return this mailbox. */ return ERR_OK; } /** * Sends a message to a mailbox. * * @param mbox is the mailbox * @param msg is the message to send */ void sys_mbox_post(sys_mbox_t *mbox, void *msg) { /* Send this message to the queue. */ while(xQueueSend(mbox->queue, &msg, portMAX_DELAY) != pdPASS); } /** * Tries to send a message to a mailbox. * * @param mbox is the mailbox * @param msg is the message to send * @return ERR_OK if the message was sent and ERR_MEM if there was no space for * the message */ err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) { /* Send this message to the queue. */ if(xQueueSend(mbox->queue, &msg, 0) == pdPASS) { return ERR_OK; } /* Update the mailbox statistics. */ #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ /* The message could not be sent. */ return ERR_MEM; } /** * Tries to send a message to a mailbox. * * @param mbox is the mailbox * @param msg is the message to send * @return ERR_OK if the message was sent and ERR_MEM if there was no space for * the message */ err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg) { /* Send this message to the queue. */ if(xQueueSendFromISR(mbox->queue, &msg, 0) == pdPASS) { return ERR_OK; } /* Update the mailbox statistics. */ #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ /* The message could not be sent. */ return ERR_MEM; } /** * Retrieve a message from a mailbox. * * @param mbox is the mailbox * @param msg is a pointer to the location to receive the message * @param timeout is the maximum number of milliseconds to wait for the message * @return the number of milliseconds that passed before the message was * received, or SYS_ARCH_TIMEOUT if the tmieout occurred */ u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) { portTickType starttime; void *dummyptr; /* If the actual message contents are not required, provide a local variable to recieve the message. */ if(msg == NULL) { msg = &dummyptr; } /* Get the starting time. */ starttime = xTaskGetTickCount(); /* See if there is a timeout. */ if(timeout != 0) { /* Receive a message from the queue. */ if(xQueueReceive(mbox->queue, msg, timeout / portTICK_RATE_MS) == pdPASS) { /* Return the amount of time it took for the message to be received. */ return (xTaskGetTickCount() - starttime) * portTICK_RATE_MS; } else { /* No message arrived in the allotted time. */ *msg = NULL; return SYS_ARCH_TIMEOUT; } } else { /* Try to receive a message until one arrives. */ while(xQueueReceive(mbox->queue, msg, portMAX_DELAY) != pdPASS); /* Return the amount of time it took for the message to be received. */ return (xTaskGetTickCount() - starttime) * portTICK_RATE_MS; } } /** * Try to receive a message from a mailbox, returning immediately if one is not * available. * * @param mbox is the mailbox * @param msg is a pointer to the location to receive the message * @return ERR_OK if a message was available and SYS_MBOX_EMPTY if one was not * available */ u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) { void *dummyptr; /* If the actual message contents are not required, provide a local variable to recieve the message. */ if(msg == NULL) { msg = &dummyptr; } /* Recieve a message from the queue. */ if(xQueueReceive(mbox->queue, msg, 0) == pdPASS) { /* A message was available. */ return ERR_OK; } else { /* A message was not available. */ return SYS_MBOX_EMPTY; } } /** * Destroys a mailbox. * * @param mbox is the mailbox to be destroyed. */ void sys_mbox_free(sys_mbox_t *mbox) { /* There should not be any messages waiting (if there are it is a bug). If any are waiting, increment the mailbox error count. */ #if RTOS_FREERTOS if(uxQueueMessagesWaiting(mbox->queue) != 0) { #endif /* RTOS_FREERTOS */ #if SYS_STATS STATS_INC(sys.mbox.err); #endif /* SYS_STATS */ } /* Clear the queue handle. */ mbox->queue = 0; /* Update the mailbox statistics. */ #if SYS_STATS STATS_DEC(sys.mbox.used); #endif /* SYS_STATS */ } /** * Checks the validity of a mailbox. * * @param mbox is the mailbox whose validity is to be checked. */ int sys_mbox_valid(sys_mbox_t *mbox) { /*Check if a mailbox has been created*/ if(mbox->queue == SYS_MBOX_NULL){ return 0; } else{ return 1; } } /** * The routine for a thread. This handles some housekeeping around the * applications's thread routine. * * @param arg is the index into the thread structure for this thread */ static void sys_arch_thread(void *arg) { u32_t i; /* Get this threads index. */ i = (u32_t)arg; /* Call the application's thread routine. */ threads[i].thread(threads[i].arg); /* Free the memory used by this thread's stack. */ mem_free(threads[i].stackstart); /* Clear the stack from the thread structure. */ threads[i].stackstart = NULL; threads[i].stackend = NULL; /* Delete this task. */ #if RTOS_FREERTOS vTaskDelete(NULL); #endif } /** * Creates a new thread. * * @param name is the name of this thread * @param thread is a pointer to the function to run in the new thread * @param arg is the argument to pass to the thread's function * @param stacksize is the size of the stack to allocate for this thread * @param prio is the priority of the new thread * @return the handle fo the created thread */ sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) { sys_thread_t created_thread; void *data; u32_t i; /* Find a thread that is not in use. */ for(i = 0; i < SYS_THREAD_MAX; i++) { if(threads[i].stackstart == NULL) { break; } } if(i == SYS_THREAD_MAX) { return NULL; } /* Allocate memory for the thread's stack. */ data = mem_malloc(stacksize); if(!data) { return NULL; } /* Save the details of this thread. */ threads[i].stackstart = data; threads[i].stackend = (void *)((char *)data + stacksize); threads[i].thread = thread; threads[i].arg = arg; /* Create a new thread. */ #if RTOS_FREERTOS if(xTaskCreate(sys_arch_thread, (signed portCHAR *)name, stacksize/sizeof(int), (void *)i, tskIDLE_PRIORITY+prio, &threads[i].taskhandle) != pdTRUE){ threads[i].stackstart = NULL; threads[i].stackend = NULL; return NULL; } created_thread = threads[i].taskhandle; #endif /* RTOS_FREERTOS */ /* Return this thread. */ return created_thread; } /** * Enters a critical section. * * @return the previous protection level */ sys_prot_t sys_arch_protect(void) { #if RTOS_FREERTOS taskENTER_CRITICAL(); #endif return 1; } /** * Leaves a critical section. * * @param the preivous protection level */ void sys_arch_unprotect(sys_prot_t val) { #if RTOS_FREERTOS taskEXIT_CRITICAL(); #endif } u32_t sys_now(void) { return xTaskGetTickCount() * portTICK_PERIOD_MS; } #endif /* NO_SYS */