2025-06-21 09:44:53 +02:00

664 lines
16 KiB
C

/**
* @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 <adam@sics.se>
*
*/
/* 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 <stdint.h>
#include <stdbool.h>
#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 */