lwip-contrib-mac/ports/coldfire/sys_arch.c
davidhaas ae49c020d7 sys_thread_new() now returns the thread (request from Marc Boucher).
Removed some unused .h files in coldfire port.

Support LWIP_DIAG and LWIP_ASSERT in coldfire (sort of).

Fix to last api fix to make sure select() is triggered, even when there has
been a FIN.

Allow build of unixsim from cygwin by specifying "make ARCH=cygwin" or from
linux by specifying "make ARCH=linux".
2003-02-11 20:59:49 +00:00

583 lines
19 KiB
C

/* @(#)sys_arch.c
* 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: David Haas <dhaas@alum.rpi.edu>
*
*/
#include "lwip/debug.h"
#include "lwip/sys.h"
#include "lwip/opt.h"
#include "lwip/stats.h"
#include "nucleus.h"
#include "config.h"
#include <stdlib.h>
#include <string.h>
struct sys_thread {
struct sys_thread *next;
struct sys_timeouts timeouts;
int errno_i;
NU_TASK *pthread;
void (*function)(void *arg);
void *arg;
};
struct sys_hisr
{
struct sys_hisr *next;
NU_HISR *hisr;
void (*disablefun) (void);
u32_t vector;
};
static int num_sem = 0; // Number of semaphores created
static int num_mbox = 0; // Number of mailboxes created
static int num_thread = 0; // Number of threads created
static int num_hisr = 0; // Number of hisrs created
static sys_sem_t thread_sem; // Protect thread structure
static struct sys_thread *threads = NULL;
static struct sys_hisr *hisrs = NULL;
#define TICKS_PER_SECOND 10000
#define MS_TO_TICKS(MS) (MS * (TICKS_PER_SECOND / 1000))
#define TICKS_TO_MS(TICKS) ((unsigned long)((1000ULL * TICKS) / TICKS_PER_SECOND))
#define SYS_MBOX_SIZE 128 // Number of elements in mbox queue
#define SYS_STACK_SIZE 2048 // A minimum Nucleus stack for coldfire
#define SYS_HISR_STACK_SIZE 2048 // A minimum Nucleus stack for coldfire
/* People often make a mistake on the priority of their communications task.
The TCP/IP stack should be at a relatively low priority if it is an endpoint
(not a router) on a somewhat underpowered CPU. You are'nt going to keep up
with network traffic during a denial of service attack or misconfigured network
and you don't want an overburdened network task to cause other important tasks
(including your UI) to stop working. Drop packets! It forces flow control and
lets the rest of your system run.
*/
#define SYS_THREAD_PRIORITY 220 // Relatively low priority
/*---------------------------------------------------------------------------------*/
void
sys_init(void)
{
thread_sem = sys_sem_new(1);
return;
}
/*---------------------------------------------------------------------------------*/
static void
sys_thread_entry(UNSIGNED argc, VOID *argv)
{
/* argv is passed as a pointer to our thread structure */
struct sys_thread *p_thread = (struct sys_thread *)argv;
p_thread->function(p_thread->arg);
}
/*---------------------------------------------------------------------------------*/
static struct sys_thread *
introduce_thread(NU_TASK *id, void (*function)(void *arg), void *arg)
{
struct sys_thread *thread;
thread = (struct sys_thread *) calloc(1,sizeof(struct sys_thread));
if(thread) {
sys_arch_sem_wait(thread_sem, 0); //Wait to obtain the semaphore
thread->next = threads;
thread->timeouts.next = NULL;
thread->pthread = id;
thread->function = function;
thread->arg = arg;
threads = thread;
sys_sem_signal(thread_sem); //Release semaphore
}
return thread;
}
/*---------------------------------------------------------------------------------*/
/* We use Nucleus task as thread. Create one with a standard size stack at a standard
* priority. */
sys_thread_t
sys_thread_new(void (*function)(void *arg), void *arg)
{
NU_TASK *p_thread;
u8_t *p_stack;
STATUS status;
char thread_name[8] = " ";
struct sys_thread *st;
p_stack = (u8_t *) malloc(SYS_STACK_SIZE);
if (p_stack)
{
p_thread = (NU_TASK *) calloc(1,sizeof(NU_TASK));
if (p_thread)
{
/* get a new thread structure */
st = introduce_thread(p_thread, function, arg);
if (st)
{
num_thread = (num_thread +1) % 100; // Only count to 99
sprintf(thread_name, "lwip%02d", num_thread);
thread_name[strlen(thread_name)] = ' ';
status = NU_Create_Task(p_thread,
thread_name,
sys_thread_entry,
0,
st,
p_stack,
SYS_STACK_SIZE,
SYS_THREAD_PRIORITY,
0, //Disable timeslicing
NU_PREEMPT,
NU_START);
if (status == NU_SUCCESS)
return p_thread;
}
}
}
abort();
}
/*-----------------------------------------------------------------------------------*/
static struct sys_thread *
current_thread(void)
{
struct sys_thread *st;
NU_TASK *pt;
pt = NU_Current_Task_Pointer();
sys_arch_sem_wait(thread_sem, 0);
for(st = threads; st != NULL; st = st->next)
{
if(st->pthread == pt)
{
sys_sem_signal(thread_sem);
return st;
}
}
sys_sem_signal(thread_sem);
st = introduce_thread(pt, 0, 0);
if(!st) {
abort();
}
return st;
}
/*---------------------------------------------------------------------------------*/
struct sys_timeouts *
sys_arch_timeouts(void)
{
struct sys_thread *thread;
thread = current_thread();
return &thread->timeouts;
}
/*---------------------------------------------------------------------------------*/
int *
sys_arch_errno(void)
{
struct sys_thread *thread;
thread = current_thread();
return &thread->errno_i;
}
/*---------------------------------------------------------------------------------*/
sys_sem_t
sys_sem_new(u8_t count)
{
STATUS status;
NU_SEMAPHORE *sem;
char sem_name[8] = " ";
#ifdef SYS_STATS
lwip_stats.sys.sem.used++;
if(lwip_stats.sys.sem.used > lwip_stats.sys.sem.max)
{
lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
}
#endif /* SYS_STATS */
/* Get memory for new semaphore */
sem = (NU_SEMAPHORE *) calloc(1,sizeof(NU_SEMAPHORE));
if (sem)
{
/* Create a unique name for semaphore based on number created */
num_sem = (num_sem + 1) % 100; // Only count to 99
sprintf(sem_name, "lwip%02d", num_sem);
sem_name[strlen(sem_name)] = ' ';
/* Ask nucleus to create semaphore */
NU_Create_Semaphore(sem,
sem_name,
count,
NU_FIFO);
}
return sem;
}
/*---------------------------------------------------------------------------------*/
void
sys_sem_free(sys_sem_t sem)
{
if (sem != SYS_SEM_NULL)
{
#ifdef SYS_STATS
lwip_stats.sys.sem.used--;
#endif /* SYS_STATS */
NU_Delete_Semaphore(sem);
free(sem);
}
}
/*---------------------------------------------------------------------------------*/
void
sys_sem_signal(sys_sem_t sem)
{
NU_Release_Semaphore(sem);
}
/*---------------------------------------------------------------------------------*/
u32_t
sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
{
UNSIGNED timestart, timespent;
STATUS status;
/* Get the current time */
timestart = NU_Retrieve_Clock();
/* Wait for the semaphore */
status = NU_Obtain_Semaphore(sem,
timeout ? MS_TO_TICKS(timeout) : NU_SUSPEND);
/* This next statement takes wraparound into account. It works. Really! */
timespent = TICKS_TO_MS(((s32_t) ((s32_t) NU_Retrieve_Clock() - (s32_t) timestart)));
if (status == NU_TIMEOUT)
return 0;
else
return timespent ? timespent : 1;
}
/*---------------------------------------------------------------------------------*/
sys_mbox_t
sys_mbox_new(void)
{
u32_t *p_queue_mem;
NU_QUEUE *p_queue;
char queue_name[8] = " ";
/* Allocate memory for queue */
p_queue_mem = (u32_t *) calloc(1,(SYS_MBOX_SIZE * sizeof(u32_t)));
if (p_queue_mem)
{
/* Allocate memory for queue control block */
p_queue = (NU_QUEUE *) calloc(1,sizeof(NU_QUEUE));
if (p_queue)
{
/* Create a unique name for mbox based on number created */
num_mbox = (num_mbox + 1) % 100;
sprintf(queue_name, "lwip%02d", num_mbox);
queue_name[strlen(queue_name)] = ' ';
NU_Create_Queue(p_queue,
queue_name,
p_queue_mem,
SYS_MBOX_SIZE,
NU_FIXED_SIZE,
1,
NU_FIFO);
#ifdef SYS_STATS
lwip_stats.sys.mbox.used++;
if(lwip_stats.sys.mbox.used > lwip_stats.sys.mbox.max) {
lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
}
#endif /* SYS_STATS */
return p_queue;
}
else
free(p_queue_mem);
}
return SYS_MBOX_NULL;
}
/*---------------------------------------------------------------------------------*/
void
sys_mbox_free(sys_mbox_t mbox)
{
VOID *p_queue_mem;
CHAR name[8];
UNSIGNED queue_size;
UNSIGNED available;
UNSIGNED messages;
OPTION message_type;
UNSIGNED message_size;
OPTION suspend_type;
UNSIGNED tasks_waiting;
NU_TASK *first_task;
STATUS status;
if (mbox != SYS_MBOX_NULL)
{
/* First we need to get address of queue memory. Ask Nucleus
for information about the queue */
status = NU_Queue_Information(mbox,
name,
&p_queue_mem,
&queue_size,
&available,
&messages,
&message_type,
&message_size,
&suspend_type,
&tasks_waiting,
&first_task);
if (status == NU_SUCCESS)
free(p_queue_mem);
NU_Delete_Queue(mbox);
free(mbox);
#ifdef SYS_STATS
lwip_stats.sys.mbox.used--;
#endif /* SYS_STATS */
}
}
/*---------------------------------------------------------------------------------
This function sends a message to a mailbox. It is unusual in that no error
return is made. This is because the caller is responsible for ensuring that
the mailbox queue will not fail. The caller does this by limiting the number
of msg structures which exist for a given mailbox.
---------------------------------------------------------------------------------*/
void
sys_mbox_post(sys_mbox_t mbox, void *msg)
{
UNSIGNED status;
DEBUGF(SYS_DEBUG, ("sys_mbox_post: mbox %p msg %p\n", mbox, msg));
status = NU_Send_To_Queue(mbox,
&msg,
1,
NU_NO_SUSPEND);
LWIP_ASSERT("sys_mbox_post: mbx post failed", status == NU_SUCCESS);
}
/*---------------------------------------------------------------------------------*/
u32_t
sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
{
UNSIGNED timestart, timespent;
STATUS status;
void *ret_msg;
UNSIGNED actual_size;
/* Get the current time */
timestart = NU_Retrieve_Clock();
/* Wait for message */
status = NU_Receive_From_Queue(mbox,
&ret_msg,
1,
&actual_size,
timeout ? MS_TO_TICKS(timeout) : NU_SUSPEND);
if (status == NU_SUCCESS)
{
if (msg)
*msg = ret_msg;
DEBUGF(SYS_DEBUG, ("sys_mbox_fetch: mbox %p msg %p\n", mbox, ret_msg));
} else {
if (msg)
*msg = 0;
if (status == NU_TIMEOUT)
DEBUGF(SYS_DEBUG, ("sys_mbox_fetch: timeout on mbox %p\n", mbox));
else
DEBUGF(SYS_DEBUG, ("sys_mbox_fetch: Queue Error %i on mbox %p\n", status, mbox));
}
/* This next statement takes wraparound into account. It works. Really! */
timespent = TICKS_TO_MS(((s32_t) ((s32_t) NU_Retrieve_Clock() - (s32_t) timestart)));
if (status == NU_TIMEOUT)
return 0;
else
return timespent ? timespent : 1;
}
/*---------------------------------------------------------------------------------*/
static void
sys_arch_lisr(INT vector_number)
{
struct sys_hisr *p_hisr = hisrs;
/* Determine which HISR to activate */
while (p_hisr != NULL)
{
if (vector_number == p_hisr->vector)
{
if (p_hisr->disablefun)
(*p_hisr->disablefun)();
NU_Activate_HISR(p_hisr->hisr);
break;
}
p_hisr = p_hisr->next;
}
return;
}
/*---------------------------------------------------------------------------------*/
void
sys_setvect(u32_t vector, void (*isr_function)(void), void (*dis_funct)(void))
{
/* The passed function is called as a high level ISR on the selected vector.
It is assumed that all the functions in this module can be called by the
isr_function.
*/
struct sys_hisr *p_hisr = hisrs;
INT old_level;
NU_HISR *nucleus_hisr;
u8_t *p_stack;
STATUS status;
char hisr_name[8] = " ";
void (*old_lisr)(INT);
/* In this case a Nucleus HISR is created for the isr_function. This
* requires it's own stack. Also get memory for Nucleus HISR. */
nucleus_hisr = (NU_HISR *) calloc(1,sizeof(NU_HISR));
if (nucleus_hisr)
{
p_stack = (u8_t *) malloc(SYS_HISR_STACK_SIZE);
if (p_stack)
{
/* It is most efficient to disable interrupts for Nucleus for a short
time. Chances are we are doing this while interrupts are disabled
already during system initialization.
*/
old_level = NU_Control_Interrupts(NU_DISABLE_INTERRUPTS);
/* It is a simplification here that once an HISR is set up for a particular
* vector it will never be set up again. This way if the init code is called
* more than once it is harmless (no memory leaks)
*/
while (p_hisr != NULL)
{
if (vector == p_hisr->vector)
{
NU_Control_Interrupts(old_level);
free(p_stack);
free(nucleus_hisr);
return;
}
p_hisr = p_hisr->next;
}
/* Get a sys_hisr structure */
p_hisr = (struct sys_hisr *) calloc(1,sizeof(struct sys_hisr));
if (p_hisr)
{
p_hisr->next = hisrs;
p_hisr->vector = vector;
p_hisr->hisr = nucleus_hisr;
p_hisr->disablefun = dis_funct;
hisrs = p_hisr;
NU_Control_Interrupts(old_level);
num_hisr = (num_hisr + 1) % 100;
sprintf(hisr_name, "lwip%02d", num_hisr);
hisr_name[strlen(hisr_name)] = ' ';
/* Ask Nucleus to create the HISR */
status = NU_Create_HISR(p_hisr->hisr,
hisr_name,
isr_function,
1, //Priority 0-2
p_stack,
SYS_HISR_STACK_SIZE);
if (status == NU_SUCCESS)
{
/* Plug vector with system lisr now */
NU_Register_LISR(vector, sys_arch_lisr, &old_lisr);
return; //Success
}
}
NU_Control_Interrupts(old_level);
}
}
/* Errors should be logged here */
abort();
}
/*---------------------------------------------------------------------------------*/
u32_t
sys_arch_protect(void)
{
return NU_Control_Interrupts(NU_DISABLE_INTERRUPTS);
}
/*---------------------------------------------------------------------------------*/
void
sys_arch_unprotect(u32_t pval)
{
NU_Control_Interrupts(pval);
}
/*********************************************************************
* void sys_get_eth_addr(struct eth_addr *eth_addr)
*
* Get configured ethernet address from nvram and return it
* in a eth_addr structure.
*********************************************************************/
void
sys_get_eth_addr(struct eth_addr *eth_addr)
{
Cfg_lan *p_lan = config_get_lan_setup();
eth_addr->addr[0] = (u8_t) ((p_lan->etheraddrhi >> 16) & 0xff);
eth_addr->addr[1] = (u8_t) ((p_lan->etheraddrhi >> 8) & 0xff);
eth_addr->addr[2] = (u8_t) ((p_lan->etheraddrhi) & 0xff);
eth_addr->addr[3] = (u8_t) ((p_lan->etheraddrlo >> 16) & 0xff);
eth_addr->addr[4] = (u8_t) ((p_lan->etheraddrlo >> 8) & 0xff);
eth_addr->addr[5] = (u8_t) ((p_lan->etheraddrlo) & 0xff);
}