From 1ea7355d85e316aadfd90468b3e808bb3dc95ee9 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sun, 16 Aug 2009 13:07:24 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1073 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/kernel/include/ch.h | 89 ++++++++++ os/kernel/include/channels.h | 292 ++++++++++++++++++++++++++++++ os/kernel/include/condvars.h | 80 +++++++++ os/kernel/include/debug.h | 149 ++++++++++++++++ os/kernel/include/events.h | 145 +++++++++++++++ os/kernel/include/heap.h | 43 +++++ os/kernel/include/inline.h | 72 ++++++++ os/kernel/include/lists.h | 92 ++++++++++ os/kernel/include/mailboxes.h | 126 +++++++++++++ os/kernel/include/mempools.h | 82 +++++++++ os/kernel/include/messages.h | 59 +++++++ os/kernel/include/mutexes.h | 84 +++++++++ os/kernel/include/queues.h | 218 +++++++++++++++++++++++ os/kernel/include/scheduler.h | 110 ++++++++++++ os/kernel/include/semaphores.h | 99 +++++++++++ os/kernel/include/serial.h | 217 +++++++++++++++++++++++ os/kernel/include/sys.h | 184 +++++++++++++++++++ os/kernel/include/threads.h | 269 ++++++++++++++++++++++++++++ os/kernel/include/vt.h | 125 +++++++++++++ os/kernel/kernel.mk | 13 ++ os/kernel/readme.txt | 1 + os/kernel/src/chcond.c | 227 ++++++++++++++++++++++++ os/kernel/src/chdebug.c | 81 +++++++++ os/kernel/src/chevents.c | 392 +++++++++++++++++++++++++++++++++++++++++ os/kernel/src/chheap.c | 276 +++++++++++++++++++++++++++++ os/kernel/src/chlists.c | 111 ++++++++++++ os/kernel/src/chmboxes.c | 244 +++++++++++++++++++++++++ os/kernel/src/chmempools.c | 112 ++++++++++++ os/kernel/src/chmsg.c | 125 +++++++++++++ os/kernel/src/chmtx.c | 299 +++++++++++++++++++++++++++++++ os/kernel/src/chqueues.c | 304 ++++++++++++++++++++++++++++++++ os/kernel/src/chschd.c | 242 +++++++++++++++++++++++++ os/kernel/src/chsem.c | 257 +++++++++++++++++++++++++++ os/kernel/src/chserial.c | 168 ++++++++++++++++++ os/kernel/src/chsys.c | 129 ++++++++++++++ os/kernel/src/chthreads.c | 381 +++++++++++++++++++++++++++++++++++++++ os/kernel/src/chvt.c | 116 ++++++++++++ 37 files changed, 6013 insertions(+) create mode 100644 os/kernel/include/ch.h create mode 100644 os/kernel/include/channels.h create mode 100644 os/kernel/include/condvars.h create mode 100644 os/kernel/include/debug.h create mode 100644 os/kernel/include/events.h create mode 100644 os/kernel/include/heap.h create mode 100644 os/kernel/include/inline.h create mode 100644 os/kernel/include/lists.h create mode 100644 os/kernel/include/mailboxes.h create mode 100644 os/kernel/include/mempools.h create mode 100644 os/kernel/include/messages.h create mode 100644 os/kernel/include/mutexes.h create mode 100644 os/kernel/include/queues.h create mode 100644 os/kernel/include/scheduler.h create mode 100644 os/kernel/include/semaphores.h create mode 100644 os/kernel/include/serial.h create mode 100644 os/kernel/include/sys.h create mode 100644 os/kernel/include/threads.h create mode 100644 os/kernel/include/vt.h create mode 100644 os/kernel/kernel.mk create mode 100644 os/kernel/readme.txt create mode 100644 os/kernel/src/chcond.c create mode 100644 os/kernel/src/chdebug.c create mode 100644 os/kernel/src/chevents.c create mode 100644 os/kernel/src/chheap.c create mode 100644 os/kernel/src/chlists.c create mode 100644 os/kernel/src/chmboxes.c create mode 100644 os/kernel/src/chmempools.c create mode 100644 os/kernel/src/chmsg.c create mode 100644 os/kernel/src/chmtx.c create mode 100644 os/kernel/src/chqueues.c create mode 100644 os/kernel/src/chschd.c create mode 100644 os/kernel/src/chsem.c create mode 100644 os/kernel/src/chserial.c create mode 100644 os/kernel/src/chsys.c create mode 100644 os/kernel/src/chthreads.c create mode 100644 os/kernel/src/chvt.c (limited to 'os/kernel') diff --git a/os/kernel/include/ch.h b/os/kernel/include/ch.h new file mode 100644 index 000000000..0584b167e --- /dev/null +++ b/os/kernel/include/ch.h @@ -0,0 +1,89 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file ch.h + * @brief ChibiOS/RT main include file, it includes everything else. + * @addtogroup Kernel + * @{ + */ + +#ifndef _CH_H_ +#define _CH_H_ + +/** + * ChibiOS/RT identification macro. + */ +#define _CHIBIOS_RT_ + +/** + * Kernel version string. + */ +#define CH_KERNEL_VERSION "1.3.2unstable" + +/** + * Kernel version major number. + */ +#define CH_KERNEL_MAJOR 1 + +/** + * Kernel version minor number. + */ +#define CH_KERNEL_MINOR 3 + +/** + * Kernel version patch number. + */ +#define CH_KERNEL_PATCH 2 + +/* + * Common values. + */ +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#include +#include +#include "lists.h" +#include +#include "sys.h" +#include "vt.h" +#include "scheduler.h" +#include "semaphores.h" +#include "mutexes.h" +#include "condvars.h" +#include "events.h" +#include "messages.h" +#include "mailboxes.h" +#include "heap.h" +#include "mempools.h" +#include "threads.h" +#include "inline.h" +#include "queues.h" +#include "channels.h" +#include "serial.h" +#include "debug.h" + +#endif /* _CH_H_ */ + +/** @} */ diff --git a/os/kernel/include/channels.h b/os/kernel/include/channels.h new file mode 100644 index 000000000..a1c538f62 --- /dev/null +++ b/os/kernel/include/channels.h @@ -0,0 +1,292 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file channels.h + * @brief I/O channels + * @addtogroup Channels + * @{ + */ + +#ifndef _CHANNELS_H_ +#define _CHANNELS_H_ + +/** + * @brief @p BaseChannel specific methods. + */ +struct _base_channel_methods { + /** + * @brief Channel output check. + * @see chIOPutWouldBlock() + */ + bool_t (*putwouldblock)(void *instance); + /** + * @brief Channel input check. + * @see chIOGetWouldBlock() + */ + bool_t (*getwouldblock)(void *instance); + /** + * @brief Channel put method with timeout specification. + * @see chIOPut() + */ + msg_t (*put)(void *instance, uint8_t b, systime_t timeout); + /** + * @brief Channel get method with timeout specification. + * @see chIOGet() + */ + msg_t (*get)(void *instance, systime_t timeout); +}; + +/** + * @brief @p BaseChannel specific data. + * @note It is empty because @p BaseChannel is only an interface without + * implementation. + */ +struct _base_channel_data { +}; + +/** + * @brief @p BaseChannel virtual methods table. + */ +struct BaseChannelVMT { + /** + * @p BaseChannel class specific methods. + */ + struct _base_channel_methods m0; +}; + +/** + * @brief Base channel class. + * @details This class represents a generic, byte-wide, I/O channel. + */ +typedef struct { + /** + * Virtual Methods Table. + */ + const struct BaseChannelVMT *vmt; + /** + * @p BaseChannel class specific data. + */ + struct _base_channel_data d0; +} BaseChannel; + +/** + * @brief Channel output check. + * @details This function verifies if a subsequent @p chIOPut() would block. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @return The output queue status: + * @retval FALSE if the output queue has space and would not block a write + * operation. + * @retval TRUE if the output queue is full and would block a write operation. + */ +#define chIOPutWouldBlock(ip) ((ip)->vmt->m0.putwouldblock(ip)) + +/** + * @brief Channel input check. + * @details This function verifies if a subsequent @p chIOGett() would block. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @return The input queue status: + * @retval FALSE if the input queue contains data and would not block a read + * operation. + * @retval TRUE if the input queue is empty and would block a read operation. + */ +#define chIOGetWouldBlock(ip) ((ip)->vmt->m0.getwouldblock(ip)) + +/** + * @brief Channel blocking byte write. + * @details This function writes a byte value to a channel. If the channel + * is not ready to accept data then the calling thread is suspended. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @param[in] b the byte value to be written to the channel + * @return The operation status: + * @retval Q_OK if the operation succeeded. + * @retval Q_RESET if the channel associated queue (if any) was reset. + */ +#define chIOPut(ip, b) ((ip)->vmt->m0.put(ip, b, TIME_INFINITE)) + +/** + * @brief Channel blocking byte write with timeout. + * @details This function writes a byte value to a channel. If the channel + * is not ready to accept data then the calling thread is suspended. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @param[in] b the byte value to be written to the channel + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status: + * @retval Q_OK if the operation succeeded. + * @retval Q_TIMEOUT if the specified time expired. + * @retval Q_RESET if the channel associated queue (if any) was reset. + */ +#define chIOPutTimeout(ip, b, timeout) ((ip)->vmt->m0.put(ip, b, timeout)) + +/** + * @brief Channel blocking byte read. + * @details This function reads a byte value from a channel. If the data + * is not available then the calling thread is suspended. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @return A byte value from the queue or: + * @retval Q_RESET if the channel associated queue (if any) was reset. + */ +#define chIOGet(ip) ((ip)->vmt->m0.get(ip, TIME_INFINITE)) + +/** + * @brief Channel blocking byte read with timeout. + * @details This function reads a byte value from a channel. If the data + * is not available then the calling thread is suspended. + * + * @param[in] ip pointer to a @p BaseChannel or derived class + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return A byte value from the queue or: + * @retval Q_TIMEOUT if the specified time expired. + * @retval Q_RESET if the channel associated queue (if any) was reset. + */ +#define chIOGetTimeout(ip, timeout) ((ip)->vmt->m0.get(ip, timeout)) + +#if CH_USE_EVENTS +/** + * @brief @p BaseAsynchronousChannel specific methods. + */ +struct _base_asynchronous_channel_methods { + /** + * Channel asynchronous write method. + * @see chIOWrite() + */ + size_t (*write)(void *instance, uint8_t *buffer, size_t n); + /** + * Channel asynchronous read method. + * @see chIORead() + */ + size_t (*read)(void *instance, uint8_t *buffer, size_t n); +}; + +/** + * @brief @p BaseAsynchronousChannel specific data. + */ +struct _base_asynchronous_channel_data { + /** + * Data Available @p EventSource. This event is generated when some incoming + * data is inserted in the input queue. + */ + EventSource ievent; + /** + * Data Transmitted @p EventSource. This event is generated when the + * output queue is empty. + */ + EventSource oevent; +}; + +/** + * @brief @p BaseAsynchronousChannel virtual methods table. + */ +struct BaseAsynchronousChannelVMT { + /** + * @p BaseChannel class inherited methods. + */ + struct _base_channel_methods m0; + /** + * @p BaseAsynchronousChannel class specific methods. + */ + struct _base_asynchronous_channel_methods m1; +}; + +/** + * @extends BaseChannel + * + * @brief Base asynchronous channel class. + * @details This class extends @p BaseChannel by adding methods for + * asynchronous I/O in an event-driven environment. + */ +typedef struct { + /** + * Virtual Methods Table. + */ + const struct BaseAsynchronousChannelVMT *vmt; + /** + * @p BaseChannel class inherited data. + */ + struct _base_channel_data d0; + /** + * @p BaseAsynchronousChannel class specific data. + */ + struct _base_asynchronous_channel_data d1; +} BaseAsynchronousChannel; + +/** + * @brief Channel non-blocking write. + * @details The function writes data from a buffer to a channel. The + * transfer is non-blocking and can return zero if the channel is + * not read to accept data. + * + * @param[in] ip pointer to a @p BaseAsynchronousChannel or derived class + * @param[out] bp pointer to the buffer where the data is stored + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + */ +#define chIOWrite(ip, bp, n) ((ip)->vmt->m1.write(ip, bp, n)) + +/** + * @brief Channel non-blocking read. + * @details The function reads data from a channel into a buffer. The + * transfer is non-blocking and can return zero if the channel has + * no data immediately available. + * + * @param[in] ip pointer to a @p BaseAsynchronousChannel or derived class + * @param[out] bp pointer to the buffer where the input data is copied + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + */ +#define chIORead(ip, bp, n) ((ip)->vmt->m1.read(ip, bp, n)) + +/** + * @brief Returns the write event source. + * @details The write event source is broadcasted when the channel is ready + * for write operations. This usually happens when the internal + * output queue becomes empty. + * @param[in] ip pointer to a @p BaseAsynchronousChannel or derived class + * @return A pointer to an @p EventSource object. + */ +#define chIOGetWriteEventSource(ip) (&((ip)->vmt->d1.oevent)) + +/** + * @brief Returns the read event source. + * @details The read event source is broadcasted when the channel is ready + * for read operations. This usually happens when the internal + * input queue becomes non-empty. + * @param[in] ip pointer to a @p BaseAsynchronousChannel or derived class + * @return A pointer to an @p EventSource object. + */ +#define chIOGetReadEventSource(ip) (&((ip)->vmt->d1.ievent)) + +#endif /* CH_USE_EVENTS */ + +#endif /* _CHANNELS_H_ */ + +/** @} */ diff --git a/os/kernel/include/condvars.h b/os/kernel/include/condvars.h new file mode 100644 index 000000000..d68637f21 --- /dev/null +++ b/os/kernel/include/condvars.h @@ -0,0 +1,80 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + Concepts and parts of this file are contributed by and Copyright (C) 2008 + of Leon Woestenberg. + */ + +/** + * @file condvars.h + * @brief Condition Variables macros and structures. + * @addtogroup CondVars + * @{ + */ + +#ifndef _CONDVARS_H_ +#define _CONDVARS_H_ + +#if CH_USE_CONDVARS && CH_USE_MUTEXES + +/** + * @brief CondVar structure. + */ +typedef struct CondVar { + ThreadsQueue c_queue; /**< CondVar threads queue.*/ +} CondVar; + +#ifdef __cplusplus +extern "C" { +#endif + void chCondInit(CondVar *cp); + void chCondSignal(CondVar *cp); + void chCondSignalI(CondVar *cp); + void chCondBroadcast(CondVar *cp); + void chCondBroadcastI(CondVar *cp); + msg_t chCondWait(CondVar *cp); + msg_t chCondWaitS(CondVar *cp); +#if CH_USE_CONDVARS_TIMEOUT + msg_t chCondWaitTimeout(CondVar *cp, systime_t time); + msg_t chCondWaitTimeoutS(CondVar *cp, systime_t time); +#endif +#ifdef __cplusplus +} +#endif + +/** + * @brief Data part of a static condition variable initializer. + * @details This macro should be used when statically initializing a condition + * variable that is part of a bigger structure. + */ +#define _CONDVAR_DATA(name) {_THREADSQUEUE_DATA(name.c_queue)} + +/** + * @brief Static condition variable initializer. + * @details Statically initialized condition variables require no explicit + * initialization using @p chCondInit(). + * @param name the name of the condition variable + */ +#define CONDVAR_DECL(name) CondVar name = _CONDVAR_DATA(name) + +#endif /* CH_USE_CONDVARS && CH_USE_MUTEXES */ + +#endif /* _CONDVARS_H_ */ + +/** @} */ diff --git a/os/kernel/include/debug.h b/os/kernel/include/debug.h new file mode 100644 index 000000000..67a5d65b4 --- /dev/null +++ b/os/kernel/include/debug.h @@ -0,0 +1,149 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file debug.h + * @brief Debug macros and structures. + * @addtogroup Debug + * @{ + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +/** + * @brief Trace buffer entries. + */ +#ifndef TRACE_BUFFER_SIZE +#define TRACE_BUFFER_SIZE 64 +#endif + +/** + * @brief Fill value for thread stack area in debug mode. + */ +#ifndef STACK_FILL_VALUE +#define STACK_FILL_VALUE 0x55 +#endif + +/** + * @brief Fill value for thread area in debug mode. + * @note The chosen default value is 0xFF in order to make evident which + * thread fields were not initialized when inspecting the memory with + * a debugger. A uninitialized field is not an error in itself but it + * better to know it. + */ +#ifndef THREAD_FILL_VALUE +#define THREAD_FILL_VALUE 0xFF +#endif + +/** + * @brief Trace buffer record. + */ +typedef struct { + void *cse_wtobjp; /**< Object where going to sleep.*/ + systime_t cse_time; /**< Time of the switch event.*/ + uint16_t cse_state: 4; /**< Switched out thread state.*/ + uint16_t cse_tid: 12; /**< Switched in thdread id.*/ +} CtxSwcEvent; + +/** + * @brief Trace buffer header. + */ +typedef struct { + unsigned tb_size; /**< Trace buffer size (records).*/ + CtxSwcEvent *tb_ptr; /**< Pointer to the ring buffer front.*/ + CtxSwcEvent tb_buffer[TRACE_BUFFER_SIZE]; /**< Ring buffer.*/ +} TraceBuffer; + +#define __QUOTE_THIS(p) #p + +#if CH_DBG_ENABLE_CHECKS +/** + * Function parameter check, if the condition check fails then the kernel + * panics. + * @param c the condition to be verified to be true + * @param func the undecorated function name + * @note The condition is tested only if the @p CH_DBG_ENABLE_CHECKS switch is + * specified in @p chconf.h else the macro does nothing. + */ +#define chDbgCheck(c, func) { \ + if (!(c)) \ + chDbgPanic(__QUOTE_THIS(func)"(), line "__QUOTE_THIS(__LINE__)); \ +} +#else /* !CH_DBG_ENABLE_CHECKS */ +#define chDbgCheck(c, func) { \ + (void)(c), (void)__QUOTE_THIS(func)"(), line "__QUOTE_THIS(__LINE__); \ +} +#endif /* !CH_DBG_ENABLE_CHECKS */ + +#if CH_DBG_ENABLE_ASSERTS +/** + * Condition assertion, if the condition check fails then the kernel panics + * with the specified message. + * @param c the condition to be verified to be true + * @param m the text message + * @param r a remark string + * @note The condition is tested only if the @p CH_DBG_ENABLE_ASSERTS switch is + * specified in @p chconf.h else the macro does nothing. + * @note The convention for the message is the following:
+ * @(), #@ + * @note The remark string is not currently used except for putting a comment + * in the code about the assert. + */ +#define chDbgAssert(c, m, r) { \ + if (!(c)) \ + chDbgPanic(m); \ +} +#else /* !CH_DBG_ENABLE_ASSERTS */ +#define chDbgAssert(c, m, r) {(void)(c);} +#endif /* !CH_DBG_ENABLE_ASSERTS */ + +#if !(CH_DBG_ENABLE_ASSERTS || CH_DBG_ENABLE_CHECKS || CH_DBG_ENABLE_STACK_CHECK) +/* When the debug features are disabled this function is replaced by an empty + * macro.*/ +#define chDbgPanic(msg) {} +#endif + +#if !CH_DBG_ENABLE_TRACE +/* When the trace feature is disabled this function is replaced by an empty + * macro.*/ +#define chDbgTrace(otp, ntp) {} +#endif + +#if !defined(__DOXYGEN__) +#ifdef __cplusplus +extern "C" { +#endif +#if CH_DBG_ENABLE_TRACE + extern TraceBuffer trace_buffer; + void trace_init(void); + void chDbgTrace(Thread *otp, Thread *ntp); +#endif +#if CH_DBG_ENABLE_ASSERTS || CH_DBG_ENABLE_CHECKS || CH_DBG_ENABLE_STACK_CHECK + extern char *panic_msg; + void chDbgPanic(char *msg); +#endif +#ifdef __cplusplus +} +#endif +#endif /* !defined(__DOXYGEN__) */ + +#endif /* _DEBUG_H_ */ + +/** @} */ diff --git a/os/kernel/include/events.h b/os/kernel/include/events.h new file mode 100644 index 000000000..c8bf0c6e6 --- /dev/null +++ b/os/kernel/include/events.h @@ -0,0 +1,145 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file events.h + * @brief Events macros and structures. + * @addtogroup Events + * @{ + */ + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#if CH_USE_EVENTS + +typedef struct EventListener EventListener; + +/** + * @brief Event Listener structure. + */ +struct EventListener { + EventListener *el_next; /**< Next Event Listener registered on + the Event Source.*/ + Thread *el_listener; /**< Thread interested in the Event + Source.*/ + eventmask_t el_mask; /**< Event flags mask associated by the + thread to the Event Source.*/ +}; + +/** + * @brief Event Source structure. + */ +typedef struct EventSource { + EventListener *es_next; /**< First Event Listener registered on + the Event Source.*/ +} EventSource; + +/** + * @brief Data part of a static event source initializer. + * @details This macro should be used when statically initializing an event + * source that is part of a bigger structure. + * @param name the name of the event source variable + */ +#define _EVENTSOURCE_DATA(name) {(void *)(&name)} + +/** + * @brief Static event source initializer. + * @details Statically initialized event sources require no explicit + * initialization using @p chEvtInit(). + * @param name the name of the event source variable + */ +#define EVENTSOURCE_DECL(name) EventSource name = _EVENTSOURCE_DATA(name) + +/** All events allowed mask.*/ +#define ALL_EVENTS -1 + +/** Returns the event mask from the event identifier.*/ +#define EVENT_MASK(eid) (1 << (eid)) + +/** + * Registers an Event Listener on an Event Source. + * @param esp pointer to the @p EventSource structure + * @param elp pointer to the @p EventListener structure + * @param eid numeric identifier assigned to the Event Listener. The identifier + * is used as index for the event callback function. + * The value must range between zero and the size, in bit, of the + * @p eventid_t type minus one. + * @note Multiple Event Listeners can use the same event identifier, the + * listener will share the callback function. + */ +#define chEvtRegister(esp, elp, eid) chEvtRegisterMask(esp, elp, EVENT_MASK(eid)) + +/** + * Initializes an Event Source. + * @param esp pointer to the @p EventSource structure + * @note Can be called with interrupts disabled or enabled. + */ +#define chEvtInit(esp) \ + ((esp)->es_next = (EventListener *)(void *)(esp)) + +/** + * Verifies if there is at least one @p EventListener registered on the + * @p EventSource. + * @param esp pointer to the @p EventSource structure + * @note Can be called with interrupts disabled or enabled. + */ +#define chEvtIsListening(esp) \ + ((void *)(esp) != (void *)(esp)->es_next) + +/** Event Handler callback function.*/ +typedef void (*evhandler_t)(eventid_t); + +#ifdef __cplusplus +extern "C" { +#endif + void chEvtRegisterMask(EventSource *esp, EventListener *elp, eventmask_t emask); + void chEvtUnregister(EventSource *esp, EventListener *elp); + eventmask_t chEvtClear(eventmask_t mask); + eventmask_t chEvtPend(eventmask_t mask); + void chEvtSignal(Thread *tp, eventmask_t mask); + void chEvtSignalI(Thread *tp, eventmask_t mask); + void chEvtBroadcast(EventSource *esp); + void chEvtBroadcastI(EventSource *esp); + void chEvtDispatch(const evhandler_t handlers[], eventmask_t mask); +#if CH_OPTIMIZE_SPEED || !CH_USE_EVENTS_TIMEOUT + eventmask_t chEvtWaitOne(eventmask_t ewmask); + eventmask_t chEvtWaitAny(eventmask_t ewmask); + eventmask_t chEvtWaitAll(eventmask_t ewmask); +#endif +#if CH_USE_EVENTS_TIMEOUT + eventmask_t chEvtWaitOneTimeout(eventmask_t ewmask, systime_t time); + eventmask_t chEvtWaitAnyTimeout(eventmask_t ewmask, systime_t time); + eventmask_t chEvtWaitAllTimeout(eventmask_t ewmask, systime_t time); +#endif +#ifdef __cplusplus +} +#endif + +#if !CH_OPTIMIZE_SPEED && CH_USE_EVENTS_TIMEOUT +#define chEvtWaitOne(ewmask) chEvtWaitOneTimeout(ewmask, TIME_INFINITE) +#define chEvtWaitAny(ewmask) chEvtWaitAnyTimeout(ewmask, TIME_INFINITE) +#define chEvtWaitAll(ewmask) chEvtWaitAllTimeout(ewmask, TIME_INFINITE) +#endif + +#endif /* CH_USE_EVENTS */ + +#endif /* _EVENTS_H_ */ + +/** @} */ diff --git a/os/kernel/include/heap.h b/os/kernel/include/heap.h new file mode 100644 index 000000000..4c9571070 --- /dev/null +++ b/os/kernel/include/heap.h @@ -0,0 +1,43 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file heap.h + * @brief Heap macros and structures. + * @addtogroup Heap + * @{ + */ + +#ifndef _HEAP_H_ +#define _HEAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + void heap_init(void); + void *chHeapAlloc(size_t size); + void chHeapFree(void *p); + size_t chHeapStatus(size_t *sizep); +#ifdef __cplusplus +} +#endif + +#endif /* _HEAP_H_ */ + +/** @} */ diff --git a/os/kernel/include/inline.h b/os/kernel/include/inline.h new file mode 100644 index 000000000..42775d5cb --- /dev/null +++ b/os/kernel/include/inline.h @@ -0,0 +1,72 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _INLINE_H_ +#define _INLINE_H_ + +/** + * @file inline.h + * @brief Kernel inlined functions. + */ + +/* + * Inlined functions if CH_OPTIMIZE_SPEED is enabled. + * Note: static inlined functions do not duplicate the code in every module + * this is true for GCC, not sure about other compilers. + */ +#if CH_OPTIMIZE_SPEED +static INLINE void prio_insert(Thread *tp, ThreadsQueue *tqp) { + + Thread *cp = (Thread *)tqp; + do { + cp = cp->p_next; + } while ((cp != (Thread *)tqp) && (cp->p_prio >= tp->p_prio)); + tp->p_prev = (tp->p_next = cp)->p_prev; + tp->p_prev->p_next = cp->p_prev = tp; +} + +static INLINE void queue_insert(Thread *tp, ThreadsQueue *tqp) { + + tp->p_prev = (tp->p_next = (Thread *)tqp)->p_prev; + tp->p_prev->p_next = tqp->p_prev = tp; +} + +static INLINE Thread *fifo_remove(ThreadsQueue *tqp) { + Thread *tp = tqp->p_next; + + (tqp->p_next = tp->p_next)->p_prev = (Thread *)tqp; + return tp; +} + +static INLINE Thread *lifo_remove(ThreadsQueue *tqp) { + Thread *tp = tqp->p_prev; + + (tqp->p_prev = tp->p_prev)->p_next = (Thread *)tqp; + return tp; +} + +static INLINE Thread *dequeue(Thread *tp) { + + tp->p_prev->p_next = tp->p_next; + tp->p_next->p_prev = tp->p_prev; + return tp; +} +#endif /* CH_OPTIMIZE_SPEED */ + +#endif /* _INLINE_H_ */ diff --git a/os/kernel/include/lists.h b/os/kernel/include/lists.h new file mode 100644 index 000000000..bc3fd7272 --- /dev/null +++ b/os/kernel/include/lists.h @@ -0,0 +1,92 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file lists.h + * @brief Thread queues/lists macros and structures. + * @addtogroup ThreadLists + * @{ + */ + +#ifndef _LISTS_H_ +#define _LISTS_H_ + +typedef struct Thread Thread; + +/** + * Threads queue initialization. + */ +#define queue_init(tqp) ((tqp)->p_next = (tqp)->p_prev = (Thread *)(tqp)); + +/** + * Macro evaluating to @p TRUE if the specified threads queue is empty. + */ +#define isempty(p) ((p)->p_next == (Thread *)(p)) + +/** + * Macro evaluating to @p TRUE if the specified threads queue is not empty. + */ +#define notempty(p) ((p)->p_next != (Thread *)(p)) + +/** + * @brief Data part of a static threads queue initializer. + * @details This macro should be used when statically initializing a threads + * queue that is part of a bigger structure. + * @param name the name of the threads queue variable + */ +#define _THREADSQUEUE_DATA(name) {(Thread *)&name, (Thread *)&name} + +/** + * @brief Static threads queue initializer. + * @details Statically initialized threads queues require no explicit + * initialization using @p queue_init(). + * @param name the name of the threads queue variable + */ +#define THREADSQUEUE_DECL(name) ThreadsQueue name = _THREADSQUEUE_DATA(name) + +/** + * @brief Generic threads bidirectional linked list header and element. + * @extends ThreadsList + */ +typedef struct { + Thread *p_next; /**< First @p Thread in the queue, or + @p ThreadQueue when empty.*/ + Thread *p_prev; /**< Last @p Thread in the queue, or + @p ThreadQueue when empty.*/ +} ThreadsQueue; + +#if !CH_OPTIMIZE_SPEED + +#ifdef __cplusplus +extern "C" { +#endif + void prio_insert(Thread *tp, ThreadsQueue *tqp); + void queue_insert(Thread *tp, ThreadsQueue *tqp); + Thread *fifo_remove(ThreadsQueue *tqp); + Thread *lifo_remove(ThreadsQueue *tqp); + Thread *dequeue(Thread *tp); +#ifdef __cplusplus +} +#endif + +#endif /* !CH_OPTIMIZE_SPEED */ + +#endif /* _LISTS_H_ */ + +/** @} */ diff --git a/os/kernel/include/mailboxes.h b/os/kernel/include/mailboxes.h new file mode 100644 index 000000000..075806da8 --- /dev/null +++ b/os/kernel/include/mailboxes.h @@ -0,0 +1,126 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file mailboxes.h + * @brief Mailboxes macros and structures. + * @addtogroup Mailboxes + * @{ + */ + +#ifndef _MAILBOXES_H_ +#define _MAILBOXES_H_ + +#if CH_USE_MAILBOXES + +typedef struct { + msg_t *mb_buffer; /**< Pointer to the mailbox buffer.*/ + msg_t *mb_top; /**< Pointer to the first location + after the buffer.*/ + msg_t *mb_wrptr; /**< Write pointer.*/ + msg_t *mb_rdptr; /**< Read pointer.*/ + Semaphore mb_fullsem; /**< Full counter @p Semaphore.*/ + Semaphore mb_emptysem; /**< Empty counter @p Semaphore.*/ +} Mailbox; + +#ifdef __cplusplus +extern "C" { +#endif + void chMBInit(Mailbox *mbp, msg_t *buf, cnt_t n); + void chMBReset(Mailbox *mbp); + msg_t chMBPost(Mailbox *mbp, msg_t msg, systime_t timeout); + msg_t chMBPostS(Mailbox *mbp, msg_t msg, systime_t timeout); + msg_t chMBPostAhead(Mailbox *mbp, msg_t msg, systime_t timeout); + msg_t chMBPostAheadS(Mailbox *mbp, msg_t msg, systime_t timeout); + msg_t chMBFetch(Mailbox *mbp, msg_t *msgp, systime_t timeout); + msg_t chMBFetchS(Mailbox *mbp, msg_t *msgp, systime_t timeout); +#ifdef __cplusplus +} +#endif + +/** + * Returns the mailbox buffer size. + * @param[in] mbp the pointer to an initialized Mailbox object + */ +#define chMBSize(mbp) \ + ((mbp)->mb_top - (mbp)->mb_buffer) + +/** + * Returns the free space into the mailbox. + * @param[in] mbp the pointer to an initialized Mailbox object + * @return The number of empty message slots. + * @note Can be invoked in any system state but if invoked out of a locked + * state then the returned value may change after reading. + * @note The returned value can be less than zero when there are waiting + * threads on the internal semaphore. + */ +#define chMBGetEmpty(mbp) chSemGetCounterI(&(mbp)->mb_emptysem) + +/** + * Returns the number of messages into the mailbox. + * @param[in] mbp the pointer to an initialized Mailbox object + * @return The number of queued messages. + * @note Can be invoked in any system state but if invoked out of a locked + * state then the returned value may change after reading. + * @note The returned value can be less than zero when there are waiting + * threads on the internal semaphore. + */ +#define chMBGetFull(mbp) chSemGetCounterI(&(mbp)->mb_fullsem) + +/** + * Returns the next message in the queue without removing it. + * @note A message must be waiting in the queue for this function to work or + * it would return garbage. The correct way to use this macro is to + * use @p chMBGetFull() and then use this macro, all within a lock state. + */ +#define chMBPeek(mbp) (*(mbp)->mb_rdptr) + +/** + * @brief Data part of a static mailbox initializer. + * @details This macro should be used when statically initializing a + * mailbox that is part of a bigger structure. + * @param name the name of the mailbox variable + * @param buffer pointer to the mailbox buffer area + * @param size size of the mailbox buffer area + */ +#define _MAILBOX_DATA(name, buffer, size) { \ + (msg_t *)(buffer), \ + (msg_t *)(buffer) + size, \ + (msg_t *)(buffer), \ + (msg_t *)(buffer), \ + _SEMAPHORE_DATA(name.mb_fullsem, 0), \ + _SEMAPHORE_DATA(name.mb_emptysem, size), \ +} + +/** + * @brief Static mailbox initializer. + * @details Statically initialized mailboxes require no explicit + * initialization using @p chMBInit(). + * @param name the name of the mailbox variable + * @param buffer pointer to the mailbox buffer area + * @param size size of the mailbox buffer area + */ +#define MAILBOX_DECL(name, buffer, size) \ + Mailbox name = _MAILBOX_DATA(name, buffer, size) + +#endif /* CH_USE_MAILBOXES */ + +#endif /* _MAILBOXES_H_ */ + +/** @} */ diff --git a/os/kernel/include/mempools.h b/os/kernel/include/mempools.h new file mode 100644 index 000000000..38e54b3d8 --- /dev/null +++ b/os/kernel/include/mempools.h @@ -0,0 +1,82 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file mempools.h + * @brief Memory Pools macros and structures. + * @addtogroup MemoryPools + * @{ + */ + +#ifndef _MEMPOOLS_H_ +#define _MEMPOOLS_H_ + +#if CH_USE_MEMPOOLS + +/** + * @brief Memory pool free object header. + */ +struct pool_header { + struct pool_header *ph_next; +}; + +/** + * @brief Memory pool descriptor. + */ +typedef struct { + struct pool_header *mp_next; /**< Pointer to the header.*/ + size_t mp_object_size; /**< Memory pool objects size.*/ +} MemoryPool; + +/** + * @brief Data part of a static memory pool initializer. + * @details This macro should be used when statically initializing a + * memory pool that is part of a bigger structure. + * @param name the name of the memory pool variable + * @param size size of the memory pool contained objects + */ +#define _MEMORYPOOL_DATA(name, size) {NULL, size} + +/** + * @brief Static memory pool initializer. + * @details Statically initialized memory pools require no explicit + * initialization using @p chPoolInit(). + * @param name the name of the memory pool variable + * @param size size of the memory pool contained objects + */ +#define MEMORYPOOL_DECL(name, size) \ + MemoryPool name = _MEMORYPOOL_DATA(name, size) + +#ifdef __cplusplus +extern "C" { +#endif + void chPoolInit(MemoryPool *mp, size_t size); + void *chPoolAllocI(MemoryPool *mp); + void *chPoolAlloc(MemoryPool *mp); + void chPoolFreeI(MemoryPool *mp, void *objp); + void chPoolFree(MemoryPool *mp, void *objp); +#ifdef __cplusplus +} +#endif + +#endif /* CH_USE_MEMPOOLS */ + +#endif /* _MEMPOOLS_H_ */ + +/** @} */ diff --git a/os/kernel/include/messages.h b/os/kernel/include/messages.h new file mode 100644 index 000000000..ee1449a82 --- /dev/null +++ b/os/kernel/include/messages.h @@ -0,0 +1,59 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file messages.h + * @brief Messages macros and structures. + * @addtogroup Messages + * @{ + */ + +#ifndef _MESSAGES_H_ +#define _MESSAGES_H_ + +#if CH_USE_MESSAGES + +/** + * Evaluates to TRUE if the thread has pending messages. + */ +#define chMsgIsPendingI(tp) \ + ((tp)->p_msgqueue.p_next != (Thread *)&(tp)->p_msgqueue) + +/** + * Returns the first message in the queue. + */ +#define chMsgGetI(tp) \ + ((tp)->p_msgqueue.p_next->p_msg) + +#ifdef __cplusplus +extern "C" { +#endif + msg_t chMsgSend(Thread *tp, msg_t msg); + msg_t chMsgWait(void); + msg_t chMsgGet(void); + void chMsgRelease(msg_t msg); +#ifdef __cplusplus +} +#endif + +#endif /* CH_USE_MESSAGES */ + +#endif /* _MESSAGES_H_ */ + +/** @} */ diff --git a/os/kernel/include/mutexes.h b/os/kernel/include/mutexes.h new file mode 100644 index 000000000..6ef6e48f4 --- /dev/null +++ b/os/kernel/include/mutexes.h @@ -0,0 +1,84 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file mutexes.h + * @brief Mutexes macros and structures. + * @addtogroup Mutexes + * @{ + */ + +#ifndef _MUTEXES_H_ +#define _MUTEXES_H_ + +#if CH_USE_MUTEXES + +/** + * @brief Mutex structure. + */ +typedef struct Mutex { + ThreadsQueue m_queue; /**< Queue of the threads sleeping on + this Mutex.*/ + Thread *m_owner; /**< Owner @p Thread pointer or + @p NULL.*/ + struct Mutex *m_next; /**< Next @p Mutex into an owner-list + or @p NULL.*/ +} Mutex; + +#ifdef __cplusplus +extern "C" { +#endif + void chMtxInit(Mutex *mp); + void chMtxLock(Mutex *mp); + void chMtxLockS(Mutex *mp); + bool_t chMtxTryLock(Mutex *mp); + bool_t chMtxTryLockS(Mutex *mp); + Mutex *chMtxUnlock(void); + Mutex *chMtxUnlockS(void); + void chMtxUnlockAll(void); +#ifdef __cplusplus +} +#endif + +/** + * @brief Data part of a static mutex initializer. + * @details This macro should be used when statically initializing a mutex + * that is part of a bigger structure. + * @param name the name of the mutex variable + */ +#define _MUTEX_DATA(name) {_THREADSQUEUE_DATA(name.m_queue), NULL, NULL} + +/** + * @brief Static mutex initializer. + * @details Statically initialized mutexes require no explicit initialization + * using @p chMtxInit(). + * @param name the name of the mutex variable + */ +#define MUTEX_DECL(name) Mutex name = _MUTEX_DATA(name) + +/** + * Returns @p TRUE if the mutex queue contains at least a waiting thread. + */ +#define chMtxQueueNotEmptyS(mp) notempty(&(mp)->m_queue) + +#endif /* CH_USE_MUTEXES */ + +#endif /* _MUTEXES_H_ */ + +/** @} */ diff --git a/os/kernel/include/queues.h b/os/kernel/include/queues.h new file mode 100644 index 000000000..6bd98b4f1 --- /dev/null +++ b/os/kernel/include/queues.h @@ -0,0 +1,218 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file queues.h I/O + * @brief Queues macros and structures. + * @addtogroup IOQueues + * @{ + */ + +#ifndef _QUEUES_H_ +#define _QUEUES_H_ + +/** Queue notification callback type. */ +typedef void (*qnotify_t)(void); + +/** Returned by the queue functions if the operation is successful. */ +#define Q_OK RDY_OK +/** Returned by the queue functions if a timeout occurs. */ +#define Q_TIMEOUT RDY_TIMEOUT +/** Returned by the queue functions if the queue is reset. */ +#define Q_RESET RDY_RESET +/** Returned by the queue functions if the queue is empty. */ +#define Q_EMPTY -3 +/** Returned by the queue functions if the queue is full. */ +#define Q_FULL -4 + +#if CH_USE_QUEUES +/** + * @brief Generic I/O queue structure. + * @details This structure represents a generic Input or Output asymmetrical + * queue. The queue is asymmetrical because one end is meant to be + * accessed from a thread context, and thus can be blocking, the other + * end is accessible from interrupt handlers or from within a kernel + * lock zone (see I-Locked and S-Locked states in + * @ref system_states) and is non-blocking. + */ +typedef struct { + uint8_t *q_buffer; /**< Pointer to the queue buffer.*/ + uint8_t *q_top; /**< Pointer to the first location + after the buffer.*/ + uint8_t *q_wrptr; /**< Write pointer.*/ + uint8_t *q_rdptr; /**< Read pointer.*/ + Semaphore q_sem; /**< Counter @p Semaphore.*/ + qnotify_t q_notify; /**< Data notification callback.*/ +} GenericQueue; + +/** Returns the queue's buffer size. */ +#define chQSize(q) ((q)->q_top - (q)->q_buffer) + +/** + * Returns the used space if used on an Input Queue and the empty space if + * used on an Output Queue. + * @note The returned value can be less than zero when there are waiting + * threads on the internal semaphore. + */ +#define chQSpace(q) chSemGetCounterI(&(q)->q_sem) + +/** + * @brief Input queue structure. + * @details This structure represents a generic asymmetrical input queue. + * Writing in the queue is non-blocking and can be performed from + * interrupt handlers or from within a kernel lock zone (see + * I-Locked and S-Locked states in @ref system_states). + * Reading the queue can be a blocking operation and is supposed to + * be performed by a system thread. + * @extends GenericQueue + */ +typedef GenericQueue InputQueue; + +/** Evaluates to @p TRUE if the specified Input Queue is empty. */ +#define chIQIsEmpty(q) (chQSpace(q) <= 0) + +/** Evaluates to @p TRUE if the specified Input Queue is full. */ +#define chIQIsFull(q) (chQSpace(q) >= chQSize(q)) + +/** + * @brief Input queue read. + * @details This function reads a byte value from an input queue. If the queue + * is empty then the calling thread is suspended until a byte arrives + * in the queue. + * + * @param[in] iqp pointer to an @p InputQueue structure + * @return A byte value from the queue or: + * @retval Q_RESET if the queue was reset. + */ +#define chIQGet(iqp) chIQGetTimeout(iqp, TIME_INFINITE) + +/** + * @brief Data part of a static input queue initializer. + * @details This macro should be used when statically initializing an + * input queue that is part of a bigger structure. + * @param name the name of the input queue variable + * @param buffer pointer to the queue buffer area + * @param size size of the queue buffer area + * @param inotify input notification callback pointer + */ +#define _INPUTQUEUE_DATA(name, buffer, size, inotify) { \ + (uint8_t *)(buffer), \ + (uint8_t *)(buffer) + size, \ + (uint8_t *)(buffer), \ + (uint8_t *)(buffer), \ + _SEMAPHORE_DATA(name.q_sem, 0), \ + inotify \ +} + +/** + * @brief Static input queue initializer. + * @details Statically initialized input queues require no explicit + * initialization using @p chIQInit(). + * @param name the name of the input queue variable + * @param buffer pointer to the queue buffer area + * @param size size of the queue buffer area + * @param inotify input notification callback pointer + */ +#define INPUTQUEUE_DECL(name, buffer, size, inotify) \ + InputQueue name = _INPUTQUEUE_DATA(name, buffer, size, inotify) + +/** + * @brief Output queue structure. + * @details This structure represents a generic asymmetrical output queue. + * Reading from the queue is non-blocking and can be performed from + * interrupt handlers or from within a kernel lock zone (see + * I-Locked and S-Locked states in @ref system_states). + * Writing the queue can be a blocking operation and is supposed to + * be performed by a system thread. + * @extends GenericQueue + */ +typedef GenericQueue OutputQueue; + +/** Evaluates to @p TRUE if the specified Output Queue is empty. */ +#define chOQIsEmpty(q) (chQSpace(q) >= chQSize(q)) + +/** Evaluates to @p TRUE if the specified Output Queue is full. */ +#define chOQIsFull(q) (chQSpace(q) <= 0) + +/** + * @brief Output queue write. + * @details This function writes a byte value to an output queue. If the queue + * is full then the calling thread is suspended until there is space + * in the queue. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @param[in] b the byte value to be written in the queue + * @return The operation status: + * @retval Q_OK if the operation succeeded. + * @retval Q_RESET if the queue was reset. + */ +#define chOQPut(oqp, b) chOQPutTimeout(oqp, b, TIME_INFINITE) + +/** + * @brief Data part of a static output queue initializer. + * @details This macro should be used when statically initializing an + * output queue that is part of a bigger structure. + * @param name the name of the output queue variable. + * @param buffer pointer to the queue buffer area + * @param size size of the queue buffer area + * @param onotify output notification callback pointer + */ +#define _OUTPUTQUEUE_DATA(name, buffer, size, onotify) { \ + (uint8_t *)(buffer), \ + (uint8_t *)(buffer) + size, \ + (uint8_t *)(buffer), \ + (uint8_t *)(buffer), \ + _SEMAPHORE_DATA(name.q_sem, size), \ + onotify \ +} + +/** + * @brief Static output queue initializer. + * @details Statically initialized output queues require no explicit + * initialization using @p chOQInit(). + * @param name the name of the output queue variable + * @param buffer pointer to the queue buffer area + * @param size size of the queue buffer area + * @param onotify output notification callback pointer + */ +#define OUTPUTQUEUE_DECL(name, buffer, size, onotify) \ + InputQueue name = _OUTPUTQUEUE_DATA(name, buffer, size, onotify) + +#ifdef __cplusplus +extern "C" { +#endif + void chIQInit(InputQueue *qp, uint8_t *buffer, size_t size, qnotify_t inotify); + void chIQResetI(InputQueue *qp); + msg_t chIQPutI(InputQueue *qp, uint8_t b); + msg_t chIQGetTimeout(InputQueue *qp, systime_t timeout); + size_t chIQRead(InputQueue *qp, uint8_t *buffer, size_t n); + + void chOQInit(OutputQueue *queue, uint8_t *buffer, size_t size, qnotify_t onotify); + void chOQResetI(OutputQueue *queue); + msg_t chOQPutTimeout(OutputQueue *queue, uint8_t b, systime_t timeout); + msg_t chOQGetI(OutputQueue *queue); + size_t chOQWrite(OutputQueue *queue, uint8_t *buffer, size_t n); +#ifdef __cplusplus +} +#endif +#endif /* CH_USE_QUEUES */ + +#endif /* _QUEUES_H_ */ + +/** @} */ diff --git a/os/kernel/include/scheduler.h b/os/kernel/include/scheduler.h new file mode 100644 index 000000000..648cb7bc5 --- /dev/null +++ b/os/kernel/include/scheduler.h @@ -0,0 +1,110 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file scheduler.h + * @brief Scheduler macros and structures. + * @addtogroup Scheduler + * @{ + */ + +#ifndef _SCHEDULER_H_ +#define _SCHEDULER_H_ + +/** Default thread wakeup low level message. */ +#define RDY_OK 0 +/** Low level message sent to a thread awakened by a timeout. */ +#define RDY_TIMEOUT -1 +/** Low level message sent to a thread awakened by a reset operation. */ +#define RDY_RESET -2 + +#define NOPRIO 0 /**< Ready list header priority.*/ +#define IDLEPRIO 1 /**< Idle thread priority.*/ +#define LOWPRIO 2 /**< Lowest user priority.*/ +#define NORMALPRIO 64 /**< Normal user priority.*/ +#define HIGHPRIO 127 /**< Highest user priority.*/ +#define ABSPRIO 255 /**< Greatest possible priority.*/ + +/** + * Zero time specification for some syscalls with a timeout + * specification. + * @note Not all functions accept @p TIME_IMMEDIATE as timeout parameter, + * see the specific function documentation. + */ +#define TIME_IMMEDIATE ((systime_t)-1) + +/** + * Infinite time specification for all the syscalls with a timeout + * specification. + */ +#define TIME_INFINITE ((systime_t)0) + +/** The priority of the first thread on the given ready list. */ +#define firstprio(rlp) ((rlp)->p_next->p_prio) + +/** + * @brief Ready list header. + * + * @extends ThreadsQueue + */ +typedef struct { + Thread *p_next; /**< Next @p Thread in the ready list.*/ + Thread *p_prev; /**< Previous @p Thread in the ready + list.*/ + /* End of the fields shared with the ThreadsQueue structure. */ + tprio_t r_prio; /**< This field must be initialized to + zero.*/ + /* End of the fields shared with the Thread structure. */ +#if CH_USE_ROUNDROBIN + cnt_t r_preempt; /**< Round robin counter.*/ +#endif +#ifndef CH_CURRP_REGISTER_CACHE + Thread *r_current; /**< The currently running thread.*/ +#endif +} ReadyList; + +extern ReadyList rlist; + +/* + * Scheduler APIs. + */ +#ifdef __cplusplus +extern "C" { +#endif + void scheduler_init(void); + Thread *chSchReadyI(Thread *tp); + void chSchGoSleepS(tstate_t newstate); + msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t time); + void chSchWakeupS(Thread *tp, msg_t msg); + void chSchDoRescheduleI(void); + void chSchRescheduleS(void); + bool_t chSchRescRequiredI(void); +#ifdef __cplusplus +} +#endif + +#ifdef CH_CURRP_REGISTER_CACHE +register Thread *currp asm(CH_CURRP_REGISTER_CACHE); +#else +#define currp rlist.r_current +#endif + +#endif /* _SCHEDULER_H_ */ + +/** @} */ diff --git a/os/kernel/include/semaphores.h b/os/kernel/include/semaphores.h new file mode 100644 index 000000000..833fc5cd2 --- /dev/null +++ b/os/kernel/include/semaphores.h @@ -0,0 +1,99 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file semaphores.h + * @brief Semaphores macros and structures. + * @addtogroup Semaphores + * @{ + */ + +#ifndef _SEMAPHORES_H_ +#define _SEMAPHORES_H_ + +#if CH_USE_SEMAPHORES + +/** + * @brief Semaphore structure. + */ +typedef struct Semaphore { + ThreadsQueue s_queue; /**< Queue of the threads sleeping on + this semaphore.*/ + cnt_t s_cnt; /**< The semaphore counter.*/ +} Semaphore; + +#ifdef __cplusplus +extern "C" { +#endif + void chSemInit(Semaphore *sp, cnt_t n); + void chSemReset(Semaphore *sp, cnt_t n); + void chSemResetI(Semaphore *sp, cnt_t n); + msg_t chSemWait(Semaphore *sp); + msg_t chSemWaitS(Semaphore *sp); + msg_t chSemWaitTimeout(Semaphore *sp, systime_t time); + msg_t chSemWaitTimeoutS(Semaphore *sp, systime_t time); + void chSemSignal(Semaphore *sp); + void chSemSignalI(Semaphore *sp); +#if CH_USE_SEMSW + msg_t chSemSignalWait(Semaphore *sps, Semaphore *spw); +#endif +#ifdef __cplusplus +} +#endif + +/** + * @brief Data part of a static semaphore initializer. + * @details This macro should be used when statically initializing a semaphore + * that is part of a bigger structure. + * @param name the name of the semaphore variable + * @param n the counter initial value, this value must be non-negative + */ +#define _SEMAPHORE_DATA(name, n) {_THREADSQUEUE_DATA(name.s_queue), n} + +/** + * @brief Static semaphore initializer. + * @details Statically initialized semaphores require no explicit initialization + * using @p chSemInit(). + * @param name the name of the semaphore variable + * @param n the counter initial value, this value must be non-negative + */ +#define SEMAPHORE_DECL(name, n) Semaphore name = _SEMAPHORE_DATA(name, n) + +/** + * Decreases the semaphore counter, this macro can be used when it is ensured + * that the counter would not become negative. + */ +#define chSemFastWaitI(sp) ((sp)->s_cnt--) + +/** + * Increases the semaphore counter, this macro can be used when the counter is + * not negative. + */ +#define chSemFastSignalI(sp) ((sp)->s_cnt++) + +/** + * Returns the semaphore counter current value. + */ +#define chSemGetCounterI(sp) ((sp)->s_cnt) + +#endif /* CH_USE_SEMAPHORES */ + +#endif /* _SEMAPHORES_H_ */ + +/** @} */ diff --git a/os/kernel/include/serial.h b/os/kernel/include/serial.h new file mode 100644 index 000000000..deaca3d97 --- /dev/null +++ b/os/kernel/include/serial.h @@ -0,0 +1,217 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file serial.h + * @brief Serial Drivers macros and structures. + * @addtogroup Serial + * @{ + */ + +#ifndef _SERIAL_H_ +#define _SERIAL_H_ + +#if CH_USE_SERIAL_FULLDUPLEX + +/** No pending conditions.*/ +#define SD_NO_ERROR 0 +/** Connection happened.*/ +#define SD_CONNECTED 1 +/** Disconnection happened.*/ +#define SD_DISCONNECTED 2 +/** Parity error happened.*/ +#define SD_PARITY_ERROR 4 +/** Framing error happened.*/ +#define SD_FRAMING_ERROR 8 +/** Overflow happened.*/ +#define SD_OVERRUN_ERROR 16 +/** Break detected.*/ +#define SD_BREAK_DETECTED 32 + +/** Serial Driver condition flags type.*/ +typedef uint8_t dflags_t; + +/** + * @brief @p FullDuplexDriver specific methods. + */ +struct _full_duplex_driver_methods { +}; + +/** + * @brief @p FullDuplexDriver specific data. + */ +struct _full_duplex_driver_data { + /** + * Input queue, incoming data can be read from this input queue by + * using the queues APIs. + */ + InputQueue iqueue; + /** + * Output queue, outgoing data can be written to this output queue by + * using the queues APIs. + */ + OutputQueue oqueue; + /** + * Status Change @p EventSource. This event is generated when one or more + * condition flags change. + */ + EventSource sevent; + /** + * I/O driver status flags. + */ + dflags_t flags; +}; + +/** + * @brief @p FullDuplexDriver virtual methods table. + */ +struct FullDuplexDriverVMT { + /** + * @p BaseChannel class inherited methods. + */ + struct _base_channel_methods m0; + /** + * @p BaseAsynchronousChannel class inherited methods. + */ + struct _base_asynchronous_channel_methods m1; + /** + * @p FullDuplexDriver specific methods. + */ + struct _full_duplex_driver_methods m2; +}; + +/** + * @extends BaseAsynchronousChannel + * + * @brief Full duplex serial driver class. + * @details This class extends @p GenericSerialDriver by adding physical I/O + * queues. + */ +typedef struct { + /** + * Virtual Methods Table. + */ + const struct FullDuplexDriverVMT *vmt; + /** + * @p BaseChannel class inherited data. + */ + struct _base_channel_data d0; + /** + * @p BaseAsynchronousChannel class inherited data. + */ + struct _base_asynchronous_channel_data d1; + /** + * @p FullDuplexDriver specific data. + */ + struct _full_duplex_driver_data d2; +} FullDuplexDriver; + +#ifdef __cplusplus +extern "C" { +#endif + void chFDDInit(FullDuplexDriver *sd, + uint8_t *ib, size_t isize, qnotify_t inotify, + uint8_t *ob, size_t osize, qnotify_t onotify); + void chFDDIncomingDataI(FullDuplexDriver *sd, uint8_t b); + msg_t chFDDRequestDataI(FullDuplexDriver *sd); + void chFDDAddFlagsI(FullDuplexDriver *sd, dflags_t mask); + dflags_t chFDDGetAndClearFlags(FullDuplexDriver *sd); +#ifdef __cplusplus +} +#endif + +/** + * @brief Direct output check on a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * checks directly the output queue. This is faster but cannot + * be used to check different channels implementations. + * @see chIOPutWouldBlock() + */ +#define chFDDPutWouldBlock(sd) chOQIsFull(&(sd)->d2.oqueue) + +/** + * @brief Direct input check on a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * checks directly the input queue. This is faster but cannot + * be used to check different channels implementations. + * @see chIOGetWouldBlock() + */ +#define chFDDGetWouldBlock(sd) chIQIsEmpty(&(sd)->d2.iqueue) + +/** + * @brief Direct blocking write to a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * writes directly on the output queue. This is faster but cannot + * be used to write to different channels implementations. + * @see chIOPut() + */ +#define chFDDPut(sd, b) chOQPut(&(sd)->d2.oqueue, b) + +/** + * @brief Direct blocking write on a @p FullDuplexDriver with timeout + * specification. + * @details This function bypasses the indirect access to the channel and + * writes directly on the output queue. This is faster but cannot + * be used to write to different channels implementations. + * @see chIOPutTimeout() + */ +#define chFDDPutTimeout(sd, b, t) chOQPutTimeout(&(sd)->d2.iqueue, b, t) + +/** + * @brief Direct blocking read from a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * reads directly from the input queue. This is faster but cannot + * be used to read from different channels implementations. + * @see chIOGet() + */ +#define chFDDGet(sd) chIQGet(&(sd)->d2.iqueue) + +/** + * @brief Direct blocking read from a @p FullDuplexDriver with timeout + * specification. + * @details This function bypasses the indirect access to the channel and + * reads directly from the input queue. This is faster but cannot + * be used to read from different channels implementations. + * @see chIOGetTimeout() + */ +#define chFDDGetTimeout(sd, t) chIQGetTimeout(&(sd)->d2.iqueue, t) + +/** + * @brief Direct non-blocking write to a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * writes directly to the output queue. This is faster but cannot + * be used to write from different channels implementations. + * @see chIOWrite() + */ +#define chFDDWrite(sd, b, n) chOQWrite(&(sd)->d2.oqueue, b, n) + +/** + * @brief Direct non-blocking read on a @p FullDuplexDriver. + * @details This function bypasses the indirect access to the channel and + * reads directly from the input queue. This is faster but cannot + * be used to read from different channels implementations. + * @see chIORead() + */ +#define chFDDRead(sd, b, n) chIQRead(&(sd)->d2.iqueue, b, n) + +#endif /* CH_USE_SERIAL_FULLDUPLEX */ + +#endif /* _SERIAL_H_ */ + +/** @} */ diff --git a/os/kernel/include/sys.h b/os/kernel/include/sys.h new file mode 100644 index 000000000..a84d33da8 --- /dev/null +++ b/os/kernel/include/sys.h @@ -0,0 +1,184 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file sys.h + * @brief System related macros and structures. + * @addtogroup System + * @{ + */ + +#ifndef _SYS_H_ +#define _SYS_H_ + +/** + * @brief Halts the system. + * @details This function is invoked by the operating system when an + * unrecoverable error is detected (as example because a programming error in + * the application code that triggers an assertion while in debug mode). + */ +#define chSysHalt() port_halt() + +/** + * @brief Performs a context switch. + * + * @param otp the thread to be switched out + * @param ntp the thread to be switched in + */ +#define chSysSwitchI(otp, ntp) port_switch(otp, ntp) + +/** + * @brief Raises the system interrupt priority mask to the maximum level. + * @details All the maskable interrupt sources are disabled regardless their + * hardware priority. + * + * @note The implementation is architecture dependent, it may just disable the + * interrupts or be exactly equivalent to @p chSysDisable(). + * @note Do not invoke this API from within a kernel lock. + */ +#define chSysDisable() port_disable() + +/** + * @brief Raises the system interrupt priority mask to system level. + * @details The interrupt sources that should not be able to preempt the kernel + * are disabled, interrupt sources with higher priority are still enabled. + * + * @note The implementation is architecture dependent, it may just disable the + * interrupts. + * @note Do not invoke this API from within a kernel lock. + * @note This API is no replacement for @p chSysLock(), the @p chSysLock() + * could do more than just disable the interrupts. + */ +#define chSysSuspend() port_suspend() + +/** + * @brief Lowers the system interrupt priority mask to user level. + * @details All the interrupt sources are enabled. + * + * @note The implementation is architecture dependent, it may just enable the + * interrupts. + * @note Do not invoke this API from within a kernel lock. + * @note This API is no replacement for @p chSysUnlock(), the @p chSysUnlock() + * could do more than just enable the interrupts. + */ +#define chSysEnable() port_enable() + +/** + * @brief Enters the kernel lock mode. + * + * @note The use of kernel lock mode is not recommended in the user code, it is + * a better idea to use the semaphores or mutexes instead. + * @see CH_USE_NESTED_LOCKS + */ +#if CH_USE_NESTED_LOCKS || defined(__DOXYGEN__) +#if CH_OPTIMIZE_SPEED || defined(__DOXYGEN__) +#define chSysLock() { \ + if (currp->p_locks++ == 0) \ + port_lock(); \ +} +#endif /* CH_OPTIMIZE_SPEED */ +#else /* !CH_USE_NESTED_LOCKS */ +#define chSysLock() port_lock() +#endif /* !CH_USE_NESTED_LOCKS */ + +/** + * @brief Leaves the kernel lock mode. + * + * @note The use of kernel lock mode is not recommended in the user code, it is + * a better idea to use the semaphores or mutexes instead. + * @see CH_USE_NESTED_LOCKS + */ +#if CH_USE_NESTED_LOCKS || defined(__DOXYGEN__) +#if CH_OPTIMIZE_SPEED || defined(__DOXYGEN__) +#define chSysUnlock() { \ + if (--currp->p_locks == 0) \ + port_unlock(); \ +} +#endif /* CH_OPTIMIZE_SPEED */ +#else /* !CH_USE_NESTED_LOCKS */ +#define chSysUnlock() port_unlock() +#endif /* !CH_USE_NESTED_LOCKS */ + +/** + * @brief Enters the kernel lock mode from within an interrupt handler. + * + * @note This API may do nothing on some architectures, it is required because + * on ports that support preemptable interrupt handlers it is required to + * raise the interrupt mask to the same level of the system mutual + * exclusion zone.
+ * It is good practice to invoke this API before invoking any I-class + * syscall from an interrupt handler. + * @note This API must be invoked exclusively from interrupt handlers. + */ +#define chSysLockFromIsr() port_lock_from_isr() + +/** + * @brief Leaves the kernel lock mode from within an interrupt handler. + * + * @note This API may do nothing on some architectures, it is required because + * on ports that support preemptable interrupt handlers it is required to + * raise the interrupt mask to the same level of the system mutual + * exclusion zone.
+ * It is good practice to invoke this API after invoking any I-class + * syscall from an interrupt handler. + * @note This API must be invoked exclusively from interrupt handlers. + */ +#define chSysUnlockFromIsr() port_unlock_from_isr() + +/** + * @brief IRQ handler enter code. + * + * @note Usually IRQ handlers functions are also declared naked. + * @note On some architectures this macro can be empty. + */ +#define CH_IRQ_PROLOGUE() PORT_IRQ_PROLOGUE() + +/** + * @brief IRQ handler exit code. + * + * @note Usually IRQ handlers function are also declared naked. + * @note This macro usually performs the final reschedulation by using + * @p chSchRescRequiredI() and @p chSchDoRescheduleI(). + */ +#define CH_IRQ_EPILOGUE() PORT_IRQ_EPILOGUE() + +/** + * @brief Standard IRQ handler declaration. + * + * @note @p id can be a function name or a vector number depending on the + * port implementation. + */ +#define CH_IRQ_HANDLER(id) PORT_IRQ_HANDLER(id) + +#ifdef __cplusplus +extern "C" { +#endif + void chSysInit(void); + void chSysTimerHandlerI(void); +#if CH_USE_NESTED_LOCKS && !CH_OPTIMIZE_SPEED + void chSysLock(void); + void chSysUnlock(void); +#endif /* CH_USE_NESTED_LOCKS && !CH_OPTIMIZE_SPEED */ +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_H_ */ + +/** @} */ diff --git a/os/kernel/include/threads.h b/os/kernel/include/threads.h new file mode 100644 index 000000000..07e068854 --- /dev/null +++ b/os/kernel/include/threads.h @@ -0,0 +1,269 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file threads.h + * @brief Threads macros and structures. + * @addtogroup Threads + * @{ + */ + +#ifndef _THREADS_H_ +#define _THREADS_H_ + +/** + * @brief Structure representing a thread. + * + * @extends ThreadsQueue + * @note Not all the listed fields are always needed, by switching off some + * not needed ChibiOS/RT subsystems it is possible to save RAM space by + * shrinking the @p Thread structure. + */ +struct Thread { + Thread *p_next; /**< Next @p Thread in the threads + list/queue.*/ + /* End of the fields shared with the ThreadsList structure. */ + Thread *p_prev; /**< Previous @p Thread in the threads + queue.*/ + /* End of the fields shared with the ThreadsQueue structure. */ + tprio_t p_prio; /**< Thread priority.*/ + /* End of the fields shared with the ReadyList structure. */ + tstate_t p_state; /**< Current thread state.*/ + tmode_t p_flags; /**< Various flags.*/ + struct context p_ctx; /**< Processor context.*/ +#if CH_USE_NESTED_LOCKS + cnt_t p_locks; /**< Number of nested locks.*/ +#endif +#if CH_DBG_THREADS_PROFILING + volatile systime_t p_time; /**< Consumed time. + @note This field can overflow.*/ +#endif + /* + * The following fields are merged in unions because they are all + * state-specific fields. This trick saves some extra space for each + * thread in the system. + */ + union { + msg_t p_rdymsg; /**< Thread wakeup code.*/ + msg_t p_exitcode; /**< The thread exit code + (@p PREXIT state).*/ + void *p_wtobjp; /**< Generic kernel object pointer used + for opaque access.*/ +#if CH_USE_SEMAPHORES + Semaphore *p_wtsemp; /**< Semaphore where the thread is + waiting on (@p PRWTSEM state).*/ +#endif +#if CH_USE_MUTEXES + Mutex *p_wtmtxp; /**< Mutex where the thread is waiting + on (@p PRWTMTX state).*/ +#endif +#if CH_USE_CONDVARS + CondVar *p_wtcondp; /**< CondVar where the thread is + waiting on (@p PRWTCOND state).*/ +#endif +#if CH_USE_MESSAGES + Thread *p_wtthdp; /**< Destination thread for message + send @p PRSNDMSG state).*/ +#endif +#if CH_USE_EVENTS + eventmask_t p_ewmask; /**< Enabled events mask (@p PRWTOREVT + or @p PRWTANDEVT states).*/ +#endif + }; + /* + * Start of the optional fields. + */ +#if CH_USE_WAITEXIT + Thread *p_waiting; /**< Thread waiting for termination.*/ +#endif +#if CH_USE_MESSAGES + ThreadsQueue p_msgqueue; /**< Message queue.*/ + msg_t p_msg; /**< The message.*/ +#endif +#if CH_USE_EVENTS + eventmask_t p_epending; /**< Pending events mask.*/ +#endif +#if CH_USE_MUTEXES + Mutex *p_mtxlist; /**< List of the mutexes owned by this + thread, @p NULL terminated.*/ + tprio_t p_realprio; /**< Thread's own, non-inherited, + priority.*/ +#endif +#if CH_USE_DYNAMIC && CH_USE_MEMPOOLS + void *p_mpool; /**< Memory Pool where the thread + workspace is returned.*/ +#endif + /* Extra fields defined in chconf.h */ + THREAD_EXT_FIELDS +}; + +/** Thread state: Ready to run, waiting on the ready list.*/ +#define PRREADY 0 +/** Thread state: Currently running. */ +#define PRCURR 1 +/** Thread state: Thread created in suspended state. */ +#define PRSUSPENDED 2 +/** Thread state: Waiting on a semaphore. */ +#define PRWTSEM 3 +/** Thread state: Waiting on a mutex. */ +#define PRWTMTX 4 +/** Thread state: Waiting in @p chThdSleep() or @p chThdSleepUntil(). */ +#define PRWTCOND 5 +/** Thread state: Waiting in @p chCondWait(). */ +#define PRSLEEP 6 +/** Thread state: Waiting in @p chThdWait(). */ +#define PRWAIT 7 +/** Thread state: Waiting in @p chEvtWaitOneTimeout() or + @p chEvtWaitAnyTimeout(). */ +#define PRWTOREVT 8 +/** Thread state: Waiting in @p chEvtWaitAllTimeout(). */ +#define PRWTANDEVT 9 +/** Thread state: Waiting in @p chMsgSend(). */ +#define PRSNDMSG 10 +/** Thread state: Waiting in @p chMsgWait(). */ +#define PRWTMSG 11 +/** Thread state: After termination.*/ +#define PREXIT 12 + +/* + * Various flags into the thread p_flags field. + */ +#define P_MEM_MODE_MASK 3 /* Thread memory mode mask. */ +#define P_MEM_MODE_STATIC 0 /* Thread memory mode: static. */ +#define P_MEM_MODE_HEAP 1 /* Thread memory mode: heap. */ +#define P_MEM_MODE_MEMPOOL 2 /* Thread memory mode: mempool. */ +#define P_TERMINATE 4 /* Termination requested. */ + +/* Not an API, don't use into the application code.*/ +Thread *init_thread(Thread *tp, tprio_t prio); + +/** Thread function.*/ +typedef msg_t (*tfunc_t)(void *); + +/* + * Threads APIs. + */ +#ifdef __cplusplus +extern "C" { +#endif + Thread *chThdInit(void *wsp, size_t size, + tprio_t prio, tfunc_t pf, void *arg); + Thread *chThdCreateStatic(void *wsp, size_t size, + tprio_t prio, tfunc_t pf, void *arg); +#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP + Thread *chThdCreateFromHeap(size_t size, tprio_t prio, + tfunc_t pf, void *arg); +#endif +#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS + Thread *chThdCreateFromMemoryPool(MemoryPool *mp, tprio_t prio, + tfunc_t pf, void *arg); +#endif + tprio_t chThdSetPriority(tprio_t newprio); + Thread *chThdResume(Thread *tp); + void chThdTerminate(Thread *tp); + void chThdSleep(systime_t time); + void chThdSleepUntil(systime_t time); + void chThdExit(msg_t msg); +#if CH_USE_WAITEXIT + msg_t chThdWait(Thread *tp); +#endif +#ifdef __cplusplus +} +#endif + +/** Returns the pointer to the @p Thread currently in execution.*/ +#define chThdSelf() currp + +/** Returns the current thread priority.*/ +#define chThdGetPriority() (currp->p_prio) + +/** Returns the pointer to the @p Thread local storage area, if any.*/ +#define chThdLS() (void *)(currp + 1) + +/** + * Verifies if the specified thread is in the @p PREXIT state. + * + * @param[in] tp the pointer to the thread + * @retval TRUE thread terminated. + * @retval FALSE thread not terminated. + */ +#define chThdTerminated(tp) ((tp)->p_state == PREXIT) + +/** + * Verifies if the current thread has a termination request pending. + * + * @retval TRUE termination request pended. + * @retval FALSE termination request not pended. + */ +#define chThdShouldTerminate() (currp->p_flags & P_TERMINATE) + +/** + * Resumes a thread created with @p chThdInit(). + * + * @param[in] tp the pointer to the thread + */ +#define chThdResumeI(tp) chSchReadyI(tp) + +/** + * Suspends the invoking thread for the specified time. + * + * @param[in] time the delay in system ticks, the special values are handled as + * follow: + * - @a TIME_INFINITE the thread enters an infinite sleep + * state. + * - @a TIME_IMMEDIATE this value is accepted but interpreted + * as a normal time specification not as an immediate timeout + * specification. + * . + */ +#define chThdSleepS(time) chSchGoSleepTimeoutS(PRSLEEP, time) + +/** + * Delays the invoking thread for the specified number of seconds. + * + * @param[in] sec the time in seconds + * @note The specified time is rounded up to a value allowed by the real + * system clock. + * @note The maximum specified value is implementation dependent. + */ +#define chThdSleepSeconds(sec) chThdSleep(S2ST(sec)) + +/** + * Delays the invoking thread for the specified number of milliseconds. + * + * @param[in] msec the time in milliseconds + * @note The specified time is rounded up to a value allowed by the real + * system clock. + * @note The maximum specified value is implementation dependent. + */ +#define chThdSleepMilliseconds(msec) chThdSleep(MS2ST(msec)) + +/** + * Delays the invoking thread for the specified number of microseconds. + * + * @param[in] usec the time in microseconds + * @note The specified time is rounded up to a value allowed by the real + * system clock. + * @note The maximum specified value is implementation dependent. + */ +#define chThdSleepMicroseconds(usec) chThdSleep(US2ST(usec)) + +#endif /* _THREADS_H_ */ + +/** @} */ diff --git a/os/kernel/include/vt.h b/os/kernel/include/vt.h new file mode 100644 index 000000000..003cffd45 --- /dev/null +++ b/os/kernel/include/vt.h @@ -0,0 +1,125 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file vt.h + * @brief Time macros and structures. + * @addtogroup Time + * @{ + */ + +#ifndef _VT_H_ +#define _VT_H_ + +/** + * Time conversion utility. Converts from seconds to system ticks number. + */ +#define S2ST(sec) ((systime_t)((sec) * CH_FREQUENCY)) + +/** + * Time conversion utility. Converts from milliseconds to system ticks number. + * @note The result is rounded upward to the next tick boundary. + */ +#define MS2ST(msec) ((systime_t)(((((msec) - 1L) * CH_FREQUENCY) / 1000L) + 1L)) + +/** + * Time conversion utility. Converts from microseconds to system ticks number. + * @note The result is rounded upward to the next tick boundary. + */ +#define US2ST(usec) ((systime_t)(((((usec) - 1L) * CH_FREQUENCY) / 1000000L) + 1L)) + +/** Virtual Timer callback function.*/ +typedef void (*vtfunc_t)(void *); + +typedef struct VirtualTimer VirtualTimer; + +/** + * @brief Virtual Timer descriptor structure. + * @extends DeltaList + */ +struct VirtualTimer { + VirtualTimer *vt_next; /**< Next timer in the delta list.*/ + VirtualTimer *vt_prev; /**< Previous timer in the delta list.*/ + systime_t vt_time; /**< Time delta before timeout.*/ + vtfunc_t vt_func; /**< Timer callback function pointer. + The pointer is reset to zero after + the callback is invoked.*/ + void *vt_par; /**< Timer callback function + parameter.*/ +}; + +/** + * @brief Virtual timers list header. + * @note The delta list is implemented as a double link bidirectional list in + * order to make the unlink time constant, the reset of a virtual timer + * is often used in the code. + */ +typedef struct { + VirtualTimer *vt_next; /**< Next timer in the delta list (the + one that will be triggered next).*/ + VirtualTimer *vt_prev; /**< Last timer in the delta list.*/ + systime_t vt_time; /**< Must be initialized to -1.*/ + volatile systime_t vt_systime; /**< System Time counter.*/ +} VTList; + +extern VTList vtlist; + +#define chVTDoTickI() { \ + vtlist.vt_systime++; \ + if (&vtlist != (VTList *)vtlist.vt_next) { \ + VirtualTimer *vtp; \ + \ + --vtlist.vt_next->vt_time; \ + while (!(vtp = vtlist.vt_next)->vt_time) { \ + vtfunc_t fn = vtp->vt_func; \ + vtp->vt_func = NULL; \ + (vtp->vt_next->vt_prev = (void *)&vtlist)->vt_next = vtp->vt_next;\ + fn(vtp->vt_par); \ + } \ + } \ +} + +/* + * Virtual Timers APIs. + */ +#ifdef __cplusplus +extern "C" { +#endif + void vt_init(void); + void chVTSetI(VirtualTimer *vtp, systime_t time, vtfunc_t vtfunc, void *par); + void chVTResetI(VirtualTimer *vtp); + bool_t chTimeIsWithin(systime_t start, systime_t end); +#ifdef __cplusplus +} +#endif + +/** Returns TRUE if the speciified timer is armed.*/ +#define chVTIsArmedI(vtp) ((vtp)->vt_func != NULL) + +/** + * Returns the number of system ticks since the @p chSysInit() invocation. + * @return the system ticks number + * @note The counter can reach its maximum and then returns to zero. + * @note This function is designed to work with the @p chThdSleepUntil(). + */ +#define chTimeNow() (vtlist.vt_systime) + +#endif /* _VT_H_ */ + +/** @} */ diff --git a/os/kernel/kernel.mk b/os/kernel/kernel.mk new file mode 100644 index 000000000..29644a5a9 --- /dev/null +++ b/os/kernel/kernel.mk @@ -0,0 +1,13 @@ +# List of all the ChibiOS/RT kernel files, there is no need to remove the files +# from this list, you can disable parts of the kernel by editing chconf.h. +KERNSRC = ../../os/kernel/src/chsys.c ../../os/kernel/src/chdebug.c \ + ../../os/kernel/src/chlists.c ../../os/kernel/src/chvt.c \ + ../../os/kernel/src/chschd.c ../../os/kernel/src/chthreads.c \ + ../../os/kernel/src/chsem.c ../../os/kernel/src/chmtx.c \ + ../../os/kernel/src/chcond.c ../../os/kernel/src/chevents.c \ + ../../os/kernel/src/chmsg.c ../../os/kernel/src/chmboxes.c \ + ../../os/kernel/src/chqueues.c ../../os/kernel/src/chheap.c \ + ../../os/kernel/src/chmempools.c ../../os/kernel/src/chserial.c + +# Required include directories +KERNINC = ../../os/kernel/include diff --git a/os/kernel/readme.txt b/os/kernel/readme.txt new file mode 100644 index 000000000..9a15d02f1 --- /dev/null +++ b/os/kernel/readme.txt @@ -0,0 +1 @@ +ChibiOS/RT portable kernel code \ No newline at end of file diff --git a/os/kernel/src/chcond.c b/os/kernel/src/chcond.c new file mode 100644 index 000000000..ef58f1ddf --- /dev/null +++ b/os/kernel/src/chcond.c @@ -0,0 +1,227 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + Concepts and parts of this file are contributed by and Copyright (C) 2008 + of Leon Woestenberg. + */ + +/** + * @file chcond.c + * @brief Condition Variables code. + * @addtogroup CondVars + * @{ + */ + +#include + +#if CH_USE_CONDVARS && CH_USE_MUTEXES + +/** + * @brief Initializes s @p CondVar structure. + * + * @param[out] cp pointer to a @p CondVar structure + * @note This function can be invoked from within an interrupt handler even if + * it is not an I-Class API because it does not touch any critical kernel + * data structure. + */ +void chCondInit(CondVar *cp) { + + chDbgCheck(cp != NULL, "chCondInit"); + + queue_init(&cp->c_queue); +} + +/** + * @brief Signals one thread that is waiting on the condition variable. + * + * @param[in] cp pointer to the @p CondVar structure + */ +void chCondSignal(CondVar *cp) { + + chDbgCheck(cp != NULL, "chCondSignal"); + + chSysLock(); + if (notempty(&cp->c_queue)) /* any thread ? */ + chSchWakeupS(fifo_remove(&cp->c_queue), RDY_OK); + chSysUnlock(); +} + +/** + * @brief Signals one thread that is waiting on the condition variable. + * + * @param[in] cp pointer to the @p CondVar structure + */ +void chCondSignalI(CondVar *cp) { + + chDbgCheck(cp != NULL, "chCondSignalI"); + + if (notempty(&cp->c_queue)) /* any thread ? */ + chSchReadyI(fifo_remove(&cp->c_queue))->p_rdymsg = RDY_OK; +} + +/** + * @brief Signals all threads that are waiting on the condition variable. + * + * @param[in] cp pointer to the @p CondVar structure + */ +void chCondBroadcast(CondVar *cp) { + + chSysLock(); + chCondBroadcastI(cp); + chSchRescheduleS(); + chSysUnlock(); +} + +/** + * @brief Signals all threads that are waiting on the condition variable. + * + * @param[in] cp pointer to the @p CondVar structure + */ +void chCondBroadcastI(CondVar *cp) { + + chDbgCheck(cp != NULL, "chCondBroadcastI"); + + /* empties the condition variable queue and inserts all the Threads into the + * ready list in FIFO order. The wakeup message is set to @p RDY_RESET in + * order to make a chCondBroadcast() detectable from a chCondSignal(). */ + while (cp->c_queue.p_next != (void *)&cp->c_queue) + chSchReadyI(fifo_remove(&cp->c_queue))->p_rdymsg = RDY_RESET; +} + +/** + * @brief Waits on the condition variable releasing the mutex lock. + * @details Releases the mutex, waits on the condition variable, and finally + * acquires the mutex again. This is done atomically. + * + * @param[in] cp pointer to the @p CondVar structure + * @return The wakep mode. + * @retval RDY_OK if the condvar was signaled using chCondSignal(). + * @retval RDY_RESET if the condvar was signaled using chCondBroadcast(). + * @note The thread MUST already have locked the mutex when calling + * @p chCondWait(). + */ +msg_t chCondWait(CondVar *cp) { + msg_t msg; + + chSysLock(); + msg = chCondWaitS(cp); + chSysUnlock(); + return msg; +} + +/** + * @brief Waits on the condition variable releasing the mutex lock. + * @details Releases the mutex, waits on the condition variable, and finally + * acquires the mutex again. This is done atomically. + * + * @param[in] cp pointer to the @p CondVar structure + * @return The wakep mode. + * @retval RDY_OK if the condvar was signaled using chCondSignal(). + * @retval RDY_RESET if the condvar was signaled using chCondBroadcast(). + * @note The thread MUST already have locked the mutex when calling + * @p chCondWaitS(). + */ +msg_t chCondWaitS(CondVar *cp) { + Mutex *mp; + msg_t msg; + + chDbgCheck(cp != NULL, "chCondWaitS"); + chDbgAssert(currp->p_mtxlist != NULL, + "chCondWaitS(), #1", + "not owning a mutex"); + + mp = chMtxUnlockS(); /* unlocks the condvar mutex */ + prio_insert(currp, &cp->c_queue); /* enters the condvar queue */ + currp->p_wtcondp = cp; /* needed by the tracer */ + chSchGoSleepS(PRWTCOND); /* waits on the condvar */ + msg = currp->p_rdymsg; /* fetches the wakeup message */ + chMtxLockS(mp); /* atomically relocks the mutex */ + return msg; /* returns the wakeup message */ +} + +#if CH_USE_CONDVARS_TIMEOUT +/** + * @brief Waits on the condition variable releasing the mutex lock. + * @details Releases the mutex, waits on the condition variable, and finally + * acquires the mutex again. This is done atomically. + * + * @param[in] cp pointer to the @p CondVar structure + * @param[in] time the number of ticks before the operation timeouts, + * the special value @p TIME_INFINITE is allowed. + * It is not possible to specify zero @p TIME_IMMEDIATE + * as timeout specification because it would make no sense + * in this function. + * @return The wakep mode. + * @retval RDY_OK if the condvar was signaled using chCondSignal(). + * @retval RDY_RESET if the condvar was signaled using chCondBroadcast(). + * @retval RDY_TIMEOUT if the condvar was not signaled within the specified + * timeout. + * @note The thread MUST already have locked the mutex when calling + * @p chCondWaitTimeout(). + */ +msg_t chCondWaitTimeout(CondVar *cp, systime_t time) { + msg_t msg; + + chSysLock(); + msg = chCondWaitTimeoutS(cp, time); + chSysUnlock(); + return msg; +} + +/** + * @brief Waits on the condition variable releasing the mutex lock. + * @details Releases the mutex, waits on the condition variable, and finally + * acquires the mutex again. This is done atomically. + * + * @param[in] cp pointer to the @p CondVar structure + * @param[in] time the number of ticks before the operation timeouts, + * the special value @p TIME_INFINITE is allowed. + * It is not possible to specify zero @p TIME_IMMEDIATE + * as timeout specification because it would make no sense + * in this function. + * @return The wakep mode. + * @retval RDY_OK if the condvar was signaled using chCondSignal(). + * @retval RDY_RESET if the condvar was signaled using chCondBroadcast(). + * @retval RDY_TIMEOUT if the condvar was not signaled within the specified + * timeout. + * @note The thread MUST already have locked the mutex when calling + * @p chCondWaitTimeoutS(). + */ +msg_t chCondWaitTimeoutS(CondVar *cp, systime_t time) { + Mutex *mp; + msg_t msg; + + chDbgCheck(cp != NULL, "chCondWaitTimeoutS"); + chDbgAssert(currp->p_mtxlist != NULL, + "chCondWaitTimeoutS(), #1", + "not owning a mutex"); + + mp = chMtxUnlockS(); /* unlocks the condvar mutex */ + prio_insert(currp, &cp->c_queue); /* enters the condvar queue */ + currp->p_wtcondp = cp; /* needed by the tracer */ + chSchGoSleepTimeoutS(PRWTCOND, time); /* waits on the condvar */ + msg = currp->p_rdymsg; /* fetches the wakeup message */ + chMtxLockS(mp); /* atomically relocks the mutex */ + return msg; /* returns the wakeup message */ +} +#endif /* CH_USE_CONDVARS_TIMEOUT */ + +#endif /* CH_USE_CONDVARS && CH_USE_MUTEXES */ + +/** @} */ diff --git a/os/kernel/src/chdebug.c b/os/kernel/src/chdebug.c new file mode 100644 index 000000000..9a8120b29 --- /dev/null +++ b/os/kernel/src/chdebug.c @@ -0,0 +1,81 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chdebug.c + * @brief ChibiOS/RT Debug code. + * @addtogroup Debug + * @{ + */ + +#include + +#if CH_DBG_ENABLE_TRACE +/** + * @brief Public trace buffer. + */ +TraceBuffer trace_buffer; + +/** + * @brief Trace circular buffer subsystem initialization. + */ +void trace_init(void) { + + trace_buffer.tb_size = TRACE_BUFFER_SIZE; + trace_buffer.tb_ptr = &trace_buffer.tb_buffer[0]; +} + +/** + * @brief Inserts in the circular debug trace buffer a context switch record. + * + * @param[in] otp the thread being switched out + * @param[in] ntp the thread to be switched in + */ +void chDbgTrace(Thread *otp, Thread *ntp) { + + trace_buffer.tb_ptr->cse_wtobjp = otp->p_wtobjp; + trace_buffer.tb_ptr->cse_time = chTimeNow(); + trace_buffer.tb_ptr->cse_state = otp->p_state; + trace_buffer.tb_ptr->cse_tid = (unsigned)ntp >> 4; + if (++trace_buffer.tb_ptr >= &trace_buffer.tb_buffer[TRACE_BUFFER_SIZE]) + trace_buffer.tb_ptr = &trace_buffer.tb_buffer[0]; +} +#endif /* CH_DBG_ENABLE_TRACE */ + +#if CH_DBG_ENABLE_ASSERTS || CH_DBG_ENABLE_CHECKS || CH_DBG_ENABLE_STACK_CHECK +/** + * @brief Pointer to the panic message. + * @details This pointer is meant to be accessed through the debugger, it is + * written once and then the system is halted. + */ +char *panic_msg; + +/** + * @brief Prints a panic message on the console and then halts the system. + * + * @param[in] msg the pointer to the panic message string + */ +void chDbgPanic(char *msg) { + + panic_msg = msg; + chSysHalt(); +} +#endif /* CH_DBG_ENABLE_ASSERTS || CH_DBG_ENABLE_CHECKS || CH_DBG_ENABLE_STACK_CHECK */ + +/** @} */ diff --git a/os/kernel/src/chevents.c b/os/kernel/src/chevents.c new file mode 100644 index 000000000..098adce5e --- /dev/null +++ b/os/kernel/src/chevents.c @@ -0,0 +1,392 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chevents.c + * @brief Events code. + * @addtogroup Events + * @{ + */ +#include + +#if CH_USE_EVENTS +/** + * @brief Registers an Event Listener on an Event Source. + * + * @param[in] esp pointer to the @p EventSource structure + * @param[in] elp pointer to the @p EventListener structure + * @param[in] emask the mask of event flags to be pended to the thread when the + * event source is broadcasted + * @note Multiple Event Listeners can specify the same bits to be pended. + */ +void chEvtRegisterMask(EventSource *esp, EventListener *elp, eventmask_t emask) { + + chDbgCheck((esp != NULL) && (elp != NULL), "chEvtRegisterMask"); + + chSysLock(); + elp->el_next = esp->es_next; + esp->es_next = elp; + elp->el_listener = currp; + elp->el_mask = emask; + chSysUnlock(); +} + +/** + * @brief Unregisters an Event Listener from its Event Source. + * + * @param[in] esp pointer to the @p EventSource structure + * @param[in] elp pointer to the @p EventListener structure + * @note If the event listener is not registered on the specified event source + * then the function does nothing. + * @note For optimal performance it is better to perform the unregister + * operations in inverse order of the register operations (elements are + * found on top of the list). + */ +void chEvtUnregister(EventSource *esp, EventListener *elp) { + EventListener *p; + + chDbgCheck((esp != NULL) && (elp != NULL), "chEvtUnregister"); + + p = (EventListener *)esp; + chSysLock(); + while (p->el_next != (EventListener *)esp) { + if (p->el_next == elp) { + p->el_next = elp->el_next; + break; + } + p = p->el_next; + } + chSysUnlock(); +} + +/** + * @brief Clears the pending events specified in the mask. + * + * @param[in] mask the events to be cleared + * @return The pending events that were cleared. + */ +eventmask_t chEvtClear(eventmask_t mask) { + eventmask_t m; + + chSysLock(); + + m = currp->p_epending & mask; + currp->p_epending &= ~mask; + + chSysUnlock(); + return m; +} + +/** + * @brief Pends a set of event flags on the current thread, this is @b much + * faster than using @p chEvtBroadcast() or @p chEvtSignal(). + * + * @param[in] mask the events to be pended + * @return The current pending events mask. + */ +eventmask_t chEvtPend(eventmask_t mask) { + + chSysLock(); + + mask = (currp->p_epending |= mask); + + chSysUnlock(); + return mask; +} + +/** + * @brief Pends a set of event flags on the specified @p Thread. + * + * @param[in] tp the thread to be signaled + * @param[in] mask the event flags set to be pended + */ +void chEvtSignal(Thread *tp, eventmask_t mask) { + + chDbgCheck(tp != NULL, "chEvtSignal"); + + chSysLock(); + chEvtSignalI(tp, mask); + chSysUnlock(); +} + +/** + * @brief Pends a set of event flags on the specified @p Thread. + * + * @param[in] tp the thread to be signaled + * @param[in] mask the event flags set to be pended + */ +void chEvtSignalI(Thread *tp, eventmask_t mask) { + + chDbgCheck(tp != NULL, "chEvtSignalI"); + + tp->p_epending |= mask; + /* Test on the AND/OR conditions wait states.*/ + if (((tp->p_state == PRWTOREVT) && ((tp->p_epending & tp->p_ewmask) != 0)) || + ((tp->p_state == PRWTANDEVT) && ((tp->p_epending & tp->p_ewmask) == tp->p_ewmask))) + chSchReadyI(tp)->p_rdymsg = RDY_OK; +} + +/** + * @brief Signals all the Event Listeners registered on the specified Event + * Source. + * + * @param[in] esp pointer to the @p EventSource structure + */ +void chEvtBroadcast(EventSource *esp) { + + chSysLock(); + chEvtBroadcastI(esp); + chSchRescheduleS(); + chSysUnlock(); +} + +/** + * @brief Signals all the Event Listeners registered on the specified Event + * Source. + * + * @param[in] esp pointer to the @p EventSource structure + */ +void chEvtBroadcastI(EventSource *esp) { + EventListener *elp; + + chDbgCheck(esp != NULL, "chEvtBroadcastI"); + + elp = esp->es_next; + while (elp != (EventListener *)esp) { + chEvtSignalI(elp->el_listener, elp->el_mask); + elp = elp->el_next; + } +} + +/** + * @brief Invokes the event handlers associated with a mask. + * + * @param[in] mask mask of the events to be dispatched + * @param[in] handlers an array of @p evhandler_t. The array must have size + * equal to the number of bits in eventmask_t. + */ +void chEvtDispatch(const evhandler_t handlers[], eventmask_t mask) { + eventid_t eid; + + chDbgCheck(handlers != NULL, "chEvtDispatch"); + + eid = 0; + while (mask) { + if (mask & EVENT_MASK(eid)) { + chDbgAssert(handlers[eid] != NULL, + "chEvtDispatch(), #1", + "null handler"); + mask &= ~EVENT_MASK(eid); + handlers[eid](eid); + } + eid++; + } +} + +#if CH_OPTIMIZE_SPEED || !CH_USE_EVENTS_TIMEOUT || defined(__DOXYGEN__) +/** + * @brief Waits for exactly one of the specified events. + * @details The function waits for one event among those specified in + * @p ewmask to become pending then the event is cleared and returned. + * + * @param[in] ewmask mask of the events that the function should wait for, + * @p ALL_EVENTS enables all the events + * @return The mask of the lowest id served and cleared event. + * @note One and only one event is served in the function, the one with the + * lowest event id. The function is meant to be invoked into a loop in + * order to serve all the pending events.
+ * This means that Event Listeners with a lower event identifier have + * an higher priority. + */ +eventmask_t chEvtWaitOne(eventmask_t ewmask) { + eventmask_t m; + + chSysLock(); + + if ((m = (currp->p_epending & ewmask)) == 0) { + currp->p_ewmask = ewmask; + chSchGoSleepS(PRWTOREVT); + m = currp->p_epending & ewmask; + } + m &= -m; + currp->p_epending &= ~m; + + chSysUnlock(); + return m; +} + +/** + * @brief Waits for any of the specified events. + * @details The function waits for any event among those specified in + * @p ewmask to become pending then the events are cleared and returned. + * + * @param[in] ewmask mask of the events that the function should wait for, + * @p ALL_EVENTS enables all the events + * @return The mask of the served and cleared events. + */ +eventmask_t chEvtWaitAny(eventmask_t ewmask) { + eventmask_t m; + + chSysLock(); + + if ((m = (currp->p_epending & ewmask)) == 0) { + currp->p_ewmask = ewmask; + chSchGoSleepS(PRWTOREVT); + m = currp->p_epending & ewmask; + } + currp->p_epending &= ~m; + + chSysUnlock(); + return m; +} + +/** + * @brief Waits for all the specified events. + * @details The function waits for all the events specified in @p ewmask to + * become pending then the events are cleared and returned. + * + * @param[in] ewmask mask of the event ids that the function should wait for + * @return The mask of the served and cleared events. + */ +eventmask_t chEvtWaitAll(eventmask_t ewmask) { + + chSysLock(); + + if ((currp->p_epending & ewmask) != ewmask) { + currp->p_ewmask = ewmask; + chSchGoSleepS(PRWTANDEVT); + } + currp->p_epending &= ~ewmask; + + chSysUnlock(); + return ewmask; +} +#endif /* CH_OPTIMIZE_SPEED || !CH_USE_EVENTS_TIMEOUT */ + +#if CH_USE_EVENTS_TIMEOUT +/** + * @brief Waits for exactly one of the specified events. + * @details The function waits for one event among those specified in + * @p ewmask to become pending then the event is cleared and returned. + * + * @param[in] ewmask mask of the events that the function should wait for, + * @p ALL_EVENTS enables all the events + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the lowest id served and cleared event. + * @retval 0 if the specified timeout expired. + * @note One and only one event is served in the function, the one with the + * lowest event id. The function is meant to be invoked into a loop in + * order to serve all the pending events.
+ * This means that Event Listeners with a lower event identifier have + * an higher priority. + */ +eventmask_t chEvtWaitOneTimeout(eventmask_t ewmask, systime_t time) { + eventmask_t m; + + chSysLock(); + + if ((m = (currp->p_epending & ewmask)) == 0) { + if (TIME_IMMEDIATE == time) + return (eventmask_t)0; + currp->p_ewmask = ewmask; + if (chSchGoSleepTimeoutS(PRWTOREVT, time) < RDY_OK) + return (eventmask_t)0; + m = currp->p_epending & ewmask; + } + m &= -m; + currp->p_epending &= ~m; + + chSysUnlock(); + return m; +} + +/** + * @brief Waits for any of the specified events. + * @details The function waits for any event among those specified in + * @p ewmask to become pending then the events are cleared and + * returned. + * + * @param[in] ewmask mask of the events that the function should wait for, + * @p ALL_EVENTS enables all the events + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the served and cleared events. + * @retval 0 if the specified timeout expired. + */ +eventmask_t chEvtWaitAnyTimeout(eventmask_t ewmask, systime_t time) { + eventmask_t m; + + chSysLock(); + + if ((m = (currp->p_epending & ewmask)) == 0) { + if (TIME_IMMEDIATE == time) + return (eventmask_t)0; + currp->p_ewmask = ewmask; + if (chSchGoSleepTimeoutS(PRWTOREVT, time) < RDY_OK) + return (eventmask_t)0; + m = currp->p_epending & ewmask; + } + currp->p_epending &= ~m; + + chSysUnlock(); + return m; +} + +/** + * @brief Waits for all the specified events. + * @details The function waits for all the events specified in @p ewmask to + * become pending then the events are cleared and returned. + * + * @param[in] ewmask mask of the event ids that the function should wait for + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the served and cleared events. + * @retval 0 if the specified timeout expired. + */ +eventmask_t chEvtWaitAllTimeout(eventmask_t ewmask, systime_t time) { + + chSysLock(); + + if ((currp->p_epending & ewmask) != ewmask) { + if (TIME_IMMEDIATE == time) + return (eventmask_t)0; + currp->p_ewmask = ewmask; + if (chSchGoSleepTimeoutS(PRWTANDEVT, time) < RDY_OK) + return (eventmask_t)0; + } + currp->p_epending &= ~ewmask; + + chSysUnlock(); + return ewmask; +} +#endif /* CH_USE_EVENTS_TIMEOUT */ + +#endif /* CH_USE_EVENTS */ + +/** @} */ diff --git a/os/kernel/src/chheap.c b/os/kernel/src/chheap.c new file mode 100644 index 000000000..82b1ba785 --- /dev/null +++ b/os/kernel/src/chheap.c @@ -0,0 +1,276 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chheap.c + * @brief Heap code. + * @addtogroup Heap + * @{ + */ + +#include + +#if CH_USE_HEAP + +#if !CH_USE_MALLOC_HEAP + +#define MAGIC 0xF5A0 +#define ALIGN_TYPE void * +#define ALIGN_MASK (sizeof(ALIGN_TYPE) - 1) +#define ALIGN_SIZE(p) (((size_t)(p) + ALIGN_MASK) & ~ALIGN_MASK) + +struct header { + union { + struct header *h_next; + size_t h_magic; + }; + size_t h_size; +}; + +static struct { + struct header free; /* Guaranteed to be not adjacent to the heap */ +#if CH_USE_MUTEXES +#define H_LOCK() chMtxLock(&heap.hmtx) +#define H_UNLOCK() chMtxUnlock() + Mutex hmtx; +#elif CH_USE_SEMAPHORES +#define H_LOCK() chSemWait(&heap.hsem) +#define H_UNLOCK() chSemSignal(&heap.hsem) + Semaphore hsem; +#else +#error "The heap allocator requires mutexes or semaphores to be enabled" +#endif +#if CH_HEAP_SIZE > 0 + union { + ALIGN_TYPE alignment; + char buffer[ALIGN_SIZE(CH_HEAP_SIZE)]; + }; +#endif +} heap; + +/** + * @brief Initializes the allocator subsystem. + * + * @note Internal use only. + */ +void heap_init(void) { + struct header *hp; + +#if CH_HEAP_SIZE == 0 + extern char __heap_base__; + extern char __heap_end__; + + hp = (void *)&__heap_base__; + hp->h_size = &__heap_end__ - &__heap_base__ - sizeof(struct header); +#else + hp = (void *)&heap.buffer[0]; + hp->h_size = (&heap.buffer[ALIGN_SIZE(CH_HEAP_SIZE)] - &heap.buffer[0]) - + sizeof(struct header); +#endif + hp->h_next = NULL; + heap.free.h_next = hp; + heap.free.h_size = 0; +#if CH_USE_MUTEXES + chMtxInit(&heap.hmtx); +#else + chSemInit(&heap.hsem, 1); +#endif +} + +/** + * @brief Allocates a block of memory from the heap by using the first-fit + * algorithm. + * @details The allocated block is guaranteed to be properly aligned for a + * pointer data type. + * + * @param[in] size the size of the block to be allocated. Note that the + * allocated block may be a bit bigger than the requested + * size for alignment and fragmentation reasons. + * @return A pointer to the allocated block. + * @retval NULL if the block cannot be allocated. + */ +void *chHeapAlloc(size_t size) { + struct header *qp, *hp, *fp; + + size = ALIGN_SIZE(size); + qp = &heap.free; + H_LOCK(); + + while (qp->h_next != NULL) { + hp = qp->h_next; + if (hp->h_size >= size) { + if (hp->h_size < size + sizeof(struct header)) { + /* Gets the whole block even if it is slightly bigger than the + requested size because the fragment would be too small to be + useful */ + qp->h_next = hp->h_next; + } + else { + /* Block bigger enough, must split it */ + fp = (void *)((char *)(hp) + sizeof(struct header) + size); + fp->h_next = hp->h_next; + fp->h_size = hp->h_size - sizeof(struct header) - size; + qp->h_next = fp; + hp->h_size = size; + } + hp->h_magic = MAGIC; + + H_UNLOCK(); + return (void *)(hp + 1); + } + qp = hp; + } + + H_UNLOCK(); + return NULL; +} + +#define LIMIT(p) (struct header *)((char *)(p) + \ + sizeof(struct header) + \ + (p)->h_size) + +/** + * @brief Frees a previously allocated memory block. + * + * @param[in] p the memory block pointer + */ +void chHeapFree(void *p) { + struct header *qp, *hp; + + chDbgCheck(p != NULL, "chHeapFree"); + + hp = (struct header *)p - 1; + chDbgAssert(hp->h_magic == MAGIC, + "chHeapFree(), #1", + "it is not magic"); + qp = &heap.free; + H_LOCK(); + + while (TRUE) { + + chDbgAssert((hp < qp) || (hp >= LIMIT(qp)), + "chHeapFree(), #2", + "within free block"); + + if (((qp == &heap.free) || (hp > qp)) && + ((qp->h_next == NULL) || (hp < qp->h_next))) { + /* Insertion after qp */ + hp->h_next = qp->h_next; + qp->h_next = hp; + /* Verifies if the newly inserted block should be merged */ + if (LIMIT(hp) == hp->h_next) { + /* Merge with the next block */ + hp->h_size += hp->h_next->h_size + sizeof(struct header); + hp->h_next = hp->h_next->h_next; + } + if ((LIMIT(qp) == hp)) { /* Cannot happen when qp == &heap.free */ + /* Merge with the previous block */ + qp->h_size += hp->h_size + sizeof(struct header); + qp->h_next = hp->h_next; + } + + H_UNLOCK(); + return; + } + qp = qp->h_next; + } +} + +/** + * @brief Reports the heap status. + * + * @param[in] sizep pointer to a variable that will receive the total + * fragmented free space + * @return The number of fragments in the heap. + * @note This function is meant to be used in the test suite, it should not be + * really useful for the application code. + * @note This function is not implemented when the @p CH_USE_MALLOC_HEAP + * configuration option is used (it always returns zero). + */ +size_t chHeapStatus(size_t *sizep) { + struct header *qp; + size_t n, sz; + + H_LOCK(); + + sz = 0; + for (n = 0, qp = &heap.free; qp->h_next; n++, qp = qp->h_next) + sz += qp->h_next->h_size; + if (sizep) + *sizep = sz; + + H_UNLOCK(); + return n; +} + +#else /* CH_USE_MALLOC_HEAP */ + +#include + +#if CH_USE_MUTEXES +#define H_LOCK() chMtxLock(&hmtx) +#define H_UNLOCK() chMtxLock(&hmtx) +static Mutex hmtx; +#elif CH_USE_SEMAPHORES +#define H_LOCK() chSemWait(&hsem) +#define H_UNLOCK() chSemSignal(&hsem) +static Semaphore hsem; +#else +#error "The heap allocator requires mutexes or semaphores to be enabled" +#endif + +void heap_init(void) { + +#if CH_USE_MUTEXES + chMtxInit(&hmtx); +#else + chSemInit(&hsem, 1); +#endif +} + +void *chHeapAlloc(size_t size) { + void *p; + + H_LOCK(); + p = malloc(size); + H_UNLOCK(); + return p; +} + +void chHeapFree(void *p) { + + chDbgCheck(p != NULL, "chHeapFree"); + + H_LOCK(); + free(p); + H_UNLOCK(); +} + +size_t chHeapStatus(size_t *sizep) { + + if (sizep) + *sizep = 0; + return 0; +} + +#endif /* CH_USE_MALLOC_HEAP */ + +#endif /* CH_USE_HEAP */ + +/** @} */ diff --git a/os/kernel/src/chlists.c b/os/kernel/src/chlists.c new file mode 100644 index 000000000..a2177ca63 --- /dev/null +++ b/os/kernel/src/chlists.c @@ -0,0 +1,111 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chlists.c + * @brief Thread queues/lists code. + * @addtogroup ThreadLists + * @{ + */ +#include + +#if !CH_OPTIMIZE_SPEED || defined(__DOXYGEN__) +/** + * @brief Inserts a thread into a priority ordered queue. + * + * @param[in] tp the pointer to the thread to be inserted in the list + * @param[in] tqp the pointer to the threads list header + * @note The insertion is done by scanning the list from the highest priority + * toward the lowest. + * @note This function is @b not an API. + */ +void prio_insert(Thread *tp, ThreadsQueue *tqp) { + + /* cp iterates over the queue */ + Thread *cp = (Thread *)tqp; + do { + /* iterate to next thread in queue */ + cp = cp->p_next; + /* not end of queue? and cp has equal or higher priority than tp? */ + } while ((cp != (Thread *)tqp) && (cp->p_prio >= tp->p_prio)); + /* insert before cp, point tp to next and prev in queue */ + tp->p_prev = (tp->p_next = cp)->p_prev; + /* make prev point to tp, and cp point back to tp */ + tp->p_prev->p_next = cp->p_prev = tp; +} + +/** + * @brief Inserts a Thread into a queue. + * + * @param[in] tp the pointer to the thread to be inserted in the list + * @param[in] tqp the pointer to the threads list header + * @note This function is @b not an API. + */ +void queue_insert(Thread *tp, ThreadsQueue *tqp) { + + tp->p_prev = (tp->p_next = (Thread *)tqp)->p_prev; + tp->p_prev->p_next = tqp->p_prev = tp; +} + +/** + * @brief Removes the first-out Thread from a queue and returns it. + * + * @param[in] tqp the pointer to the threads list header + * @return The removed thread pointer. + * @note This function is @b not an API. + */ +Thread *fifo_remove(ThreadsQueue *tqp) { + Thread *tp = tqp->p_next; + + (tqp->p_next = tp->p_next)->p_prev = (Thread *)tqp; + return tp; +} + +/** + * @brief Removes the last-out Thread from a queue and returns it. + * + * @param[in] tqp the pointer to the threads list header + * @return The removed thread pointer. + * @note This function is @b not an API. + */ +Thread *lifo_remove(ThreadsQueue *tqp) { + Thread *tp = tqp->p_next; + + (tqp->p_next = tp->p_next)->p_prev = (Thread *)tqp; + return tp; +} + +/** + * @brief Removes a Thread from a queue and returns it. + * @details The thread is removed from the queue regardless of its relative + * position and regardless the used insertion method. + * + * @param[in] tp the pointer to the thread to be removed from the queue + * @return The removed thread pointer. + * @note This function is @b not an API. + */ +Thread *dequeue(Thread *tp) { + + tp->p_prev->p_next = tp->p_next; + tp->p_next->p_prev = tp->p_prev; + return tp; +} +#endif /* CH_OPTIMIZE_SPEED */ + +/** @} */ diff --git a/os/kernel/src/chmboxes.c b/os/kernel/src/chmboxes.c new file mode 100644 index 000000000..8a791a984 --- /dev/null +++ b/os/kernel/src/chmboxes.c @@ -0,0 +1,244 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chmboxes.c + * @brief Mailboxes code. + * @addtogroup Mailboxes + * @{ + */ + +#include + +#if CH_USE_MAILBOXES +/** + * @brief Initializes a Mailbox object. + * + * @param[out] mbp the pointer to the Mailbox structure to be initialized + * @param[in] buf the circular messages buffer + * @param[in] n the buffer size as number of @p msg_t + */ +void chMBInit(Mailbox *mbp, msg_t *buf, cnt_t n) { + + chDbgCheck((mbp != NULL) && (buf != NULL) && (n > 0), "chMBInit"); + + mbp->mb_buffer = mbp->mb_wrptr = mbp->mb_rdptr = buf; + mbp->mb_top = &buf[n]; + chSemInit(&mbp->mb_emptysem, n); + chSemInit(&mbp->mb_fullsem, 0); +} + +/** + * @brief Resets a Mailbox object. + * @details All the waiting threads are resumed with status @p RDY_RESET and + * the queued messages are lost. + * + * @param[in] mbp the pointer to an initialized Mailbox object + */ +void chMBReset(Mailbox *mbp) { + + chDbgCheck(mbp != NULL, "chMBReset"); + + chSysLock(); + mbp->mb_wrptr = mbp->mb_rdptr = mbp->mb_buffer; + chSemResetI(&mbp->mb_emptysem, mbp->mb_top - mbp->mb_buffer); + chSemResetI(&mbp->mb_fullsem, 0); + chSchRescheduleS(); + chSysUnlock(); +} + +/** + * @brief Posts a message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[in] msg the message to be posted on the mailbox + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the message was correctly posted. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBPost(Mailbox *mbp, msg_t msg, systime_t time) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBPostS(mbp, msg, time); + chSysUnlock(); + return rdymsg; +} + +/** + * @brief Posts a message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[in] msg the message to be posted on the mailbox + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the message was correctly posted. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBPostS(Mailbox *mbp, msg_t msg, systime_t time) { + msg_t rdymsg; + + chDbgCheck(mbp != NULL, "chMBPostS"); + + rdymsg = chSemWaitTimeoutS(&mbp->mb_emptysem, time); + if (rdymsg == RDY_OK) { + *mbp->mb_wrptr++ = msg; + if (mbp->mb_wrptr >= mbp->mb_top) + mbp->mb_wrptr = mbp->mb_buffer; + chSemSignalI(&mbp->mb_fullsem); + chSchRescheduleS(); + } + return rdymsg; +} + +/** + * @brief Posts an high priority message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[in] msg the message to be posted on the mailbox + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the message was correctly posted. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBPostAhead(Mailbox *mbp, msg_t msg, systime_t time) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBPostAheadS(mbp, msg, time); + chSysUnlock(); + return rdymsg; +} + +/** + * @brief Posts an high priority message into a mailbox. + * @details The invoking thread waits until a empty slot in the mailbox becomes + * available or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[in] msg the message to be posted on the mailbox + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the message was correctly posted. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBPostAheadS(Mailbox *mbp, msg_t msg, systime_t time) { + msg_t rdymsg; + + chDbgCheck(mbp != NULL, "chMBPostAheadS"); + + rdymsg = chSemWaitTimeoutS(&mbp->mb_emptysem, time); + if (rdymsg == RDY_OK) { + if (--mbp->mb_rdptr < mbp->mb_buffer) + mbp->mb_rdptr = mbp->mb_top - 1; + *mbp->mb_rdptr = msg; + chSemSignalI(&mbp->mb_fullsem); + chSchRescheduleS(); + } + return rdymsg; +} + +/** + * @brief Retrieves a message from a mailbox. + * @details The invoking thread waits until a message is posted in the mailbox + * or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[out] msgp pointer to a message variable for the received message + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if a message was correctly fetched. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBFetch(Mailbox *mbp, msg_t *msgp, systime_t time) { + msg_t rdymsg; + + chSysLock(); + rdymsg = chMBFetchS(mbp, msgp, time); + chSysUnlock(); + return rdymsg; +} + +/** + * @brief Retrieves a message from a mailbox. + * @details The invoking thread waits until a message is posted in the mailbox + * or the specified time runs out. + * + * @param[in] mbp the pointer to an initialized Mailbox object + * @param[out] msgp pointer to a message variable for the received message + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if a message was correctly fetched. + * @retval RDY_RESET if the mailbox was reset while waiting. + * @retval RDY_TIMEOUT if the operation timed out. + */ +msg_t chMBFetchS(Mailbox *mbp, msg_t *msgp, systime_t time) { + msg_t rdymsg; + + chDbgCheck((mbp != NULL) && (msgp != NULL), "chMBFetchS"); + + rdymsg = chSemWaitTimeoutS(&mbp->mb_fullsem, time); + if (rdymsg == RDY_OK) { + *msgp = *mbp->mb_rdptr++; + if (mbp->mb_rdptr >= mbp->mb_top) + mbp->mb_rdptr = mbp->mb_buffer; + chSemSignalI(&mbp->mb_emptysem); + chSchRescheduleS(); + } + return rdymsg; +} +#endif /* CH_USE_MAILBOXES */ + +/** @} */ diff --git a/os/kernel/src/chmempools.c b/os/kernel/src/chmempools.c new file mode 100644 index 000000000..dd6f3d1ba --- /dev/null +++ b/os/kernel/src/chmempools.c @@ -0,0 +1,112 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chmempools.c + * @brief Memory Pools code. + * @addtogroup MemoryPools + * @{ + */ + +#include + +#if CH_USE_MEMPOOLS +/** + * @brief Initializes an empty memory pool. + * + * @param[out] mp pointer to a @p MemoryPool structure + * @param[in] size the size of the objects contained in this memory pool, + * the minimum accepted size is the size of a pointer to void + */ +void chPoolInit(MemoryPool *mp, size_t size) { + + chDbgCheck((mp != NULL) && (size >= sizeof(void *)), "chPoolInit"); + + mp->mp_next = NULL; + mp->mp_object_size = size; +} + +/** + * @brief Allocates an object from a memory pool. + * + * @param[in] mp pointer to a @p MemoryPool structure + * @return The pointer to the allocated object. + * @retval NULL if pool is empty. + */ +void *chPoolAllocI(MemoryPool *mp) { + void *objp; + + chDbgCheck(mp != NULL, "chPoolAllocI"); + + if ((objp = mp->mp_next) != NULL) + mp->mp_next = mp->mp_next->ph_next; + + return objp; +} + +/** + * @brief Allocates an object from a memory pool. + * + * @param[in] mp pointer to a @p MemoryPool structure + * @return The pointer to the allocated object. + * @retval NULL if pool is empty. + */ +void *chPoolAlloc(MemoryPool *mp) { + void *objp; + + chSysLock(); + objp = chPoolAllocI(mp); + chSysUnlock(); + return objp; +} + +/** + * @brief Releases (or adds) an object into (to) a memory pool. + * + * @param[in] mp pointer to a @p MemoryPool structure + * @param[in] objp the pointer to the object to be released or added + * @note the object is assumed to be of the right size for the specified + * memory pool. + */ +void chPoolFreeI(MemoryPool *mp, void *objp) { + struct pool_header *php = objp; + + chDbgCheck((mp != NULL) && (objp != NULL), "chPoolFreeI"); + + php->ph_next = mp->mp_next; + mp->mp_next = php; +} + +/** + * @brief Releases (or adds) an object into (to) a memory pool. + * + * @param[in] mp pointer to a @p MemoryPool structure + * @param[in] objp the pointer to the object to be released or added + * @note the object is assumed to be of the right size for the specified + * memory pool. + */ +void chPoolFree(MemoryPool *mp, void *objp) { + + chSysLock(); + chPoolFreeI(mp, objp); + chSysUnlock(); +} +#endif /* CH_USE_MEMPOOLS */ + +/** @} */ diff --git a/os/kernel/src/chmsg.c b/os/kernel/src/chmsg.c new file mode 100644 index 000000000..393ab8dad --- /dev/null +++ b/os/kernel/src/chmsg.c @@ -0,0 +1,125 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chmsg.c + * @brief Messages code. + * @addtogroup Messages + * @{ + */ + +#include + +#if CH_USE_MESSAGES + +#if CH_USE_MESSAGES_PRIORITY +#define msg_insert(tp, qp) prio_insert(tp, qp) +#else +#define msg_insert(tp, qp) queue_insert(tp, qp) +#endif + +/** + * @brief Sends a message to the specified thread. + * @details The sender is stopped until the receiver executes a + * @p chMsgRelease()after receiving the message. + * + * @param[in] tp the pointer to the thread + * @param[in] msg the message + * @return The return message from @p chMsgRelease(). + */ +msg_t chMsgSend(Thread *tp, msg_t msg) { + + chDbgCheck(tp != NULL, "chMsgSend"); + + chSysLock(); + msg_insert(currp, &tp->p_msgqueue); + currp->p_msg = msg; + currp->p_wtthdp = tp; + if (tp->p_state == PRWTMSG) + chSchReadyI(tp); + chSchGoSleepS(PRSNDMSG); + msg = currp->p_rdymsg; + chSysUnlock(); + return msg; +} + +/** + * @brief Suspends the thread and waits for an incoming message. + * + * @return The pointer to the message structure. Note, it is always the + * message associated to the thread on the top of the messages queue. + * @note You can assume that the data contained in the message is stable until + * you invoke @p chMsgRelease() because the sending thread is + * suspended until then. + */ +msg_t chMsgWait(void) { + msg_t msg; + + chSysLock(); + if (!chMsgIsPendingI(currp)) + chSchGoSleepS(PRWTMSG); + msg = chMsgGetI(currp); + chSysUnlock(); + return msg; +} + +/** + * @brief Returns the next message in the queue. + * + * @return The pointer to the message structure. Note, it is always the + * message associated to the thread on the top of the messages queue. + * If the queue is empty then @p NULL is returned. + * @note You can assume that the data pointed by the message is stable until + * you invoke @p chMsgRelease() because the sending thread is + * suspended until then. Always remember that the message data is not + * copied between the sender and the receiver, just a pointer is passed. + */ +msg_t chMsgGet(void) { + msg_t msg; + + chSysLock(); + msg = chMsgIsPendingI(currp) ? chMsgGetI(currp) : (msg_t)NULL; + chSysUnlock(); + return msg; +} + +/** + * @brief Releases the thread waiting on top of the messages queue. + * + * @param[in] msg the message returned to the message sender + * @note You can call this function only if there is a message already in the + * queue else the result will be unpredictable (a crash most likely). + * Exiting from the @p chMsgWait() ensures you have at least one + * message in the queue so it is not a big deal.
+ * The condition is only tested in debug mode in order to make this code + * as fast as possible. + */ +void chMsgRelease(msg_t msg) { + + chSysLock(); + chDbgAssert(chMsgIsPendingI(currp), + "chMsgRelease(), #1", + "no message pending"); + chSchWakeupS(fifo_remove(&currp->p_msgqueue), msg); + chSysUnlock(); +} + +#endif /* CH_USE_MESSAGES */ + +/** @} */ diff --git a/os/kernel/src/chmtx.c b/os/kernel/src/chmtx.c new file mode 100644 index 000000000..5fcb6fd5e --- /dev/null +++ b/os/kernel/src/chmtx.c @@ -0,0 +1,299 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chmtx.c + * @brief Mutexes code. + * @addtogroup Mutexes + * @{ + */ + +#include + +#if CH_USE_MUTEXES + +/** + * @brief Initializes s @p Mutex structure. + * + * @param[out] mp pointer to a @p Mutex structure + * @note This function can be invoked from within an interrupt handler even if + * it is not an I-Class API because it does not touch any critical kernel + * data structure. + */ +void chMtxInit(Mutex *mp) { + + chDbgCheck(mp != NULL, "chMtxInit"); + + queue_init(&mp->m_queue); + mp->m_owner = NULL; +} + +/** + * @brief Locks the specified mutex. + * + * @param[in] mp pointer to the @p Mutex structure + */ +void chMtxLock(Mutex *mp) { + + chSysLock(); + + chMtxLockS(mp); + + chSysUnlock(); +} + +/** + * @brief Locks the specified mutex. + * + * @param[in] mp pointer to the @p Mutex structure + * @note This function must be called within a @p chSysLock() / @p chSysUnlock() + * block. + */ +void chMtxLockS(Mutex *mp) { + + chDbgCheck(mp != NULL, "chMtxLockS"); + + /* the mutex is already locked? */ + if (mp->m_owner != NULL) { + /* + * Priority inheritance protocol; explores the thread-mutex dependencies + * boosting the priority of all the affected threads to equal the priority + * of the running thread requesting the mutex. + */ + Thread *tp = mp->m_owner; + /* { tp is the thread currently owning the mutex } */ + /* the running thread has higher priority than tp? */ + while (tp->p_prio < currp->p_prio) { + /* make priority of thread tp match the running thread's priority */ + tp->p_prio = currp->p_prio; + /* + * The following states need priority queues reordering. + */ + switch (tp->p_state) { + /* thread tp is waiting on a mutex? */ + case PRWTMTX: + /* Requeues tp with its new priority on the mutex wait queue. */ + prio_insert(dequeue(tp), &tp->p_wtmtxp->m_queue); + /* boost the owner of this mutex if needed */ + tp = tp->p_wtmtxp->m_owner; + continue; +#if CH_USE_CONDVARS + case PRWTCOND: + /* Requeues tp with its new priority on the condvar queue. */ + prio_insert(dequeue(tp), &tp->p_wtcondp->c_queue); + break; +#endif +#if CH_USE_SEMAPHORES_PRIORITY + case PRWTSEM: + /* Requeues tp with its new priority on the semaphore queue. */ + prio_insert(dequeue(tp), &tp->p_wtsemp->s_queue); + break; +#endif +#if CH_USE_MESSAGES_PRIORITY + case PRSNDMSG: + /* Requeues tp with its new priority on the server thread queue. */ + prio_insert(dequeue(tp), &tp->p_wtthdp->p_msgqueue); + break; +#endif + /* thread tp is ready? */ + case PRREADY: + /* Requeue tp with its new priority on the ready list. */ + chSchReadyI(dequeue(tp)); + } + break; + } + /* sleep on the mutex */ + prio_insert(currp, &mp->m_queue); + /* thread remembers the mutex where it is waiting on */ + currp->p_wtmtxp = mp; + chSchGoSleepS(PRWTMTX); + chDbgAssert(mp->m_owner == NULL, "chMtxLockS(), #1", "still owned"); + } + /* + * The mutex is now inserted in the owned mutexes list. + */ + mp->m_owner = currp; + mp->m_next = currp->p_mtxlist; + currp->p_mtxlist = mp; +} + +/** + * @brief Tries to lock a mutex. + * @details This function does not have any overhead related to + * the priority inheritance mechanism because it does not try to + * enter a sleep state on the mutex. + * + * @param[in] mp pointer to the @p Mutex structure + * @retval TRUE if the mutex was successfully acquired + * @retval FALSE if the lock attempt failed. + */ +bool_t chMtxTryLock(Mutex *mp) { + bool_t b; + + chSysLock(); + + b = chMtxTryLockS(mp); + + chSysUnlock(); + return b; +} + +/** + * @brief Tries to lock a mutex. + * @details This function does not have any overhead related to + * the priority inheritance mechanism because it does not try to + * enter a sleep state on the mutex. + * @param[in] mp pointer to the @p Mutex structure + * @retval TRUE if the mutex was successfully acquired + * @retval FALSE if the lock attempt failed. + * @note This function must be called within a @p chSysLock() / @p chSysUnlock() + * block. + */ +bool_t chMtxTryLockS(Mutex *mp) { + + chDbgCheck(mp != NULL, "chMtxTryLockS"); + + if (mp->m_owner != NULL) + return FALSE; + mp->m_owner = currp; + mp->m_next = currp->p_mtxlist; + currp->p_mtxlist = mp; + return TRUE; +} + +/** + * @brief Unlocks the next owned mutex in reverse lock order. + * + * @return The pointer to the unlocked mutex. + */ +Mutex *chMtxUnlock(void) { + Mutex *ump, *mp; + + chSysLock(); + chDbgAssert(currp->p_mtxlist != NULL, + "chMtxUnlock(), #1", + "owned mutexes list empty"); + chDbgAssert(currp->p_mtxlist->m_owner == currp, + "chMtxUnlock(), #2", + "ownership failure"); + /* remove the top Mutex from the Threads's owned mutexes list */ + ump = currp->p_mtxlist; + currp->p_mtxlist = ump->m_next; + /* mark the Mutex as not owned */ + ump->m_owner = NULL; + /* + * If a thread is waiting on the mutex then the hard part begins. + */ + if (chMtxQueueNotEmptyS(ump)) { + /* get the highest priority thread waiting for the unlocked mutex */ + Thread *tp = fifo_remove(&ump->m_queue); + /* + * Recalculates the optimal thread priority by scanning the owned mutexes list. + */ + tprio_t newprio = currp->p_realprio; + /* iterate mp over all the (other) mutexes the current thread still owns */ + mp = currp->p_mtxlist; + while (mp != NULL) { + /* mutex mp has a higher priority thread pending? */ + if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) + /* boost current thread's priority to waiting thread */ + newprio = mp->m_queue.p_next->p_prio; + mp = mp->m_next; + } + /* (possibly) boost the priority of the current thread */ + currp->p_prio = newprio; + /* awaken the highest priority thread waiting for the unlocked mutex */ + chSchWakeupS(tp, RDY_OK); + } + chSysUnlock(); + return ump; +} + +/** + * @brief Unlocks the next owned mutex in reverse lock order. + * + * @return The pointer to the unlocked mutex. + * @note This function must be called within a @p chSysLock() / @p chSysUnlock() + * block. + * @note This function does not reschedule internally. + */ +Mutex *chMtxUnlockS(void) { + Mutex *ump, *mp; + + chDbgAssert(currp->p_mtxlist != NULL, + "chMtxUnlockS(), #1", + "owned mutexes list empty"); + chDbgAssert(currp->p_mtxlist->m_owner == currp, + "chMtxUnlockS(), #2", + "ownership failure"); + + /* + * Removes the top Mutex from the owned mutexes list and marks it as not owned. + */ + ump = currp->p_mtxlist; + currp->p_mtxlist = ump->m_next; + ump->m_owner = NULL; + /* + * If a thread is waiting on the mutex then the hard part begins. + */ + if (chMtxQueueNotEmptyS(ump)) { + Thread *tp = fifo_remove(&ump->m_queue); + /* + * Recalculates the optimal thread priority by scanning the owned mutexes list. + */ + tprio_t newprio = currp->p_realprio; + mp = currp->p_mtxlist; + while (mp != NULL) { + if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) + newprio = mp->m_queue.p_next->p_prio; + mp = mp->m_next; + } + currp->p_prio = newprio; + chSchReadyI(tp); + } + return ump; +} + +/** + * @brief Unlocks all the mutexes owned by the invoking thread. + * @details This function is MUCH MORE efficient than releasing the + * mutexes one by one and not just because the call overhead, + * this function does not have any overhead related to the priority + * inheritance mechanism. + */ +void chMtxUnlockAll(void) { + + chSysLock(); + if (currp->p_mtxlist != NULL) { + do { + Mutex *mp = currp->p_mtxlist; + currp->p_mtxlist = mp->m_next; + mp->m_owner = NULL; + if (chMtxQueueNotEmptyS(mp)) + chSchReadyI(fifo_remove(&mp->m_queue)); + } while (currp->p_mtxlist != NULL); + currp->p_prio = currp->p_realprio; + chSchRescheduleS(); + } + chSysUnlock(); +} + +#endif /* CH_USE_MUTEXES */ + +/** @} */ diff --git a/os/kernel/src/chqueues.c b/os/kernel/src/chqueues.c new file mode 100644 index 000000000..a72b83696 --- /dev/null +++ b/os/kernel/src/chqueues.c @@ -0,0 +1,304 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chqueues.c + * @brief I/O Queues code. + * @addtogroup IOQueues + * @{ + */ + +#include + +#if CH_USE_QUEUES + +/** + * @brief Initializes an input queue. + * @details A Semaphore is internally initialized and works as a counter of + * the bytes contained in the queue. + * + * @param[out] iqp pointer to an @p InputQueue structure + * @param[in] buffer pointer to a memory area allocated as queue buffer + * @param[in] size size of the queue buffer + * @param[in] inotify pointer to a callback function that is invoked when + * some data is read from the queue. The value can be + * @p NULL. + * + * @note The callback is invoked from within the S-Locked system state, + * see @ref system_states. + */ +void chIQInit(InputQueue *iqp, uint8_t *buffer, + size_t size, qnotify_t inotify) { + + iqp->q_buffer = iqp->q_rdptr = iqp->q_wrptr = buffer; + iqp->q_top = buffer + size; + chSemInit(&iqp->q_sem, 0); + iqp->q_notify = inotify; +} + +/** + * @brief Resets an input queue. + * @details All the data in the input queue is erased and lost, any waiting + * thread is resumed with status @p Q_RESET. + * + * @param[in] iqp pointer to an @p InputQueue structure + * + * @note A reset operation can be used by a low level driver in order to obtain + * immediate attention from the high level layers. + */ +void chIQResetI(InputQueue *iqp) { + + iqp->q_rdptr = iqp->q_wrptr = iqp->q_buffer; + chSemResetI(&iqp->q_sem, 0); +} + +/** + * @brief Input queue write. + * @details A byte value is written into the low end of an input queue. + * + * @param[in] iqp pointer to an @p InputQueue structure + * @param[in] b the byte value to be written in the queue + * @return The operation status, it can be one of: + * @retval Q_OK if the operation has been completed with success. + * @retval Q_FULL if the queue is full and the operation cannot be completed. + */ +msg_t chIQPutI(InputQueue *iqp, uint8_t b) { + + if (chIQIsFull(iqp)) + return Q_FULL; + + *iqp->q_wrptr++ = b; + if (iqp->q_wrptr >= iqp->q_top) + iqp->q_wrptr = iqp->q_buffer; + chSemSignalI(&iqp->q_sem); + return Q_OK; +} + +/** + * @brief Input queue read with timeout. + * @details This function reads a byte value from an input queue. If the queue + * is empty then the calling thread is suspended until a byte arrives + * in the queue or a timeout occurs. + * + * @param[in] iqp pointer to an @p InputQueue structure + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return A byte value from the queue or: + * @retval Q_TIMEOUT if the specified time expired. + * @retval Q_RESET if the queue was reset. + */ +msg_t chIQGetTimeout(InputQueue *iqp, systime_t timeout) { + uint8_t b; + msg_t msg; + + chSysLock(); + if ((msg = chSemWaitTimeoutS(&iqp->q_sem, timeout)) < RDY_OK) { + chSysUnlock(); + return msg; + } + b = *iqp->q_rdptr++; + if (iqp->q_rdptr >= iqp->q_top) + iqp->q_rdptr = iqp->q_buffer; + + if (iqp->q_notify) + iqp->q_notify(); + + chSysUnlock(); + return b; +} + +/** + * @brief Non-blocking read. + * @details The function reads data from an input queue into a buffer. The + * transfer is non-blocking and can return zero if the queue is + * empty. + * + * @param[in] iqp pointer to an @p InputQueue structure + * @param[out] buffer pointer to the buffer where the input data is copied + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + * + * @note The function is not atomic, if you need atomicity it is suggested + * to use a semaphore or a mutex for mutual exclusion. + */ +size_t chIQRead(InputQueue *iqp, uint8_t *buffer, size_t n) { + size_t r = 0; + + while (n--) { + chSysLock(); + if (chIQIsEmpty(iqp)) { + chSysUnlock(); + break; + } + chSemFastWaitI(&iqp->q_sem); + *buffer++ = *iqp->q_rdptr++; + if (iqp->q_rdptr >= iqp->q_top) + iqp->q_rdptr = iqp->q_buffer; + chSysUnlock(); + r++; + } + if (r && iqp->q_notify) { + chSysLock(); + iqp->q_notify(); + chSysUnlock(); + } + return r; +} + +/** + * @brief Initializes an output queue. + * @details A Semaphore is internally initialized and works as a counter of + * the free bytes in the queue. + * + * @param[out] oqp pointer to an @p OutputQueue structure + * @param[in] buffer pointer to a memory area allocated as queue buffer + * @param[in] size size of the queue buffer + * @param[in] onotify pointer to a callback function that is invoked when + * some data is written to the queue. The value can be + * @p NULL. + * + * @note The callback is invoked from within the S-Locked system state, + * see @ref system_states. + */ +void chOQInit(OutputQueue *oqp, uint8_t *buffer, + size_t size, qnotify_t onotify) { + + oqp->q_buffer = oqp->q_rdptr = oqp->q_wrptr = buffer; + oqp->q_top = buffer + size; + chSemInit(&oqp->q_sem, size); + oqp->q_notify = onotify; +} + +/** + * @brief Resets an output queue. + * @details All the data in the output queue is erased and lost, any waiting + * thread is resumed with status @p Q_RESET. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * + * @note A reset operation can be used by a low level driver in order to obtain + * immediate attention from the high level layers. + */ +void chOQResetI(OutputQueue *oqp) { + + oqp->q_rdptr = oqp->q_wrptr = oqp->q_buffer; + chSemResetI(&oqp->q_sem, (cnt_t)(oqp->q_top - oqp->q_buffer)); +} + +/** + * @brief Output queue write with timeout. + * @details This function writes a byte value to an output queue. If the queue + * is full then the calling thread is suspended until there is space + * in the queue or a timeout occurs. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @param[in] b the byte value to be written in the queue + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status: + * @retval Q_OK if the operation succeeded. + * @retval Q_TIMEOUT if the specified time expired. + * @retval Q_RESET if the queue was reset. + */ +msg_t chOQPutTimeout(OutputQueue *oqp, uint8_t b, systime_t timeout) { + msg_t msg; + + chSysLock(); + if ((msg = chSemWaitTimeoutS(&oqp->q_sem, timeout)) < RDY_OK) { + chSysUnlock(); + return msg; + } + *oqp->q_wrptr++ = b; + if (oqp->q_wrptr >= oqp->q_top) + oqp->q_wrptr = oqp->q_buffer; + + if (oqp->q_notify) + oqp->q_notify(); + + chSysUnlock(); + return Q_OK; +} + +/** + * @brief Output queue read. + * @details A byte value is read from the low end of an output queue. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @return The byte value from the queue or: + * @retval Q_EMPTY if the queue is empty. + */ +msg_t chOQGetI(OutputQueue *oqp) { + uint8_t b; + + if (chOQIsEmpty(oqp)) + return Q_EMPTY; + + b = *oqp->q_rdptr++; + if (oqp->q_rdptr >= oqp->q_top) + oqp->q_rdptr = oqp->q_buffer; + chSemSignalI(&oqp->q_sem); + return b; +} + +/** + * @brief Non-blocking write. + * @details The function writes data from a buffer to an output queue. The + * transfer is non-blocking and can return zero if the queue is + * already full. + * + * @param[in] oqp pointer to an @p OutputQueue structure + * @param[out] buffer pointer to the buffer where the output data is stored + * @param[in] n the maximum amount of data to be transferred + * @return The number of bytes transferred. + * + * @note The function is not atomic, if you need atomicity it is suggested + * to use a semaphore or a mutex for mutual exclusion. + */ +size_t chOQWrite(OutputQueue *oqp, uint8_t *buffer, size_t n) { + + size_t w = 0; + while (n--) { + chSysLock(); + if (chOQIsFull(oqp)) { + chSysUnlock(); + break; + } + chSemFastWaitI(&oqp->q_sem); + *oqp->q_wrptr++ = *buffer++; + if (oqp->q_wrptr >= oqp->q_top) + oqp->q_wrptr = oqp->q_buffer; + chSysUnlock(); + w++; + } + if (w && oqp->q_notify) { + chSysLock(); + oqp->q_notify(); + chSysUnlock(); + } + return w; +} +#endif /* CH_USE_QUEUES */ + +/** @} */ diff --git a/os/kernel/src/chschd.c b/os/kernel/src/chschd.c new file mode 100644 index 000000000..3ba8b29e9 --- /dev/null +++ b/os/kernel/src/chschd.c @@ -0,0 +1,242 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chschd.c + * @brief Scheduler code. + * @addtogroup Scheduler + * @{ + */ + +#include + +/** @cond never */ +ReadyList rlist; +/** @endcond */ + +/** + * @brief Scheduler initialization. + * + * @note Internally invoked by the @p chSysInit(). + */ +void scheduler_init(void) { + + queue_init(&rlist); + rlist.r_prio = NOPRIO; +#if CH_USE_ROUNDROBIN + rlist.r_preempt = CH_TIME_QUANTUM; +#endif +} + +/** + * @brief Inserts a thread in the Ready List. + * + * @param[in] tp the Thread to be made ready + * @return The Thread pointer. + * @note The function does not reschedule, the @p chSchRescheduleS() should + * be called soon after. + */ +#if CH_OPTIMIZE_SPEED +/* NOTE: it is inlined in this module only.*/ +INLINE Thread *chSchReadyI(Thread *tp) { +#else +Thread *chSchReadyI(Thread *tp) { +#endif + Thread *cp; + + tp->p_state = PRREADY; + cp = (Thread *)&rlist; + do { + cp = cp->p_next; + } while (cp->p_prio >= tp->p_prio); + /* Insertion on p_prev.*/ + tp->p_prev = (tp->p_next = cp)->p_prev; + tp->p_prev->p_next = cp->p_prev = tp; + return tp; +} + +/** + * @brief Puts the current thread to sleep into the specified state. + * @details The thread goes into a sleeping state. The @ref thread_states are + * described into @p threads.h. + * + * @param[in] newstate the new thread state + */ +void chSchGoSleepS(tstate_t newstate) { + Thread *otp; + + (otp = currp)->p_state = newstate; + (currp = fifo_remove((void *)&rlist))->p_state = PRCURR; +#if CH_USE_ROUNDROBIN + rlist.r_preempt = CH_TIME_QUANTUM; +#endif + chDbgTrace(otp, currp); + chSysSwitchI(otp, currp); +} + +/* + * Timeout wakeup callback. + */ +static void wakeup(void *p) { + Thread *tp = (Thread *)p; + +#if CH_USE_SEMAPHORES || CH_USE_MUTEXES || CH_USE_CONDVARS + switch (tp->p_state) { +#if CH_USE_SEMAPHORES + case PRWTSEM: + chSemFastSignalI(tp->p_wtsemp); + /* Falls into, intentional. */ +#endif +#if CH_USE_MUTEXES + case PRWTMTX: +#endif +#if CH_USE_CONDVARS + case PRWTCOND: +#endif + /* States requiring dequeuing. */ + dequeue(tp); + } +#endif + chSchReadyI(tp)->p_rdymsg = RDY_TIMEOUT; +} + +/** + * @brief Puts the current thread to sleep into the specified state with + * timeout specification. + * @details The thread goes into a sleeping state, if it is not awakened + * explicitly within the specified timeout then it is forcibly + * awakened with a @p RDY_TIMEOUT low level message. The @ref + * thread_states are described into @p threads.h. + * + * @param[in] newstate the new thread state + * @param[in] time the number of ticks before the operation timeouts, the + * special values are handled as follow: + * - @a TIME_INFINITE the thread enters an infinite sleep + * state, this is equivalent to invoking @p chSchGoSleepS() + * but, of course, less efficient. + * - @a TIME_IMMEDIATE this value is accepted but interpreted + * as a normal time specification not as an immediate timeout + * specification. + * . + * @return The wakeup message. + * @retval RDY_TIMEOUT if a timeout occurs. + */ +msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t time) { + + if (TIME_INFINITE != time) { + VirtualTimer vt; + + chVTSetI(&vt, time, wakeup, currp); + chSchGoSleepS(newstate); + if (chVTIsArmedI(&vt)) + chVTResetI(&vt); + } + else + chSchGoSleepS(newstate); + return currp->p_rdymsg; +} + +/** + * @brief Wakes up a thread. + * @details The thread is inserted into the ready list or immediately made + * running depending on its relative priority compared to the current + * thread. + * + * @param[in] ntp the Thread to be made ready + * @param[in] msg message to the awakened thread + * @note It is equivalent to a @p chSchReadyI() followed by a + * @p chSchRescheduleS() but much more efficient. + */ +void chSchWakeupS(Thread *ntp, msg_t msg) { + + ntp->p_rdymsg = msg; + /* If the waken thread has a not-greater priority than the current + * one then it is just inserted in the ready list else it made + * running immediately and the invoking thread goes in the ready + * list instead.*/ + if (ntp->p_prio <= currp->p_prio) + chSchReadyI(ntp); + else { + Thread *otp = currp; + chSchReadyI(otp); + (currp = ntp)->p_state = PRCURR; +#if CH_USE_ROUNDROBIN + rlist.r_preempt = CH_TIME_QUANTUM; +#endif + chDbgTrace(otp, ntp); + chSysSwitchI(otp, ntp); + } +} + +/** + * @brief Switches to the first thread on the runnable queue. + * + * @note It is intended to be called if @p chSchRescRequiredI() evaluates to + * @p TRUE. + */ +void chSchDoRescheduleI(void) { + + Thread *otp = currp; + /* pick the first thread from the ready queue and makes it current */ + (currp = fifo_remove((void *)&rlist))->p_state = PRCURR; + chSchReadyI(otp); +#if CH_USE_ROUNDROBIN + rlist.r_preempt = CH_TIME_QUANTUM; +#endif + chDbgTrace(otp, currp); + chSysSwitchI(otp, currp); +} + +/** + * @brief Performs a reschedulation if a higher priority thread is runnable. + * @details If a thread with a higher priority than the current thread is in + * the ready list then make the higher priority thread running. + */ +void chSchRescheduleS(void) { + /* first thread in the runnable queue has higher priority than the running + * thread? */ + if (firstprio(&rlist) > currp->p_prio) + chSchDoRescheduleI(); +} + +/** + * @brief Evaluates if a reschedulation is required. + * @details The decision is taken by comparing the relative priorities and + * depending on the state of the round robin timeout counter. + * + * @retval TRUE if there is a thread that should go in running state. + * @retval FALSE if a reschedulation is not required. + */ +bool_t chSchRescRequiredI(void) { + tprio_t p1 = firstprio(&rlist); + tprio_t p2 = currp->p_prio; +#if CH_USE_ROUNDROBIN + /* If the running thread has not reached its time quantum, reschedule only + * if the first thread on the ready queue has a higher priority. + * Otherwise, if the running thread has used up its time quantum, reschedule + * if the first thread on the ready queue has equal or higher priority.*/ + return rlist.r_preempt ? p1 > p2 : p1 >= p2; +#else + /* If the round robin feature is not enabled then performs a simpler + * comparison.*/ + return p1 > p2; +#endif +} + +/** @} */ diff --git a/os/kernel/src/chsem.c b/os/kernel/src/chsem.c new file mode 100644 index 000000000..9a8570580 --- /dev/null +++ b/os/kernel/src/chsem.c @@ -0,0 +1,257 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chsem.c + * @brief Semaphores code. + * @addtogroup Semaphores + * @{ + */ + +#include + +#if CH_USE_SEMAPHORES + +#if CH_USE_SEMAPHORES_PRIORITY +#define sem_insert(tp, qp) prio_insert(tp, qp) +#else +#define sem_insert(tp, qp) queue_insert(tp, qp) +#endif + +/** + * @brief Initializes a semaphore with the specified counter value. + * + * @param[out] sp pointer to a @p Semaphore structure + * @param[in] n initial value of the semaphore counter. Must be non-negative. + * @note This function can be invoked from within an interrupt handler even if + * it is not an I-Class API because it does not touch any critical kernel + * data structure. + */ +void chSemInit(Semaphore *sp, cnt_t n) { + + chDbgCheck((sp != NULL) && (n >= 0), "chSemInit"); + + queue_init(&sp->s_queue); + sp->s_cnt = n; +} + +/** + * @brief Performs a reset operation on the semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @param[in] n the new value of the semaphore counter. The value must be non-negative. + * @note The released threads can recognize they were waked up by a reset + * instead than a signal because the @p chSemWait() will return + * @p RDY_RESET instead of @p RDY_OK. + */ +void chSemReset(Semaphore *sp, cnt_t n) { + + chSysLock(); + chSemResetI(sp, n); + chSchRescheduleS(); + chSysUnlock(); +} + +/** + * @brief Performs a reset operation on the semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @param[in] n the new value of the semaphore counter. The value must be non-negative. + * @note The released threads can recognize they were waked up by a reset + * instead than a signal because the @p chSemWait() will return + * @p RDY_RESET instead of @p RDY_OK. + * @note This function does not reschedule. + */ +void chSemResetI(Semaphore *sp, cnt_t n) { + cnt_t cnt; + + chDbgCheck((sp != NULL) && (n >= 0), "chSemResetI"); + + cnt = sp->s_cnt; + sp->s_cnt = n; + while (cnt++ < 0) + chSchReadyI(lifo_remove(&sp->s_queue))->p_rdymsg = RDY_RESET; +} + +/** + * @brief Performs a wait operation on a semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @retval RDY_OK if the semaphore was signaled or not taken. + * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). + */ +msg_t chSemWait(Semaphore *sp) { + msg_t msg; + + chSysLock(); + msg = chSemWaitS(sp); + chSysUnlock(); + return msg; +} + +/** + * @brief Performs a wait operation on a semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @retval RDY_OK if the semaphore was signaled or not taken. + * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). + * @note This function must be called with interrupts disabled. + * @note This function cannot be called by an interrupt handler. + */ +msg_t chSemWaitS(Semaphore *sp) { + + chDbgCheck(sp != NULL, "chSemWaitS"); + + if (--sp->s_cnt < 0) { + sem_insert(currp, &sp->s_queue); + currp->p_wtsemp = sp; + chSchGoSleepS(PRWTSEM); + return currp->p_rdymsg; + } + return RDY_OK; +} + +/** + * @brief Performs a wait operation on a semaphore with timeout specification. + * + * @param[in] sp pointer to a @p Semaphore structure + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @retval RDY_OK if the semaphore was signaled or not taken. + * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). + * @retval RDY_TIMEOUT if the semaphore was not signaled or reset within the + * specified timeout. + */ +msg_t chSemWaitTimeout(Semaphore *sp, systime_t time) { + msg_t msg; + + chSysLock(); + msg = chSemWaitTimeoutS(sp, time); + chSysUnlock(); + return msg; +} + +/** + * @brief Performs a wait operation on a semaphore with timeout specification. + * + * @param[in] sp pointer to a @p Semaphore structure + * @param[in] time the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @retval RDY_OK if the semaphore was signaled or not taken. + * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). + * @retval RDY_TIMEOUT if the semaphore was not signaled or reset within the specified + * timeout. + */ +msg_t chSemWaitTimeoutS(Semaphore *sp, systime_t time) { + + chDbgCheck(sp != NULL, "chSemWaitTimeoutS"); + + if (--sp->s_cnt < 0) { + if (TIME_IMMEDIATE == time) { + sp->s_cnt++; + return RDY_TIMEOUT; + } + sem_insert(currp, &sp->s_queue); + currp->p_wtsemp = sp; + return chSchGoSleepTimeoutS(PRWTSEM, time); + } + return RDY_OK; +} + +/** + * @brief Performs a signal operation on a semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @note The function is available only if the @p CH_USE_SEMAPHORES + * option is enabled in @p chconf.h. + */ +void chSemSignal(Semaphore *sp) { + + chDbgCheck(sp != NULL, "chSemSignal"); + + chSysLock(); + if (sp->s_cnt++ < 0) + chSchWakeupS(fifo_remove(&sp->s_queue), RDY_OK); + chSysUnlock(); +} + +/** + * @brief Performs a signal operation on a semaphore. + * + * @param[in] sp pointer to a @p Semaphore structure + * @note The function is available only if the @p CH_USE_SEMAPHORES + * option is enabled in @p chconf.h. + * @note This function does not reschedule. + */ +void chSemSignalI(Semaphore *sp) { + + chDbgCheck(sp != NULL, "chSemSignalI"); + + if (sp->s_cnt++ < 0) { + /* NOTE: It is done this way in order to allow a tail call on + chSchReadyI().*/ + Thread *tp = fifo_remove(&sp->s_queue); + tp->p_rdymsg = RDY_OK; + chSchReadyI(tp); + } +} + +#if CH_USE_SEMSW +/** + * @brief Performs atomic signal and wait operations on two semaphores. + * + * @param[in] sps pointer to a @p Semaphore structure to be signaled + * @param[in] spw pointer to a @p Semaphore structure to be wait on + * @retval RDY_OK if the semaphore was signaled or not taken. + * @retval RDY_RESET if the semaphore was reset using @p chSemReset(). + * @note The function is available only if the @p CH_USE_SEMSW + * option is enabled in @p chconf.h. + */ +msg_t chSemSignalWait(Semaphore *sps, Semaphore *spw) { + msg_t msg; + + chDbgCheck((sps != NULL) && (spw != NULL), "chSemSignalWait"); + + chSysLock(); + if (sps->s_cnt++ < 0) + chSchReadyI(fifo_remove(&sps->s_queue))->p_rdymsg = RDY_OK; + if (--spw->s_cnt < 0) { + sem_insert(currp, &spw->s_queue); + currp->p_wtsemp = spw; + chSchGoSleepS(PRWTSEM); + msg = currp->p_rdymsg; + } + else { + chSchRescheduleS(); + msg = RDY_OK; + } + chSysUnlock(); + return msg; +} +#endif /* CH_USE_SEMSW */ + +#endif /* CH_USE_SEMAPHORES */ + +/** @} */ diff --git a/os/kernel/src/chserial.c b/os/kernel/src/chserial.c new file mode 100644 index 000000000..65179689d --- /dev/null +++ b/os/kernel/src/chserial.c @@ -0,0 +1,168 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chserial.c + * @brief Serial Drivers code. + * @addtogroup Serial + * @{ + */ + +#include + +#if CH_USE_SERIAL_FULLDUPLEX + +/* + * Interface implementation, the following functions just invoke the equivalent + * queue-level function or macro. + */ +static bool_t putwouldblock(void *instance) { + + return chOQIsFull(&((FullDuplexDriver *)instance)->d2.oqueue); +} + +static bool_t getwouldblock(void *instance) { + + return chIQIsEmpty(&((FullDuplexDriver *)instance)->d2.iqueue); +} + +static msg_t put(void *instance, uint8_t b, systime_t timeout) { + + return chOQPutTimeout(&((FullDuplexDriver *)instance)->d2.oqueue, b, timeout); +} + +static msg_t get(void *instance, systime_t timeout) { + + return chIQGetTimeout(&((FullDuplexDriver *)instance)->d2.iqueue, timeout); +} + +static size_t write(void *instance, uint8_t *buffer, size_t n) { + + return chOQWrite(&((FullDuplexDriver *)instance)->d2.oqueue, buffer, n); +} + +static size_t read(void *instance, uint8_t *buffer, size_t n) { + + return chIQRead(&((FullDuplexDriver *)instance)->d2.iqueue, buffer, n); +} + +static const struct FullDuplexDriverVMT vmt = { + {putwouldblock, getwouldblock, put, get}, + {write, read}, + {} +}; + +/** + * @brief Initializes a generic full duplex driver. + * @details The HW dependent part of the initialization has to be performed + * outside, usually in the hardware initialization code. + * + * @param[out] sd pointer to a @p FullDuplexDriver structure + * @param[in] ib pointer to a memory area allocated for the Input Queue buffer + * @param[in] isize size of the Input Queue buffer + * @param[in] inotify pointer to a callback function that is invoked when + * some data is read from the Queue. The value can be + * @p NULL. + * @param[in] ob pointer to a memory area allocated for the Output Queue buffer + * @param[in] osize size of the Output Queue buffer + * @param[in] onotify pointer to a callback function that is invoked when + * some data is written in the Queue. The value can be + * @p NULL. + */ +void chFDDInit(FullDuplexDriver *sd, + uint8_t *ib, size_t isize, qnotify_t inotify, + uint8_t *ob, size_t osize, qnotify_t onotify) { + + chDbgCheck((sd != NULL) && (ib != NULL) && (ob != NULL) && + (isize > 0) && (osize > 0), "chFDDInit"); + + sd->vmt = &vmt; + chEvtInit(&sd->d1.ievent); + chEvtInit(&sd->d1.oevent); + chEvtInit(&sd->d2.sevent); + sd->d2.flags = SD_NO_ERROR; + chIQInit(&sd->d2.iqueue, ib, isize, inotify); + chOQInit(&sd->d2.oqueue, ob, osize, onotify); +} + +/** + * @brief Handles incoming data. + * @details This function must be called from the input interrupt service + * routine in order to enqueue incoming data and generate the + * related events. + * @param[in] sd pointer to a @p FullDuplexDriver structure + * @param[in] b the byte to be written in the driver's Input Queue + */ +void chFDDIncomingDataI(FullDuplexDriver *sd, uint8_t b) { + + if (chIQPutI(&sd->d2.iqueue, b) < Q_OK) + chFDDAddFlagsI(sd, SD_OVERRUN_ERROR); + else + chEvtBroadcastI(&sd->d1.ievent); +} + +/** + * @brief Handles outgoing data. + * @details Must be called from the output interrupt service routine in order + * to get the next byte to be transmitted. + * + * @param[in] sd pointer to a @p FullDuplexDriver structure + * @return The byte value read from the driver's output queue. + * @retval Q_EMPTY if the queue is empty (the lower driver usually disables + * the interrupt source when this happens). + */ +msg_t chFDDRequestDataI(FullDuplexDriver *sd) { + + msg_t b = chOQGetI(&sd->d2.oqueue); + if (b < Q_OK) + chEvtBroadcastI(&sd->d1.oevent); + return b; +} + +/** + * @brief Handles communication events/errors. + * @details Must be called from the I/O interrupt service routine in order to + * notify I/O conditions as errors, signals change etc. + * + * @param[in] sd pointer to a @p FullDuplexDriver structure + * @param[in] mask condition flags to be added to the mask + */ +void chFDDAddFlagsI(FullDuplexDriver *sd, dflags_t mask) { + + sd->d2.flags |= mask; + chEvtBroadcastI(&sd->d2.sevent); +} + +/** + * @brief Returns and clears the errors mask associated to the driver. + * + * @param[in] sd pointer to a @p FullDuplexDriver structure + * @return The condition flags modified since last time this function was + * invoked. + */ +dflags_t chFDDGetAndClearFlags(FullDuplexDriver *sd) { + dflags_t mask; + + mask = sd->d2.flags; + sd->d2.flags = SD_NO_ERROR; + return mask; +} +#endif /* CH_USE_SERIAL_FULLDUPLEX */ + +/** @} */ diff --git a/os/kernel/src/chsys.c b/os/kernel/src/chsys.c new file mode 100644 index 000000000..217e2f2da --- /dev/null +++ b/os/kernel/src/chsys.c @@ -0,0 +1,129 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chsys.c + * @brief System related code. + * @addtogroup System + * @{ + */ + +#include + +static WORKING_AREA(idle_thread_wa, IDLE_THREAD_STACK_SIZE); + +/** + * @brief This function implements the idle thread infinite loop. + * @details The function puts the processor in the lowest power mode capable + * to serve interrupts.
+ * The priority is internally set to the minimum system value so + * that this thread is executed only if there are no other ready + * threads in the system. + * + * @param[in] p the thread parameter, unused in this scenario + */ +static void idle_thread(void *p) { + + while (TRUE) { + port_wait_for_interrupt(); + IDLE_LOOP_HOOK(); + } +} + +/** + * @brief ChibiOS/RT initialization. + * @details After executing this function the current instructions stream + * becomes the main thread. + * + * @note Interrupts should be still disabled when @p chSysInit() is invoked + * and are internally enabled. + * @note The main thread is created with priority @p NORMALPRIO. + */ +void chSysInit(void) { + static Thread mainthread; + + port_init(); + scheduler_init(); + vt_init(); +#if CH_USE_HEAP + heap_init(); +#endif +#if CH_DBG_ENABLE_TRACE + trace_init(); +#endif + + /* + * Now this instructions flow becomes the main thread. + */ + (currp = init_thread(&mainthread, NORMALPRIO))->p_state = PRCURR; + chSysEnable(); + + /* + * This thread has the lowest priority in the system, its role is just to + * serve interrupts in its context while keeping the lowest energy saving + * mode compatible with the system status. + */ + chThdCreateStatic(idle_thread_wa, sizeof(idle_thread_wa), IDLEPRIO, + (tfunc_t)idle_thread, NULL); +} + +/** + * @brief Handles time ticks for round robin preemption and timer increments. + * @details Decrements the remaining time quantum of the running thread + * and preempts it when the quantum is used up. Increments system + * time and manages the timers. + * + * @note The frequency of the timer determines the system tick granularity and, + * together with the @p CH_TIME_QUANTUM macro, the round robin interval. + */ +void chSysTimerHandlerI(void) { + +#if CH_USE_ROUNDROBIN + /* running thread has not used up quantum yet? */ + if (rlist.r_preempt > 0) + /* decrement remaining quantum */ + rlist.r_preempt--; +#endif +#if CH_DBG_THREADS_PROFILING + currp->p_time++; +#endif + chVTDoTickI(); +} + +#if CH_USE_NESTED_LOCKS && !CH_OPTIMIZE_SPEED +void chSysLock(void) { + + chDbgAssert(currp->p_locks >= 0, + "chSysLock(), #1", + "negative nesting counter"); + if (currp->p_locks++ == 0) + port_lock(); +} + +void chSysUnlock(void) { + + chDbgAssert(currp->p_locks > 0, + "chSysUnlock(), #1", + "non-positive nesting counter"); + if (--currp->p_locks == 0) + port_unlock(); +} +#endif /* CH_USE_NESTED_LOCKS && !CH_OPTIMIZE_SPEED */ + +/** @} */ diff --git a/os/kernel/src/chthreads.c b/os/kernel/src/chthreads.c new file mode 100644 index 000000000..f8bb3b869 --- /dev/null +++ b/os/kernel/src/chthreads.c @@ -0,0 +1,381 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chthreads.c + * @brief Threads code. + * @addtogroup Threads + * @{ + */ + +#include + +/* + * Initializes a thread structure. + */ +Thread *init_thread(Thread *tp, tprio_t prio) { + + tp->p_flags = P_MEM_MODE_STATIC; + tp->p_prio = prio; + tp->p_state = PRSUSPENDED; +#if CH_USE_NESTED_LOCKS + tp->p_locks = 0; +#endif +#if CH_DBG_THREADS_PROFILING + tp->p_time = 0; +#endif +#if CH_USE_MUTEXES + /* realprio is the thread's own, non-inherited, priority */ + tp->p_realprio = prio; + tp->p_mtxlist = NULL; +#endif +#if CH_USE_WAITEXIT + tp->p_waiting = NULL; +#endif +#if CH_USE_MESSAGES + queue_init(&tp->p_msgqueue); +#endif +#if CH_USE_EVENTS + tp->p_epending = 0; +#endif + THREAD_EXT_INIT(tp); + return tp; +} + +#if CH_DBG_FILL_THREADS +static void memfill(uint8_t *startp, uint8_t *endp, uint8_t v) { + + while (startp < endp) + *startp++ = v; +} +#endif + +/** + * @brief Initializes a new thread. + * @details The new thread is initialized but not inserted in the ready list, + * the initial state is @p PRSUSPENDED. + * + * @param[out] wsp pointer to a working area dedicated to the thread stack + * @param[in] size size of the working area + * @param[in] prio the priority level for the new thread + * @param[in] pf the thread function + * @param[in] arg an argument passed to the thread function. It can be @p NULL. + * @return The pointer to the @p Thread structure allocated for the + * thread into the working space area. + * @note A thread can terminate by calling @p chThdExit() or by simply + * returning from its main function. + * @note This function can be invoked from within an interrupt handler even if + * it is not an I-Class API because it does not touch any critical kernel + * data structure. + */ +Thread *chThdInit(void *wsp, size_t size, tprio_t prio, tfunc_t pf, void *arg) { + /* Thread structure is layed out in the lower part of the thread workspace */ + Thread *tp = wsp; + + chDbgCheck((wsp != NULL) && (size >= THD_WA_SIZE(0)) && + (prio <= HIGHPRIO) && (pf != NULL), + "chThdInit"); +#if CH_DBG_FILL_THREADS + memfill((uint8_t *)wsp, (uint8_t *)wsp + sizeof(Thread), THREAD_FILL_VALUE); + memfill((uint8_t *)wsp + sizeof(Thread), + (uint8_t *)wsp + size, STACK_FILL_VALUE); +#endif + SETUP_CONTEXT(wsp, size, pf, arg); + return init_thread(tp, prio); +} + +/** + * @brief Creates a new thread into a static memory area. + * + * @param[out] wsp pointer to a working area dedicated to the thread + * stack + * @param[in] size size of the working area + * @param[in] prio the priority level for the new thread + * @param[in] pf the thread function + * @param[in] arg an argument passed to the thread function. It can be @p NULL. + * @return The pointer to the @p Thread structure allocated for the + * thread into the working space area. + * @note A thread can terminate by calling @p chThdExit() or by simply + * returning from its main function. + */ +Thread *chThdCreateStatic(void *wsp, size_t size, + tprio_t prio, tfunc_t pf, void *arg) { + + return chThdResume(chThdInit(wsp, size, prio, pf, arg)); +} + +#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP +/** + * @brief Creates a new thread allocating the memory from the heap. + * + * @param[in] size size of the working area to be allocated + * @param[in] prio the priority level for the new thread + * @param[in] pf the thread function + * @param[in] arg an argument passed to the thread function. It can be @p NULL. + * @return The pointer to the @p Thread structure allocated for the + * thread into the working space area. + * @retval NULL if the memory cannot be allocated. + * @note A thread can terminate by calling @p chThdExit() or by simply + * returning from its main function. + * @note The memory allocated for the thread is not released when the thread + * terminates but when a @p chThdWait() is performed. + * @note The function is available only if the @p CH_USE_DYNAMIC, + * @p CH_USE_HEAP and @p CH_USE_WAITEXIT options are enabled + * in @p chconf.h. + */ +Thread *chThdCreateFromHeap(size_t size, tprio_t prio, tfunc_t pf, void *arg) { + void *wsp; + Thread *tp; + + wsp = chHeapAlloc(size); + if (wsp == NULL) + return NULL; + tp = chThdInit(wsp, size, prio, pf, arg); + tp->p_flags = P_MEM_MODE_HEAP; + return chThdResume(tp); +} +#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP */ + +#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS +/** + * @brief Creates a new thread allocating the memory from the specified Memory + * Pool. + * + * @param[in] mp the memory pool + * @param[in] prio the priority level for the new thread + * @param[in] pf the thread function + * @param[in] arg an argument passed to the thread function. It can be @p NULL. + * @return The pointer to the @p Thread structure allocated for the + * thread into the working space area or @p NULL if the memory cannot + * be allocated. + * @retval NULL if the memory pool is empty. + * @note A thread can terminate by calling @p chThdExit() or by simply + * returning from its main function. + * @note The memory allocated for the thread is not released when the thread + * terminates but when a @p chThdWait() is performed. + * @note The function is available only if the @p CH_USE_DYNAMIC, + * @p CH_USE_MEMPOOLS and @p CH_USE_WAITEXIT options are enabled + * in @p chconf.h. + */ +Thread *chThdCreateFromMemoryPool(MemoryPool *mp, tprio_t prio, + tfunc_t pf, void *arg) { + void *wsp; + Thread *tp; + + chDbgCheck(mp != NULL, "chThdCreateFromMemoryPool"); + + wsp = chPoolAlloc(mp); + if (wsp == NULL) + return NULL; + tp = chThdInit(wsp, mp->mp_object_size, prio, pf, arg); + tp->p_flags = P_MEM_MODE_MEMPOOL; + tp->p_mpool = mp; + return chThdResume(tp); +} +#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS */ + +/** + * @brief Changes the running thread priority level then reschedules if + * necessary. + * + * @param[in] newprio the new priority level of the running thread + * @return The old priority level. + * @note The function returns the real thread priority regardless of the + * current priority that could be higher than the real priority because + * the priority inheritance mechanism. + */ +tprio_t chThdSetPriority(tprio_t newprio) { + tprio_t oldprio; + + chDbgCheck((newprio >= LOWPRIO) && (newprio <= HIGHPRIO), + "chThdSetPriority"); + + chSysLock(); +#if CH_USE_MUTEXES + oldprio = currp->p_realprio; + if ((currp->p_prio == currp->p_realprio) || (newprio > currp->p_prio)) + currp->p_prio = newprio; + currp->p_realprio = newprio; +#else + oldprio = currp->p_prio; + currp->p_prio = newprio; +#endif + chSchRescheduleS(); + chSysUnlock(); + return oldprio; +} + +/** + * @brief Resumes a suspended thread. + * + * @param[in] tp the pointer to the thread + * @return The pointer to the thread. + * @note This call is supposed to resume threads created with @p chThdInit(). + * It should not be used on threads suspended using @p chThdSuspend(). + */ +Thread *chThdResume(Thread *tp) { + + chSysLock(); + chDbgAssert(tp->p_state == PRSUSPENDED, + "chThdResume(), #1", + "thread not in PRSUSPENDED state"); + chSchWakeupS(tp, RDY_OK); + chSysUnlock(); + return tp; +} + +/** + * @brief Requests a thread termination. + * + * @param[in] tp the pointer to the thread + * @note The thread is not termitated but a termination request is added to + * its @p p_flags field. The thread can read this status by + * invoking @p chThdShouldTerminate() and then terminate cleanly. + */ +void chThdTerminate(Thread *tp) { + + chSysLock(); + tp->p_flags |= P_TERMINATE; + chSysUnlock(); +} + +/** + * @brief Suspends the invoking thread for the specified time. + * + * @param[in] time the delay in system ticks, the special values are handled as + * follow: + * - @a TIME_INFINITE the thread enters an infinite sleep + * state. + * - @a TIME_IMMEDIATE this value is accepted but interpreted + * as a normal time specification not as an immediate timeout + * specification. + * . + */ +void chThdSleep(systime_t time) { + + chDbgCheck(time != TIME_INFINITE, "chThdSleep"); + + chSysLock(); + chThdSleepS(time); + chSysUnlock(); +} + +/** + * @brief Suspends the invoking thread until the system time arrives to the + * specified value. + * + * @param[in] time the absolute system time + */ +void chThdSleepUntil(systime_t time) { + + chSysLock(); + if ((time -= chTimeNow()) > 0) + chThdSleepS(time); + chSysUnlock(); +} + +/** + * @brief Terminates the current thread by specifying an exit status code. + * + * @param[in] msg the thread exit code. The code can be retrieved by using + * @p chThdWait(). + */ +void chThdExit(msg_t msg) { + Thread *tp = currp; + + chSysLock(); + tp->p_exitcode = msg; + THREAD_EXT_EXIT(tp); +#if CH_USE_WAITEXIT + if (tp->p_waiting != NULL) + chSchReadyI(tp->p_waiting); +#endif + chSchGoSleepS(PREXIT); +} + +#if CH_USE_WAITEXIT +/** + * @brief Blocks the execution of the invoking thread until the specified + * thread terminates then the exit code is returned. + * @details The memory used by the exited thread is handled in different ways + * depending on the API that spawned the thread: + * - If the thread was spawned by @p chThdCreateStatic() or by + * @p chThdInit() then nothing happens and the thread working area + * is not released or modified in any way. This is the default, + * totally static, behavior. + * - If the thread was spawned by @p chThdCreateFromHeap() then + * the working area is returned to the system heap. + * - If the thread was spawned by @p chThdCreateFromMemoryPool() + * then the working area is returned to the owning memory pool. + * . + * @param[in] tp the thread pointer + * @return The exit code from the terminated thread + * @note After invoking @p chThdWait() the thread pointer becomes invalid and + * must not be used as parameter for further system calls. + * @note The function is available only if the @p CH_USE_WAITEXIT + * option is enabled in @p chconf.h. + * @note Only one thread can be waiting for another thread at any time. You + * should imagine the threads as having a reference counter that is set + * to one when the thread is created, chThdWait() decreases the reference + * and the memory is freed when the counter reaches zero. In the current + * implementation there is no real reference counter in the thread + * structure but it is a planned extension. + */ +msg_t chThdWait(Thread *tp) { + msg_t msg; + + chDbgCheck(tp != NULL, "chThdWait"); + + chSysLock(); + + chDbgAssert(tp != currp, "chThdWait(), #1", "waiting self"); + chDbgAssert(tp->p_waiting == NULL, "chThdWait(), #2", "some other thread waiting"); + + if (tp->p_state != PREXIT) { + tp->p_waiting = currp; + chSchGoSleepS(PRWAIT); + } + msg = tp->p_exitcode; +#if !CH_USE_DYNAMIC + chSysUnlock(); + return msg; +#else /* CH_USE_DYNAMIC */ + + /* Returning memory.*/ + tmode_t mode = tp->p_flags & P_MEM_MODE_MASK; + chSysUnlock(); + + switch (mode) { +#if CH_USE_HEAP + case P_MEM_MODE_HEAP: + chHeapFree(tp); + break; +#endif +#if CH_USE_MEMPOOLS + case P_MEM_MODE_MEMPOOL: + chPoolFree(tp->p_mpool, tp); + break; +#endif + } + return msg; +#endif /* CH_USE_DYNAMIC */ +} +#endif /* CH_USE_WAITEXIT */ + +/** @} */ diff --git a/os/kernel/src/chvt.c b/os/kernel/src/chvt.c new file mode 100644 index 000000000..c1d8734ec --- /dev/null +++ b/os/kernel/src/chvt.c @@ -0,0 +1,116 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file chvt.c + * @brief Time and Virtual Timers related code. + * @addtogroup Time + * @{ + */ + +#include + +VTList vtlist; + +/** + * @brief Virtual Timers initialization. + * + * @note Internal use only. + */ +void vt_init(void) { + + vtlist.vt_next = vtlist.vt_prev = (void *)&vtlist; + vtlist.vt_time = (systime_t)-1; + vtlist.vt_systime = 0; +} + +/** + * @brief Enables a virtual timer. + * + * @param[out] vtp the @p VirtualTimer structure pointer + * @param[in] time the number of time ticks, the value @p TIME_INFINITE is not + * allowed. The value @p TIME_IMMEDIATE is allowed but + * interpreted as a normal time specification not as an + * immediate timeout specification. + * @param[in] vtfunc the timer callback function. After invoking the callback + * the timer is disabled and the structure can be disposed or + * reused. + * @param[in] par a parameter that will be passed to the callback function + * @note The associated function is invoked by an interrupt handler within + * the I-Locked state, see @ref system_states. + */ +void chVTSetI(VirtualTimer *vtp, systime_t time, vtfunc_t vtfunc, void *par) { + VirtualTimer *p; + + chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (time != TIME_INFINITE), + "chVTSetI"); + + vtp->vt_par = par; + vtp->vt_func = vtfunc; + p = vtlist.vt_next; + while (p->vt_time < time) { + time -= p->vt_time; + p = p->vt_next; + } + + vtp->vt_prev = (vtp->vt_next = p)->vt_prev; + vtp->vt_prev->vt_next = p->vt_prev = vtp; + vtp->vt_time = time; + if (p != (void *)&vtlist) + p->vt_time -= time; +} + +/** + * @brief Disables a Virtual Timer. + * + * @param[in] vtp the @p VirtualTimer structure pointer + * @note The timer MUST be active when this function is invoked. + */ +void chVTResetI(VirtualTimer *vtp) { + + chDbgCheck(vtp != NULL, "chVTResetI"); + chDbgAssert(vtp->vt_func != NULL, + "chVTResetI(), #1", + "timer not set or already triggered"); + + if (vtp->vt_next != (void *)&vtlist) + vtp->vt_next->vt_time += vtp->vt_time; + vtp->vt_prev->vt_next = vtp->vt_next; + vtp->vt_next->vt_prev = vtp->vt_prev; + vtp->vt_func = NULL; +} + +/** + * @brief Checks if the current system time is within the specified time window. + * + * @param[in] start the start of the time window (inclusive) + * @param[in] end the end of the time window (non inclusive) + * @retval TRUE current time within the specified time window. + * @retval FALSE current time not within the specified time window. + * @note When start==end then the function returns always true because the + * whole time range is specified. + */ +bool_t chTimeIsWithin(systime_t start, systime_t end) { + + systime_t time = chTimeNow(); + return end > start ? (time >= start) && (time < end) : + (time >= start) || (time < end); +} + +/** @} */ -- cgit v1.2.3