--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2001,2003 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Events handling
+ *
+ * This module implements a common system for executing
+ * a user defined action calling a hook function.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+#ifndef KERN_EVENT_H
+#define KERN_EVENT_H
+
+#include "config.h"
+
+#ifdef CONFIG_KERNEL
+ #include "config_kern.h"
+ #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
+ #include "signal.h"
+ #endif
+#endif
+
+
+/* Forward decl */
+struct Process;
+
+
+/*! Event types */
+enum EventAction
+{
+ EVENT_IGNORE, /*!< No-op event */
+#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
+ EVENT_SIGNAL, /*!< Send a signal to a process */
+#endif
+ EVENT_SOFTINT /*!< Trigger a software interrupt (i.e.: call user hook) */
+};
+
+//! User defined callback type
+typedef void (*Hook)(void *);
+
+typedef struct Event
+{
+ enum EventAction action;
+ union
+ {
+#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
+ struct
+ {
+ struct Process *sig_proc; /* Process to be signalled */
+ sig_t sig_bit; /* Signal to send */
+ } Sig;
+#endif
+ struct
+ {
+ Hook func; /* Pointer to softint hook */
+ void *user_data; /* Data to be passed back to user hook */
+ } Int;
+ } Ev;
+} Event;
+
+
+/*! Initialize an event with a softint */
+#define INITEVENT_INT(e,f,u) \
+ ((e)->action = EVENT_SOFTINT,(e)->Ev.Int.func = (f), (e)->Ev.Int.user_data = (u))
+
+
+#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
+
+/*! Initialize an event with a signal */
+#define INITEVENT_SIG(e,p,s) \
+ ((e)->action = EVENT_SIGNAL,(e)->Ev.Sig.sig_proc = (p), (e)->Ev.Sig.sig_bit = (s))
+
+/*! Trigger an event */
+#define DOEVENT(e) \
+do { \
+ if ((e)->action == EVENT_SIGNAL) \
+ sig_signal((e)->Ev.Sig.sig_proc, (e)->Sig.sig_bit); \
+ else if ((e)->action == EVENT_SOFTINT) \
+ (e)->Ev.Int.func((e)->Ev.Int.user_data); \
+} while (0)
+
+/*! Trigger an event (to be used inside interrupts) */
+#define DOEVENT_INTR(e) \
+do { \
+ if ((e)->action == EVENT_SIGNAL) \
+ _sig_signal((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit); \
+ else if ((e)->action == EVENT_SOFTINT) \
+ (e)->Ev.Int.func((e)->Ev.Int.user_data); \
+} while (0)
+
+#else /* !CONFIG_KERN_SIGNALS */
+
+/*! Trigger an event */
+#define DOEVENT(e) \
+do { \
+ if ((e)->action == EVENT_SOFTINT) \
+ (e)->Ev.Int.func((e)->Ev.Int.user_data); \
+} while (0)
+
+/*! Trigger an event (to be used inside interrupts) */
+#define DOEVENT_INTR(e) \
+do { \
+ if ((e)->action == EVENT_SOFTINT) \
+ (e)->Ev.Int.func((e)->Ev.Int.user_data); \
+} while (0)
+
+#endif
+
+#endif /* KERN_EVENT_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2001,2003 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Interface to KFile virtual class
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+#ifndef KERN_KFILE_H
+#define KERN_KFILE_H
+
+#ifndef COMPILER_H
+#include <compiler.h>
+#endif
+
+struct _KFile;
+
+typedef size_t (*ReadFunc_t) (struct _KFile *fd, char *buf, size_t size);
+typedef size_t (*WriteFunc_t) (struct _KFile *fd, const char *buf, size_t size);
+typedef bool (*SeekFunc_t) (struct _KFile *fd, int32_t offset);
+typedef bool (*CloseFunc_t) (struct _KFile *fd);
+typedef bool (*OpenFunc_t) (struct _KFile *fd, const char *name, int mode);
+
+
+/* Context data for callback functions which operate on
+ * pseudo files.
+ */
+typedef struct _KFile
+{
+ ReadFunc_t Read;
+ WriteFunc_t Write;
+ SeekFunc_t Seek;
+/* OpenFunc_t Open; unused */
+ CloseFunc_t Close;
+
+ /* NOTE: these must _NOT_ be size_t on 16bit CPUs! */
+ uint32_t SeekPos;
+ uint32_t Size;
+} KFile;
+
+
+#endif /* KERN_KFILE_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Heap subsystem (public interface).
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#include "heap.h"
+
+/* NOTE: struct size must be a 2's power! */
+typedef struct _MemChunk
+{
+ struct _MemChunk *next;
+ size_t size;
+} MemChunk;
+
+static REGISTER MemChunk *FreeList; /* Head of the free list */
+static uint8_t Heap[HEAP_SIZE]; /* Heap memory block */
+
+
+
+void heap_init(void)
+{
+#ifdef _DEBUG
+ int i;
+
+ /* Fill the heap with a "DEAD" code to help debugging */
+ for (i = 0; i < HEAP_SIZE / sizeof (uint16_t); i++)
+ ((uint16_t *)Heap)[i] = MEM_FILL_CODE;
+#endif
+
+ /* Initialize heap with a single big chunk */
+ FreeList = (MemChunk *)Heap;
+ FreeList->next = NULL;
+ FreeList->size = sizeof(Heap);
+}
+
+
+void *heap_alloc(size_t size)
+{
+ MemChunk *chunk, *prev;
+
+ /* Round size up to the allocation granularity */
+ size = ROUND2(size, sizeof(MemChunk));
+
+ /* Walk on the free list looking for any chunk big enough to
+ * fit the requested block size.
+ */
+ for (prev = (MemChunk *)&FreeList, chunk = FreeList;
+ chunk;
+ prev = chunk, chunk = chunk->next)
+ {
+ if (chunk->size <= size)
+ {
+ if (chunk->size == size)
+ {
+ /* Just remove this chunk from the free list */
+ prev->next = chunk->next;
+ return (void *)chunk;
+ }
+ else
+ {
+ /* Allocate from the END of an existing chunk */
+ prev->size -= size;
+ return (void *)(((uint8_t *)prev) + prev->size);
+ }
+ }
+ }
+
+ return NULL; /* fail */
+}
+
+
+void heap_free(void *mem, size_t size)
+{
+ MemChunk *prev;
+
+ /* Round size up to the allocation granularity */
+ size = ROUND2(size, sizeof(MemChunk));
+
+ /* Special case: first chunk in the free list */
+ if (((uint8_t *)mem) < ((uint8_t *)FreeList))
+ {
+ /* Insert memory block before the current free list head */
+ prev = (MemChunk *)mem;
+ prev->next = FreeList;
+ prev->size = size;
+ FreeList = prev;
+ }
+ else /* Normal case: not the first chunk in the free list */
+ {
+ /* Walk on the free list. Stop at the insertion point */
+ prev = FreeList;
+ while ((prev->next >= ((MemChunk *)mem)) || (!prev->next))
+ prev = prev->next;
+
+ /* Should it be merged with previous block? */
+ if (((uint8_t *)prev) + prev->Size == ((uint8_t *)mem))
+ {
+ /* Yes */
+ prev->size += size;
+ }
+ else /* not merged with previous chunk */
+ {
+ /* insert it after the previous node
+ * and move the 'prev' pointer forward
+ * for the following operations
+ */
+ ((MemChunk *)mem)->next = prev->next;
+ prev->next = (MemChunk *)mem;
+ prev = (MemChunk *)mem;
+ }
+ }
+
+ /* Also merge with next chunk? */
+ if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
+ {
+ prev->size += prev->next->size;
+ prev->next = prev->next->next;
+ }
+}
+
+#ifdef __POSIX__
+
+/*! ANSI/POSIX-like malloc() implementation based on heap_alloc()/heap_free() */
+void *malloc(size_t size)
+{
+ void *mem;
+
+ size += sizeof(size_t);
+
+ if (mem = heap_alloc(size))
+ *((size_t *)mem)++ = size;
+
+ return mem;
+}
+
+
+/*! ANSI/POSIX-like calloc() implementation based on heap_alloc()/heap_free() */
+void *calloc(size_t nelem, size_t size)
+{
+ void *mem, *tmp;
+
+ size *= nelem;
+
+ if (mem = malloc(size))
+ {
+ tmp = mem;
+ while (size--)
+ ((uint8_t *)tmp++) = 0;
+ }
+
+ return mem;
+}
+
+/*! ANSI/POSIX-like free() implementation based on heap_alloc()/heap_free() */
+void free(void *mem)
+{
+ ((size_t *)mem)--;
+ heap_free(mem, *((size_t *)mem));
+}
+
+#endif /* __POSIX__ */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Heap subsystem (public interface).
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#ifndef KERN_HEAP_H
+#define KERN_HEAP_H
+
+#include "compiler.h"
+
+
+/* Memory allocation services */
+void heap_init(void);
+void *heap_alloc(size_t size);
+void heap_free(void *mem, size_t size);
+
+#ifdef __POSIX__ /* unused */
+void *malloc(size_t size);
+void *calloc(unsigned int nelem, size_t size);
+void free(void * mem);
+#endif /* __POSIX__ */
+
+#endif /* KERN_HEAP_H */
+
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Simple realtime multitasking scheduler.
+ * Context switching is only done cooperatively.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#include "cpu.h"
+#include "proc_p.h"
+#include "proc.h"
+#include "event.h"
+#include "hw.h"
+#include <drv/kdebug.h>
+
+#include <string.h> /* memset() */
+
+/* CPU dependent context switching routines */
+extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
+
+/*
+ * The scheduer tracks ready and waiting processes
+ * by enqueuing them in these lists. A pointer to the currently
+ * running process is stored in the CurrentProcess pointer.
+ *
+ * NOTE: these variables are protected by DI/EI locking
+ */
+REGISTER Process *CurrentProcess;
+REGISTER List ProcReadyList;
+
+#if CONFIG_KERN_PREEMPTIVE
+/*
+ * The time sharing scheduler forces a task switch when
+ * the current process has consumed its quantum.
+ */
+uint16_t Quantum;
+#endif
+
+
+/* In Win32 we must emulate stack on the real process stack */
+#if (ARCH & ARCH_EMUL)
+extern List StackFreeList;
+#endif
+
+/* The main process (the one that executes main()) */
+struct Process MainProcess;
+
+static void proc_init_struct(Process* proc)
+{
+#if CONFIG_KERN_TIMER
+ INITEVENT_SIG(&proc->proc_timer.expire, proc, SIG_SINGLE);
+#endif
+
+#if CONFIG_KERN_SIGNALS
+ proc->sig_recv = 0;
+#endif
+
+#if CONFIG_KERN_HEAP
+ proc->flags = 0;
+#endif
+}
+
+void proc_init(void)
+{
+ INITLIST(&ProcReadyList);
+
+ /* We "promote" the current context into a real process. The only thing we have
+ to do is create a PCB and make it current. We don't need to setup the stack
+ pointer because it will be written the first time we switch to another process. */
+ proc_init_struct(&MainProcess);
+ CurrentProcess = &MainProcess;
+}
+
+
+/*!
+ * Create a new process, starting at the provided entry point.
+ *
+ * \return Process structure of new created process
+ * if successful, NULL otherwise.
+ */
+Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base)
+{
+ Process *proc;
+ size_t i;
+ size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
+#if CONFIG_KERN_HEAP
+ bool free_stack = false;
+#endif
+
+#if (ARCH & ARCH_EMUL)
+ /* Ignore stack provided by caller
+ * and use the large enough default instead
+ */
+ stack_base = (cpustack_t *)StackFreeList.head;
+ REMOVE((Node *)stack_base);
+ stacksize = DEF_STACKSIZE;
+#elif CONFIG_KERN_HEAP
+ /* Did the caller provide a stack for us? */
+ if (!stack_base)
+ {
+ /* Did the caller specify the desired stack size? */
+ if (!stacksize)
+ stacksize = CONFIG_KERN_DEFSTACKSIZE + sizeof(Process);
+
+ /* Allocate stack dinamically */
+ if (!(stack_base = heap_alloc(stacksize)))
+ return NULL;
+
+ free_stack = true;
+ }
+#else
+ /* Stack must have been provided by the user */
+ ASSERT(stack_base);
+ ASSERT(stacksize);
+#endif
+
+#ifdef _DEBUG
+ /* Fill-in the stack with a special marker to help debugging */
+ memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t));
+#endif /* _DEBUG */
+
+ /* Initialize the process control block */
+ if (CPU_STACK_GROWS_UPWARD)
+ {
+ proc = (Process*)stack_base;
+ proc->stack = stack_base + proc_size_words;
+ if (CPU_SP_ON_EMPTY_SLOT)
+ proc->stack++;
+ }
+ else
+ {
+ proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words);
+ proc->stack = (cpustack_t*)proc;
+ if (CPU_SP_ON_EMPTY_SLOT)
+ proc->stack--;
+ }
+
+ proc_init_struct(proc);
+
+#if CONFIG_KERN_HEAP
+ proc->stack_base = stack_base;
+ proc->stack_size = stack_size;
+ if (free_stack)
+ proc->flags |= PF_FREESTACK;
+#endif
+
+ /* Initialize process stack frame */
+ CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
+ CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
+
+ /* Push a clean set of CPU registers for asm_switch_context() */
+ for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
+ CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
+
+ /* Add to ready list */
+ DISABLE_INTS;
+ SCHED_ENQUEUE(proc);
+ ENABLE_INTS;
+
+ return proc;
+}
+
+
+/*!
+ * System scheduler: pass CPU control to the next process in
+ * the ready queue.
+ *
+ * Saving and restoring the context on the stack is done
+ * by a CPU-dependent support routine which must usually be
+ * written in assembly.
+ */
+void proc_schedule(void)
+{
+ /* This function must not have any "auto" variables, otherwise
+ * the compiler might put them on the stack of the process
+ * being switched out.
+ */
+ static Process *old_process;
+
+ /* Remember old process to save its context later */
+ old_process = CurrentProcess;
+ CurrentProcess = NULL;
+
+ /* Poll on the ready queue for the first ready process
+ */
+ for(;;) /* forever */
+ {
+ /* Do CPU specific idle processing (ARGH, should be moved to the end of the loop!) */
+ SCHEDULER_IDLE;
+
+ DISABLE_INTS;
+ if (!ISLISTEMPTY(&ProcReadyList))
+ {
+ /* Get process from ready list */
+ CurrentProcess = (Process *)ProcReadyList.head;
+ REMOVE((Node *)CurrentProcess);
+ ENABLE_INTS;
+ break;
+ }
+ ENABLE_INTS;
+ }
+
+ /* Optimization: don't switch contexts when the active
+ * process has not changed.
+ */
+ if (CurrentProcess != old_process)
+ {
+ static cpustack_t* dummy;
+
+#if CONFIG_KERN_PREEMPTIVE
+ /* Reset quantum for this process */
+ Quantum = CONFIG_KERN_QUANTUM;
+#endif
+
+ /* Save context of old process and switch to new process. If there is no
+ * old process, we save the old stack pointer into a dummy variable that
+ * we ignore. In fact, this happens only when the old process has just
+ * exited.
+ * TODO: Instead of physically clearing the process at exit time, a zombie
+ * list should be created.
+ */
+ asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
+ }
+
+ /* This RET resumes the execution on the new process */
+}
+
+
+/*!
+ * Terminate the current process
+ */
+void proc_exit(void)
+{
+#if CONFIG_KERN_HEAP
+ /* The following code is BROKEN.
+ * We are freeing our own stack before entering proc_schedule()
+ * BAJO: A correct fix would be to rearrange the scheduler with
+ * an additional parameter which frees the old stack/process
+ * after a context switch.
+ */
+ if (CurrentProcess->flags & PF_FREESTACK)
+ heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
+ heap_free(CurrentProcess);
+#endif
+
+#if (ARCH & ARCH_EMUL)
+#error This is wrong
+ /* Reinsert process stack in free list */
+ ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
+ - (DEF_STACKSIZE / sizeof(cpustack_t))));
+
+ /* NOTE: At this point the first two words of what used
+ * to be our stack contain a list node. From now on, we
+ * rely on the compiler not reading/writing the stack.
+ */
+#endif /* ARCH_EMUL */
+
+ CurrentProcess = NULL;
+ proc_schedule();
+ /* not reached */
+}
+
+
+/*!
+ * Co-operative context switch
+ */
+void proc_switch(void)
+{
+ DISABLE_INTS;
+ SCHED_ENQUEUE(CurrentProcess);
+ ENABLE_INTS;
+ proc_schedule();
+}
+
+
+/*!
+ * Get the pointer to the current process
+ */
+struct Process *proc_current(void)
+{
+ return CurrentProcess;
+}
+
+
+#if 0 /* Simple testcase for the scheduler */
+
+/*!
+ * Proc scheduling test subthread 1
+ */
+static void NORETURN proc_test_thread1(void)
+{
+ for (;;)
+ {
+ kputs(">task 1\n");
+ timer_delay(50);
+ proc_switch();
+ }
+}
+
+/*!
+ * Proc scheduling test subthread 2
+ */
+static void NORETURN proc_test_thread2(void)
+{
+ for (;;)
+ {
+ kputs(">task 2\n");
+ timer_delay(75);
+ proc_switch();
+ }
+}
+
+static cpustack_t proc_test_stack1[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
+static cpustack_t proc_test_stack2[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
+
+/*!
+ * Proc scheduling test
+ */
+void NORETURN proc_test(void)
+{
+ proc_new(proc_test_thread1, sizeof(proc_test_stack1), proc_test_stack1);
+ proc_new(proc_test_thread2, sizeof(proc_test_stack2), proc_test_stack2);
+ kputs("Created tasks\n");
+
+ kputs("stack1:\n");
+ kdump(proc_test_stack1+sizeof(proc_test_stack1)-64, 64);
+ kputs("stack2:\n");
+ kdump(proc_test_stack2+sizeof(proc_test_stack1)-64, 64);
+
+ for (;;)
+ {
+ kputs(">main task\n");
+ timer_delay(93);
+ proc_switch();
+ }
+
+ ASSERT(false);
+}
+#endif
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Process scheduler (public interface).
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#ifndef KERN_PROC_H
+#define KERN_PROC_H
+
+#include "compiler.h"
+#include "cpu.h"
+#include "config_kern.h"
+
+/* Fwd decl */
+struct Process;
+
+/* Task scheduling services */
+void proc_init(void);
+struct Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack);
+void proc_exit(void);
+void proc_switch(void);
+void proc_test(void);
+struct Process* proc_current(void);
+
+#if CONFIG_KERN_PREEMPTIVE
+ #define FORBID proc_forbid()
+ #define PERMIT proc_permit()
+#else
+ #define FORBID
+ #define PERMIT
+#endif
+
+#endif /* KERN_PROC_H */
+
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Internal scheduler structures and definitions for processes.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ * Revision 1.3 2004/05/14 12:52:13 rasky
+ * Importato supporto kernel per AVR da Stefano
+ *
+ * Revision 1.2 2004/04/28 16:13:49 rasky
+ * proc_schedule() is now semi-private (used only within the kernel)
+ *
+ * Revision 1.1 2004/04/26 18:02:40 rasky
+ * Importato microkernel
+ *
+ * Revision 1.1 2004/04/04 17:40:26 aleph
+ * Add multithreading kernel
+ *
+ */
+
+#ifndef KERN_PROC_P_H
+#define KERN_PROC_P_H
+
+#include "compiler.h"
+#include "config.h"
+#include "config_kern.h"
+#include <mware/list.h>
+#include <drv/timer.h>
+
+
+typedef struct Process
+{
+ Node link; /*!< Link Process into scheduler lists */
+ cpustack_t *stack; /*!< Per-process SP */
+
+#if CONFIG_KERN_SIGNALS
+ sigset_t sig_wait; /*!< Signals the process is waiting for */
+ sigset_t sig_recv; /*!< Received signals */
+#endif
+
+#if CONFIG_KERN_TIMER
+ struct Timer proc_timer; /*!< Process own timer */
+#endif
+
+#if CONFIG_KERN_HEAP
+ uint16_t flags; /*!< Flags */
+ cpustack_t *stack_base; /*!< Base of process stack */
+ size_t stack_size; /*!< Size of process stack */
+#endif
+} Process;
+
+
+/*!
+ * \name Flags for Process.flags
+ * \{
+ */
+#define PF_FREESTACK BV(0) /*!< Free the stack when process dies */
+/*\}*/
+
+
+/*! Track running processes */
+extern REGISTER Process *CurrentProcess;
+
+/*! Track ready processes */
+extern REGISTER List ProcReadyList;
+
+
+/*!
+ * Enqueue a task in the ready list
+ */
+#define SCHED_ENQUEUE(proc) ADDTAIL(&ProcReadyList, &(proc)->link)
+
+/*! Schedule to another process *without* adding the current to the ready list */
+void proc_schedule(void);
+
+#endif /* KERN_PROC_P_H */
+
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Semaphore based synchronization services.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#include "sem.h"
+#include "proc.h"
+#include "proc_p.h"
+#include "signal.h"
+#include "hw.h"
+
+
+/*!
+ * \brief Initialize a Semaphore structure
+ */
+void sem_init(struct Semaphore *s)
+{
+ INITLIST(&s->wait_queue);
+ s->owner = NULL;
+ s->nest_count = 0;
+}
+
+
+/*!
+ * \brief Attempt to lock a semaphore without waiting.
+ *
+ * \return true in case of success, false if the semaphore
+ * was already locked by someone else.
+ *
+ * \note each call to sem_attempt() must be matched by a
+ * call to sem_release().
+ */
+bool sem_attempt(struct Semaphore *s)
+{
+ DISABLE_INTS;
+ if ((!s->owner) || (s->owner == CurrentProcess))
+ {
+ s->owner = CurrentProcess;
+ s->nest_count++;
+ ENABLE_INTS;
+ return true;
+ }
+ ENABLE_INTS;
+ return false;
+}
+
+
+/*!
+ * \brief Lock a semaphore.
+ *
+ * If the semaphore is already owned by another process, the caller
+ * process will be enqueued into the waiting list and sleep until
+ * the semaphore is available.
+ *
+ * \note Each call to sem_obtain() must be matched by a
+ * call to sem_release().
+ *
+ * \note This routine is optimized for highest speed in
+ * the most common case: the semaphore is free or locked
+ * by the calling process itself. Rearranging this code
+ * is probably a bad idea.
+ */
+void sem_obtain(struct Semaphore *s)
+{
+ DISABLE_INTS;
+
+ /* Is the semaphore already locked by another process? */
+ if (s->owner && (s->owner != CurrentProcess))
+ {
+ /* Append calling process to the wait queue */
+ ADDTAIL(&s->wait_queue, (Node *)CurrentProcess);
+ ENABLE_INTS;
+
+ /* We will awake only when the current owner calls
+ * ReleaseSemaphore(). Then, the semaphore will already
+ * be locked for us.
+ */
+ proc_schedule();
+ }
+ else
+ {
+ /* The semaphore is free: lock it */
+ s->owner = CurrentProcess;
+ s->nest_count++;
+ ENABLE_INTS;
+ }
+}
+
+
+/*!
+ * \brief Releases a lock on a previously locked semaphore.
+ *
+ * If the nesting count of the semaphore reaches zero,
+ * the next process waiting for it will be awaken.
+ *
+ * \note This routine is optimized for highest speed in
+ * the most common case: the semaphore has been locked just
+ * once and nobody else was waiting for it. Rearranging
+ * this code is probably a bad idea.
+ */
+void sem_release(struct Semaphore *s)
+{
+ DISABLE_INTS;
+
+ /* Decremement nesting count and check if the semaphore
+ * has been fully unlocked
+ */
+ if (--s->nest_count == 0)
+ {
+ /* Disown semaphore */
+ s->owner = NULL;
+
+ /* Anybody still waiting for this semaphore? */
+ if (!ISLISTEMPTY(&s->wait_queue))
+ {
+ /* Give semaphore to the first applicant */
+ Process *proc = (Process *)s->wait_queue.head;
+ REMOVE((Node *)proc);
+ s->nest_count = 1;
+ s->owner = proc;
+ SCHED_ENQUEUE(proc);
+ }
+ }
+
+ ENABLE_INTS;
+}
+
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Mutually exclusive semaphores.
+ * Shared locking not supported in this implementation.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#ifndef KERN_SEM_H
+#define KERN_SEM_H
+
+#include "compiler.h"
+#include <mware/list.h>
+
+/* Fwd decl */
+struct Process;
+
+
+struct Semaphore
+{
+ struct Process *owner;
+ List wait_queue;
+ int nest_count;
+};
+
+/*!
+ * \name Process synchronization services
+ * \{
+ */
+void sem_init(struct Semaphore *s);
+bool sem_attempt(struct Semaphore *s);
+void sem_obtain(struct Semaphore *s);
+void sem_release(struct Semaphore *s);
+/* \} */
+
+#endif /* KERN_SEM_H */
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief IPC signals implementation.
+ *
+ * Each process can wait for just one signal.
+ * Multiple processes can wait for the same signal. In this
+ * case, each signal will wake-up one of them.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#include "signal.h"
+#include "proc.h"
+#include "proc_p.h"
+#include "hw.h"
+
+// FIXME
+#if CONFIG_KERN_SIGNALS
+
+/*!
+ * Check if any of the signals in \a sigs has occurred and clear them.
+ * Return the signals that have occurred.
+ */
+sigset_t sig_check(sigset_t sigs)
+{
+ sigset_t result;
+
+ DISABLE_INTS;
+ result = CurrentProcess->sig_recv & sigs;
+ CurrentProcess->sig_recv &= ~sigs;
+ ENABLE_INTS;
+ return result;
+}
+
+
+/*!
+ * Sleep until any of the signals in \a sigs occurs.
+ * Return the signal(s) that have awaked the process.
+ */
+sigset_t sig_wait(sigset_t sigs)
+{
+ sigset_t result;
+
+ DISABLE_INTS;
+
+ for(;;)
+ {
+ /* Check if we got at least one of the signals */
+ if ((result = CurrentProcess->sig_recv & sigs))
+ {
+ /* Yes, clear signals and return */
+ CurrentProcess->sig_recv &= ~sigs;
+ ENABLE_INTS;
+ return result;
+ }
+
+ /* No, go to sleep and proc_schedule() another process */
+ CurrentProcess->sig_wait = sigs;
+ proc_schedule();
+ }
+}
+
+
+/*!
+ * Send the signals \a sigs to the process \a proc.
+ * The process will be awaken if it was waiting for any of them.
+ *
+ * This call is interrupt safe (no \c DISABLE_INTS/ENABLE_INTS protection)
+ */
+void _sig_signal(Process *proc, sigset_t sigs)
+{
+ /* Set the signals */
+ proc->sig_recv |= sigs;
+
+ /* Check if process needs to be awaken */
+ if (proc->sig_recv & proc->sig_wait)
+ {
+ /* Wake up process and enqueue in ready list */
+ proc->sig_wait = 0;
+ SCHED_ENQUEUE(proc);
+ }
+}
+
+
+/*!
+ * Same as _sig_signal() with interrupt protection.
+ *
+ * \note Inlined manually because some compilers are too
+ * stupid to it automatically.
+ */
+void sig_signal(Process *proc, sigset_t sigs)
+{
+ DISABLE_INTS;
+
+ /* Set the signals */
+ proc->sig_recv |= sigs;
+
+ /* Check if process needs to be awaken */
+ if (proc->sig_recv & proc->sig_wait)
+ {
+ /* Wake up process and enqueue in ready list */
+ proc->sig_wait = 0;
+ SCHED_ENQUEUE(proc);
+ }
+
+ ENABLE_INTS;
+}
+
+#endif /* CONFIG_KERN_SIGNALS */
+
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Signal module (public interface).
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+#ifndef KERN_SIGNAL_H
+#define KERN_SIGNAL_H
+
+#include "compiler.h"
+
+
+/* Fwd decl */
+struct Process;
+
+/* Inter-process Communication services */
+sigset_t sig_check(sigset_t sigs);
+void sig_signal(struct Process *proc, sigset_t sig);
+void _sig_signal(struct Process *proc, sigset_t sig);
+sigset_t sig_wait(sigset_t sigs);
+
+
+/*!
+ * \name Signal definitions
+ *
+ * \{
+ */
+#define SIG_USER0 BV(0) /*!< Free for user usage */
+#define SIG_USER1 BV(1) /*!< Free for user usage */
+#define SIG_USER2 BV(2) /*!< Free for user usage */
+#define SIG_USER3 BV(3) /*!< Free for user usage */
+#define SIG_SYSTEM4 BV(4) /*!< Reserved for system use */
+#define SIG_SYSTEM5 BV(5) /*!< Reserved for system use */
+#define SIG_SYSTEM6 BV(6) /*!< Reserved for system use */
+#define SIG_SINGLE BV(7) /*!< Used to wait for a single event */
+/*\}*/
+
+#endif /* KERN_SIGNAL_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief AVR context switch
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+
+#include <avr/io.h>
+
+/* void asm_switch_context(void **new_sp, void **save_sp) */
+.globl asm_switch_context
+asm_switch_context:
+
+; push r0 caller-save
+; push r1 caller-save
+ push r2
+ push r3
+ push r4
+ push r5
+ push r6
+ push r7
+ push r8
+ push r9
+ push r10
+ push r11
+ push r12
+ push r13
+ push r14
+ push r15
+ push r16
+ push r17
+; push r18 caller-save
+; push r19 caller-save
+; push r20 caller-save
+; push r21 caller-save
+; push r22 caller-save
+; push r23 caller-save
+; push r24 caller-save
+; push r25 caller-save
+; push r26 caller-save
+; push r27 caller-save
+ push r28
+ push r29
+; push r30 caller-save
+; push r31 caller-save
+
+; First parameter (new_sp) is in r24:r25, second (save_sp) in r22:r23
+
+ in r0,SPL-__SFR_OFFSET ; r0:r1 = SP
+ in r1,SPH-__SFR_OFFSET
+ movw r26,r22 ; X = save_sp
+ st X+,r0 ; *save_sp = SP
+ st X,r1
+ movw r26,r24 ; X = new_sp
+ ld r0,X+
+ ld r1,X
+ out SPL-__SFR_OFFSET,r0 ; SP = *new_sp
+ out SPH-__SFR_OFFSET,r1
+
+; pop r31 caller-save
+; pop r30 caller-save
+ pop r29
+ pop r28
+; pop r27 caller-save
+; pop r26 caller-save
+; pop r25 caller-save
+; pop r24 caller-save
+; pop r23 caller-save
+; pop r22 caller-save
+; pop r21 caller-save
+; pop r20 caller-save
+; pop r19 caller-save
+; pop r18 caller-save
+ pop r17
+ pop r16
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop r7
+ pop r6
+ pop r5
+ pop r4
+ pop r3
+ pop r2
+; pop r1 caller-save
+; pop r0 caller-save
+
+ ret
--- /dev/null
+/*!
+ * \file
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ *
+ * \version $Id$
+ *
+ * \author Giovanni Bajo <rasky@develer.com>
+ *
+ * \brief DSP5680x task switching support
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+void asm_switch_context(void ** new_sp/*R2*/, void ** save_sp/*R3*/);
+asm void asm_switch_context(void ** new_sp, void ** save_sp)
+{
+ lea (SP)+
+ move n,x:(SP)+
+ move x0,x:(SP)+
+ move y0,x:(SP)+
+ move y1,x:(SP)+
+ move a0,x:(SP)+
+ move a1,x:(SP)+
+ move a2,x:(SP)+
+ move b0,x:(SP)+
+ move b1,x:(SP)+
+ move b2,x:(SP)+
+ move r0,x:(SP)+
+ move r1,x:(SP)+
+ move r2,x:(SP)+
+ move r3,x:(SP)+
+
+ move omr,x:(SP)+
+ move la,x:(SP)+
+ move m01,x:(SP)+
+ move lc,x:(SP)+
+
+ ;
+ ; save hardware stack
+ ;
+ move hws,x:(SP)+
+ move hws,x:(SP)+
+
+ ; From the manual:
+ ; The compiler uses page 0 address locations X: 0x0030 - 0x003F as register
+ ; variables. Frequently accessed local variables are assigned to the page 0
+ ; registers instead of to stack locations so that load and store instructions
+ ; are shortened. Addresses X: 0x0030 - 0x0037 (page 0 registers MR0-MR7) are
+ ; volatile registers and can be overwritten. The remaining registers (page 0
+ ; registers MR8-MR15) are treated as non-volatile and, if used by a routine,
+ ; must be saved on entry and restored on exit.
+ ;
+ ; So, register 0x30-0x37 are caller-save, while 0x38-0x3F are callee-save.
+ move x:<$38,y1
+ move y1,x:(SP)+
+ move x:<$39,y1
+ move y1,x:(SP)+
+ move x:<$3A,y1
+ move y1,x:(SP)+
+ move x:<$3B,y1
+ move y1,x:(SP)+
+ move x:<$3C,y1
+ move y1,x:(SP)+
+ move x:<$3D,y1
+ move y1,x:(SP)+
+ move x:<$3E,y1
+ move y1,x:(SP)+
+ move x:<$3F,y1
+ move y1,x:(SP)
+
+ ;
+ ; 28 words have been pushed on the stack.
+ nop
+ move SP, x:(R3)
+ nop
+ move x:(R2), SP
+ nop
+
+ pop y1
+ move y1,x:<$3F
+ pop y1
+ move y1,x:<$3E
+ pop y1
+ move y1,x:<$3D
+ pop y1
+ move y1,x:<$3C
+ pop y1
+ move y1,x:<$3B
+ pop y1
+ move y1,x:<$3A
+ pop y1
+ move y1,x:<$39
+ pop y1
+ move y1,x:<$38
+
+ ;
+ ; restore hardware stack
+ ;
+ move hws,la ; Clear HWS to ensure proper reload
+ move hws,la
+ pop HWS
+ pop HWS
+
+ ;
+ ; restore all saved registers
+ ;
+ pop lc
+ pop m01
+ pop la
+ pop omr
+
+ pop r3
+ pop r2
+ pop r1
+ pop r0
+ pop b2
+ pop b1
+ pop b0
+ pop a2
+ pop a1
+ pop a0
+
+ pop y1
+ pop y0
+ pop x0
+
+ pop n
+
+ rts
+}
--- /dev/null
+;* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+;* Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+;*
+;* \version $Id$
+;*
+;* \author Bernardo Innocenti <bernie@develer.com>
+;*
+
+;* $Log$
+;* Revision 1.1 2004/05/23 17:27:00 bernie
+;* Import kern/ subdirectory.
+;*
+;*
+
+!!!!!! THIS FILE HAS NOT BEEN REVISED FOR THE NEW SCHEDULER API !!!!!!
+
+
+
+ NAME AsmSwitch
+ RSEG CODE
+
+ EXTERN ?LR
+ EXTERN ?GR
+ PUBLIC AsmSwitchContext
+ PUBLIC AsmReplaceContext
+
+
+;* Perform low-level process context switching
+;*
+;* void AsmSwitchContext(cpustack_t *new_sp, cpustack_t **save_sp)
+;* GR+0 SP+2
+;*
+;* Replace current context with new process
+;*
+;* void AsmReplaceContext(cpustack_t *new_sp, cpustack_t **dummy)
+;* GR+0 SP+2
+;*
+
+AsmSwitchContext:
+
+; pop 2nd parameter from the stack
+ ld ?GR+2,2[SP]
+
+; save all registers
+ push ?LR+0
+ push ?LR+2
+ push ?LR+4
+ push ?LR+6
+ push ?LR+8
+ push ?LR+10
+ push ?LR+12
+ push ?LR+14
+ push ?LR+16
+ push ?LR+18
+ push ?LR+20
+ push ?LR+22
+ push ?LR+24
+ push ?LR+26
+ push ?LR+28
+ push ?LR+30
+ st SP,[?GR+2] ; save old stack pointer
+ ; fall-thru
+
+AsmReplaceContext:
+ ld SP,?GR+0 ; load new stack pointer
+
+; restore all registers
+ pop ?LR+30
+ pop ?LR+28
+ pop ?LR+26
+ pop ?LR+24
+ pop ?LR+22
+ pop ?LR+20
+ pop ?LR+18
+ pop ?LR+16
+ pop ?LR+14
+ pop ?LR+12
+ pop ?LR+10
+ pop ?LR+8
+ pop ?LR+6
+ pop ?LR+4
+ pop ?LR+2
+ pop ?LR+0
+
+; restore execution in new context
+ ret
+
+ END
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief i386 context switch
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+ *
+ * $Log$
+ * Revision 1.1 2004/05/23 17:27:00 bernie
+ * Import kern/ subdirectory.
+ *
+ */
+
+!!!!!! THIS FILE HAS NOT BEEN REVISED FOR THE NEW SCHEDULER API !!!!!!
+
+/* I know it's ugly... */
+#.intel_syntax
+
+/* void AsmSwitchContext(void * new_sp, void ** save_sp) */
+.globl AsmSwitchContext
+AsmSwitchContext:
+ pushl %eax
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ movl 0x24(%esp),%ebp /* ebp = save_sp */
+ movl %esp,(%ebp) /* *save_sp = esp */
+ movl 0x20(%esp),%esp /* esp = new_sp */
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %edx
+ popl %ecx
+ popl %ebx
+ popl %eax
+ ret
+
+/* void AsmReplaceContext(void * new_sp, void ** dummy) */
+.globl AsmReplaceContext
+AsmReplaceContext:
+ movl 4(%esp),%esp /* esp = new_sp */
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %edx
+ popl %ecx
+ popl %ebx
+ popl %eax
+ ret
+
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief i386 context switch for WIN32
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+__declspec(naked) void AsmSwitchContext(void * new_sp, void ** save_sp)
+{
+ __asm
+ {
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push esi
+ push edi
+ push ebp
+ mov ebp,dword ptr [esp+24h] ; ebp <- save_sp
+ mov dword ptr [ebp],esp ; *save_sp = esp
+ mov esp,dword ptr [esp+20h] ; new_sp
+ pop ebp
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ ret
+ }
+}
+
+__declspec(naked) void AsmReplaceContext(void * new_sp, void ** dummy)
+{
+ __asm
+ {
+ mov esp,dword ptr [esp + 4] ; new_sp
+ pop ebp
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ ret
+ }
+}
+