2013-09-01 08:19:24 +00:00
|
|
|
/*
|
|
|
|
Nil RTOS - Copyright (C) 2012 Giovanni Di Sirio.
|
|
|
|
|
|
|
|
This file is part of Nil RTOS.
|
|
|
|
|
|
|
|
Nil RTOS 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.
|
|
|
|
|
|
|
|
Nil RTOS 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file nil.c
|
|
|
|
* @brief Nil RTOS main source file.
|
|
|
|
*
|
|
|
|
* @defgroup nil
|
|
|
|
* @details Nil RTOS services.
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nil.h"
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module local definitions. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module exported variables. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief System data structures.
|
|
|
|
*/
|
|
|
|
nil_system_t nil;
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module local variables. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module local functions. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module interrupt handlers. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Module exported functions. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Initializes the kernel.
|
|
|
|
* @details Initializes the kernel structures, the current instructions flow
|
|
|
|
* becomes the idle thread upon return. The idle thread must not
|
|
|
|
* invoke any kernel primitive able to change state to not runnable.
|
|
|
|
*
|
|
|
|
* @special
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSysInit(void) {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr;
|
2013-09-01 08:19:24 +00:00
|
|
|
const thread_config_t *tcp;
|
|
|
|
|
|
|
|
/* Port layer initialization.*/
|
|
|
|
port_init();
|
|
|
|
|
|
|
|
/* Iterates through the list of defined threads.*/
|
|
|
|
for (tr = &nil.threads[0], tcp = nil_thd_configs;
|
|
|
|
tr < &nil.threads[NIL_CFG_NUM_THREADS];
|
|
|
|
tr++, tcp++) {
|
2013-09-03 13:50:15 +00:00
|
|
|
tr->state = NIL_STATE_READY;
|
2013-09-01 08:19:24 +00:00
|
|
|
tr->timeout = 0;
|
|
|
|
|
|
|
|
/* Port dependent thread initialization.*/
|
2013-09-03 13:21:10 +00:00
|
|
|
PORT_SETUP_CONTEXT(tr, tcp->wap, tcp->size, tcp->funcp, tcp->arg);
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
/* Initialization hook.*/
|
|
|
|
#if defined(NIL_CFG_THREAD_EXT_INIT_HOOK)
|
|
|
|
NIL_CFG_THREAD_EXT_INIT_HOOK(tr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Runs the highest priority thread, the current one becomes the null
|
|
|
|
thread.*/
|
|
|
|
nil.current = nil.next = nil.threads;
|
|
|
|
port_switch(nil.threads, &nil.threads[NIL_CFG_NUM_THREADS]);
|
|
|
|
|
|
|
|
/* Interrupts enabled for the idle thread.*/
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysEnable();
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:21:10 +00:00
|
|
|
/**
|
|
|
|
* @brief Halts the system.
|
|
|
|
* @details This function is invoked by the operating system when an
|
|
|
|
* unrecoverable error is detected, for example because a programming
|
|
|
|
* error in the application code that triggers an assertion while
|
|
|
|
* in debug mode.
|
|
|
|
* @note Can be invoked from any system state.
|
|
|
|
*
|
|
|
|
* @special
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSysHalt(const char *reason) {
|
2013-09-03 13:21:10 +00:00
|
|
|
|
|
|
|
port_disable();
|
|
|
|
|
|
|
|
#if NIL_DBG_ENABLED
|
|
|
|
nil.dbg_panic_msg = reason;
|
|
|
|
#else
|
|
|
|
(void)reason;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(NIL_CFG_SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
|
|
|
|
NIL_CFG_SYSTEM_HALT_HOOK(reason);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Harmless infinite loop.*/
|
|
|
|
while (true)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2013-09-01 08:19:24 +00:00
|
|
|
/**
|
|
|
|
* @brief Time management handler.
|
|
|
|
* @note This handler has to be invoked by a periodic ISR in order to
|
|
|
|
* reschedule the waiting threads.
|
|
|
|
*
|
|
|
|
* @iclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSysTimerHandlerI(void) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
#if NIL_CFG_TIMEDELTA == 0
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr = &nil.threads[0];
|
2013-09-01 08:19:24 +00:00
|
|
|
nil.systime++;
|
|
|
|
do {
|
|
|
|
/* Is the thread in a wait state with timeout?.*/
|
|
|
|
if (tr->timeout > 0) {
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(!NIL_THD_IS_READY(tr), "is ready");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
/* Did the timer reach zero?*/
|
|
|
|
if (--tr->timeout == 0) {
|
|
|
|
/* Timeout on semaphores requires a special handling because the
|
|
|
|
semaphore counter must be incremented.*/
|
|
|
|
if (NIL_THD_IS_WTSEM(tr))
|
|
|
|
tr->u1.semp->cnt++;
|
|
|
|
else if (NIL_THD_IS_SUSP(tr))
|
|
|
|
tr->u1.trp = NULL;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSchReadyI(tr, MSG_TIMEOUT);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Lock released in order to give a preemption chance on those
|
|
|
|
architectures supporting IRQ preemption.*/
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysUnlockFromISR();
|
2013-09-01 08:19:24 +00:00
|
|
|
tr++;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLockFromISR();
|
2013-09-01 08:19:24 +00:00
|
|
|
} while (tr < &nil.threads[NIL_CFG_NUM_THREADS]);
|
|
|
|
#else
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr = &nil.threads[0];
|
2013-09-01 08:19:24 +00:00
|
|
|
systime_t next = 0;
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(nil.nexttime == port_timer_get_alarm(),
|
|
|
|
"chSysTimerHandlerI(), #1", "time mismatch");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
/* Is the thread in a wait state with timeout?.*/
|
|
|
|
if (tr->timeout > 0) {
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(!NIL_THD_IS_READY(tr),
|
|
|
|
"chSysTimerHandlerI(), #2", "is ready");
|
|
|
|
chDbgAssert(tr->timeout >= nil.nexttime - nil.lasttime,
|
|
|
|
"chSysTimerHandlerI(), #3", "skipped one");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
tr->timeout -= nil.nexttime - nil.lasttime;
|
|
|
|
if (tr->timeout == 0) {
|
|
|
|
/* Timeout on semaphores requires a special handling because the
|
|
|
|
semaphore counter must be incremented.*/
|
|
|
|
if (NIL_THD_IS_WTSEM(tr))
|
|
|
|
tr->u1.semp->cnt++;
|
|
|
|
else if (NIL_THD_IS_SUSP(tr))
|
|
|
|
tr->u1.trp = NULL;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSchReadyI(tr, NIL_MSG_TMO);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (tr->timeout <= next - 1)
|
|
|
|
next = tr->timeout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Lock released in order to give a preemption chance on those
|
|
|
|
architectures supporting IRQ preemption.*/
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysUnlockFromISR();
|
2013-09-01 08:19:24 +00:00
|
|
|
tr++;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLockFromISR();
|
2013-09-01 08:19:24 +00:00
|
|
|
} while (tr < &nil.threads[NIL_CFG_NUM_THREADS]);
|
|
|
|
nil.lasttime = nil.nexttime;
|
|
|
|
if (next > 0) {
|
|
|
|
nil.nexttime += next;
|
|
|
|
port_timer_set_alarm(nil.nexttime);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* No tick event needed.*/
|
|
|
|
port_timer_stop_alarm();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-09-04 10:26:42 +00:00
|
|
|
/**
|
|
|
|
* @brief Returns the execution status and enters a critical zone.
|
|
|
|
* @details This functions enters into a critical zone and can be called
|
|
|
|
* from any context. Because its flexibility it is less efficient
|
|
|
|
* than @p chSysLock() which is preferable when the calling context
|
|
|
|
* is known.
|
|
|
|
* @post The system is in a critical zone.
|
|
|
|
*
|
|
|
|
* @return The previous system status, the encoding of this
|
|
|
|
* status word is architecture-dependent and opaque.
|
|
|
|
*
|
|
|
|
* @xclass
|
|
|
|
*/
|
|
|
|
syssts_t chSysGetStatusAndLockX(void) {
|
|
|
|
|
|
|
|
syssts_t sts = port_get_irq_status();
|
|
|
|
if (port_irq_enabled(sts)) {
|
|
|
|
if (port_is_isr_context())
|
|
|
|
chSysLockFromISR();
|
|
|
|
else
|
|
|
|
chSysLock();
|
|
|
|
}
|
|
|
|
return sts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Restores the specified execution status and leaves a critical zone.
|
|
|
|
* @note A call to @p chSchRescheduleS() is automatically performed
|
|
|
|
* if exiting the critical zone and if not in ISR context.
|
|
|
|
*
|
|
|
|
* @param[in] sts the system status to be restored.
|
|
|
|
*
|
|
|
|
* @xclass
|
|
|
|
*/
|
|
|
|
void chSysRestoreStatusX(syssts_t sts) {
|
|
|
|
|
|
|
|
if (port_irq_enabled(sts)) {
|
|
|
|
if (port_is_isr_context())
|
|
|
|
chSysUnlockFromISR();
|
|
|
|
else {
|
|
|
|
chSchRescheduleS();
|
|
|
|
chSysUnlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-01 08:19:24 +00:00
|
|
|
/**
|
|
|
|
* @brief Makes the specified thread ready for execution.
|
|
|
|
*
|
|
|
|
* @param[in] tr reference to the @p thread_t object
|
|
|
|
* @param[in] msg the wakeup message
|
|
|
|
*
|
|
|
|
* @return The same reference passed as parameter.
|
|
|
|
*/
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t chSchReadyI(thread_reference_t tr, msg_t msg) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert((tr >= nil.threads) &&
|
2013-09-01 08:19:24 +00:00
|
|
|
(tr < &nil.threads[NIL_CFG_NUM_THREADS]),
|
2013-09-03 13:21:10 +00:00
|
|
|
"pointer out of range");
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(!NIL_THD_IS_READY(tr), "already ready");
|
|
|
|
chDbgAssert(nil.next <= nil.current, "priority ordering");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
tr->u1.msg = msg;
|
2013-09-03 13:50:15 +00:00
|
|
|
tr->state = NIL_STATE_READY;
|
2013-09-01 08:19:24 +00:00
|
|
|
tr->timeout = 0;
|
|
|
|
if (tr < nil.next)
|
|
|
|
nil.next = tr;
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Reschedules.
|
|
|
|
*
|
|
|
|
* @sclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSchRescheduleS() {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t otr = nil.current;
|
|
|
|
thread_reference_t ntr = nil.next;
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
if (ntr != otr) {
|
|
|
|
nil.current = ntr;
|
|
|
|
#if defined(NIL_CFG_IDLE_LEAVE_HOOK)
|
|
|
|
if (otr == &nil.threads[NIL_CFG_NUM_THREADS]) {
|
|
|
|
NIL_CFG_IDLE_LEAVE_HOOK();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
port_switch(ntr, otr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 system time then it is forcibly
|
|
|
|
* awakened with a @p NIL_MSG_TMO low level message.
|
|
|
|
*
|
|
|
|
* @param[in] newstate the new thread state or a semaphore pointer
|
|
|
|
* @param[in] timeout the number of ticks before the operation timeouts.
|
|
|
|
* the following special values are allowed:
|
|
|
|
* - @a TIME_INFINITE no timeout.
|
|
|
|
* .
|
|
|
|
* @return The wakeup message.
|
|
|
|
* @retval NIL_MSG_TMO if a timeout occurred.
|
|
|
|
*
|
|
|
|
* @sclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t timeout) {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t ntr, otr = nil.current;
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(otr != &nil.threads[NIL_CFG_NUM_THREADS],
|
2013-09-03 13:21:10 +00:00
|
|
|
"idle cannot sleep");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
/* Storing the wait object for the current thread.*/
|
|
|
|
otr->state = newstate;
|
|
|
|
|
|
|
|
#if NIL_CFG_TIMEDELTA > 0
|
|
|
|
if (timeout != TIME_INFINITE) {
|
2013-09-03 13:50:15 +00:00
|
|
|
systime_t time = chTimeNowI() + timeout;
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
/* TIMEDELTA makes sure to have enough time to reprogram the timer
|
|
|
|
before the free-running timer counter reaches the selected timeout.*/
|
|
|
|
if (timeout < NIL_CFG_TIMEDELTA)
|
|
|
|
timeout = NIL_CFG_TIMEDELTA;
|
|
|
|
|
|
|
|
if (nil.lasttime == nil.nexttime) {
|
|
|
|
/* Special case, first thread asking for a timeout.*/
|
|
|
|
port_timer_start_alarm(time);
|
|
|
|
nil.nexttime = time;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Special case, there are already other threads with a timeout
|
|
|
|
activated, evaluating the order.*/
|
2013-09-03 13:50:15 +00:00
|
|
|
if (chTimeIsWithin(time, nil.lasttime, nil.nexttime)) {
|
2013-09-01 08:19:24 +00:00
|
|
|
port_timer_set_alarm(time);
|
|
|
|
nil.nexttime = time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Timeout settings.*/
|
|
|
|
otr->timeout = time - nil.lasttime;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* Timeout settings.*/
|
|
|
|
otr->timeout = timeout;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Scanning the whole threads array.*/
|
|
|
|
ntr = nil.threads;
|
|
|
|
while (true) {
|
|
|
|
/* Is this thread ready to execute?*/
|
|
|
|
if (NIL_THD_IS_READY(ntr)) {
|
|
|
|
nil.current = nil.next = ntr;
|
|
|
|
#if defined(NIL_CFG_IDLE_ENTER_HOOK)
|
|
|
|
if (ntr == &nil.threads[NIL_CFG_NUM_THREADS]) {
|
|
|
|
NIL_CFG_IDLE_ENTER_HOOK();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
port_switch(ntr, otr);
|
|
|
|
return nil.current->u1.msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Points to the next thread in lowering priority order.*/
|
|
|
|
ntr++;
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(ntr <= &nil.threads[NIL_CFG_NUM_THREADS],
|
2013-09-03 13:21:10 +00:00
|
|
|
"pointer out of range");
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sends the current thread sleeping and sets a reference variable.
|
|
|
|
* @note This function must reschedule, it can only be called from thread
|
|
|
|
* context.
|
|
|
|
*
|
|
|
|
* @param[in] trp a pointer to a thread reference object
|
|
|
|
* @param[in] timeout the number of ticks before the operation timeouts,
|
|
|
|
* the following special values are allowed:
|
|
|
|
* - @a TIME_INFINITE no timeout.
|
|
|
|
* .
|
|
|
|
* @return The wake up message.
|
|
|
|
*
|
|
|
|
* @sclass
|
|
|
|
*/
|
2013-09-04 08:19:38 +00:00
|
|
|
msg_t chThdSuspendTimeoutS(thread_reference_t *trp, systime_t timeout) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(*trp == NULL, "not NULL");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
*trp = nil.current;
|
|
|
|
nil.current->u1.trp = trp;
|
2013-09-03 13:50:15 +00:00
|
|
|
return chSchGoSleepTimeoutS(NIL_STATE_SUSP, timeout);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Wakes up a thread waiting on a thread reference object.
|
|
|
|
* @note This function must not reschedule because it can be called from
|
|
|
|
* ISR context.
|
|
|
|
*
|
|
|
|
* @param[in] trp a pointer to a thread reference object
|
|
|
|
* @param[in] msg the message code
|
|
|
|
*
|
|
|
|
* @iclass
|
|
|
|
*/
|
2013-09-04 08:19:38 +00:00
|
|
|
void chThdResumeI(thread_reference_t *trp, msg_t msg) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
if (*trp != NULL) {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr = *trp;
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(NIL_THD_IS_SUSP(tr), "not suspended");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
*trp = NULL;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSchReadyI(tr, msg);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Suspends the invoking thread for the specified time.
|
|
|
|
*
|
|
|
|
* @param[in] time the delay in system ticks
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chThdSleep(systime_t time) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLock();
|
|
|
|
chThdSleepS(time);
|
|
|
|
chSysUnlock();
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Suspends the invoking thread until the system time arrives to the
|
|
|
|
* specified value.
|
|
|
|
*
|
|
|
|
* @param[in] time absolute system time
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chThdSleepUntil(systime_t time) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLock();
|
|
|
|
chThdSleepUntilS(time);
|
|
|
|
chSysUnlock();
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-09-04 10:26:42 +00:00
|
|
|
* @brief Checks if the specified time is within the specified time window.
|
2013-09-01 08:19:24 +00:00
|
|
|
* @note When start==end then the function returns always true because the
|
|
|
|
* whole time range is specified.
|
2013-09-04 10:26:42 +00:00
|
|
|
* @note This function can be called from any context.
|
2013-09-01 08:19:24 +00:00
|
|
|
*
|
2013-09-04 10:26:42 +00:00
|
|
|
* @param[in] time the time to be verified
|
2013-09-01 08:19:24 +00:00
|
|
|
* @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.
|
|
|
|
*
|
2013-09-04 10:26:42 +00:00
|
|
|
* @xclass
|
2013-09-01 08:19:24 +00:00
|
|
|
*/
|
2013-09-04 10:26:42 +00:00
|
|
|
bool chVTIsTimeWithinX(systime_t time, systime_t start, systime_t end) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-04 10:26:42 +00:00
|
|
|
return end > start ? (time >= start) && (time < end) :
|
|
|
|
(time >= start) || (time < end);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a wait operation on a semaphore with timeout specification.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t 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 message specifying how the invoking thread has been
|
|
|
|
* released from the semaphore.
|
|
|
|
* @retval NIL_MSG_OK if the thread has not stopped on the semaphore or the
|
|
|
|
* semaphore has been signaled.
|
2013-09-03 13:50:15 +00:00
|
|
|
* @retval NIL_MSG_RST if the semaphore has been reset using @p chSemReset().
|
2013-09-01 08:19:24 +00:00
|
|
|
* @retval NIL_MSG_TMO if the semaphore has not been signaled or reset within
|
|
|
|
* the specified timeout.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
msg_t chSemWaitTimeout(semaphore_t *sp, systime_t timeout) {
|
2013-09-01 08:19:24 +00:00
|
|
|
msg_t msg;
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLock();
|
|
|
|
msg = chSemWaitTimeoutS(sp, timeout);
|
|
|
|
chSysUnlock();
|
2013-09-01 08:19:24 +00:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a wait operation on a semaphore with timeout specification.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t 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 message specifying how the invoking thread has been
|
|
|
|
* released from the semaphore.
|
|
|
|
* @retval NIL_MSG_OK if the thread has not stopped on the semaphore or the
|
|
|
|
* semaphore has been signaled.
|
2013-09-03 13:50:15 +00:00
|
|
|
* @retval NIL_MSG_RST if the semaphore has been reset using @p chSemReset().
|
2013-09-01 08:19:24 +00:00
|
|
|
* @retval NIL_MSG_TMO if the semaphore has not been signaled or reset within
|
|
|
|
* the specified timeout.
|
|
|
|
*
|
|
|
|
* @sclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
msg_t chSemWaitTimeoutS(semaphore_t *sp, systime_t timeout) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
/* Note, the semaphore counter is a volatile variable so accesses are
|
|
|
|
manually optimized.*/
|
|
|
|
cnt_t cnt = sp->cnt;
|
|
|
|
if (cnt <= 0) {
|
|
|
|
if (TIME_IMMEDIATE == timeout)
|
2013-09-03 13:21:10 +00:00
|
|
|
return MSG_TIMEOUT;
|
2013-09-01 08:19:24 +00:00
|
|
|
sp->cnt = cnt - 1;
|
|
|
|
nil.current->u1.semp = sp;
|
2013-09-03 13:50:15 +00:00
|
|
|
return chSchGoSleepTimeoutS(NIL_STATE_WTSEM, timeout);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
sp->cnt = cnt - 1;
|
2013-09-03 13:21:10 +00:00
|
|
|
return MSG_OK;
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a signal operation on a semaphore.
|
|
|
|
* @post This function does not reschedule so a call to a rescheduling
|
|
|
|
* function must be performed before unlocking the kernel. Note that
|
|
|
|
* interrupt handlers always reschedule on exit so an explicit
|
|
|
|
* reschedule must not be performed in ISRs.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t structure
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSemSignal(semaphore_t *sp) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLock();
|
|
|
|
chSemSignalI(sp);
|
|
|
|
chSchRescheduleS();
|
|
|
|
chSysUnlock();
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a signal operation on a semaphore.
|
|
|
|
* @post This function does not reschedule so a call to a rescheduling
|
|
|
|
* function must be performed before unlocking the kernel. Note that
|
|
|
|
* interrupt handlers always reschedule on exit so an explicit
|
|
|
|
* reschedule must not be performed in ISRs.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t structure
|
|
|
|
*
|
|
|
|
* @iclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSemSignalI(semaphore_t *sp) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
if (++sp->cnt <= 0) {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr = nil.threads;
|
2013-09-01 08:19:24 +00:00
|
|
|
while (true) {
|
|
|
|
/* Is this thread waiting on this semaphore?*/
|
|
|
|
if (tr->u1.semp == sp) {
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(NIL_THD_IS_WTSEM(tr), "not waiting");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSchReadyI(tr, MSG_OK);
|
2013-09-01 08:19:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
tr++;
|
2013-09-04 08:19:38 +00:00
|
|
|
|
|
|
|
chDbgAssert(tr < &nil.threads[NIL_CFG_NUM_THREADS],
|
|
|
|
"pointer out of range");
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a reset operation on the semaphore.
|
|
|
|
* @post After invoking this function all the threads waiting on the
|
|
|
|
* semaphore, if any, are released and the semaphore counter is set
|
|
|
|
* to the specified, non negative, value.
|
|
|
|
* @post This function does not reschedule so a call to a rescheduling
|
|
|
|
* function must be performed before unlocking the kernel. Note that
|
|
|
|
* interrupt handlers always reschedule on exit so an explicit
|
|
|
|
* reschedule must not be performed in ISRs.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t structure
|
|
|
|
* @param[in] n the new value of the semaphore counter. The value must
|
|
|
|
* be non-negative.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSemReset(semaphore_t *sp, cnt_t n) {
|
2013-09-01 08:19:24 +00:00
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chSysLock();
|
|
|
|
chSemResetI(sp, n);
|
|
|
|
chSchRescheduleS();
|
|
|
|
chSysUnlock();
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Performs a reset operation on the semaphore.
|
|
|
|
* @post After invoking this function all the threads waiting on the
|
|
|
|
* semaphore, if any, are released and the semaphore counter is set
|
|
|
|
* to the specified, non negative, value.
|
|
|
|
* @post This function does not reschedule so a call to a rescheduling
|
|
|
|
* function must be performed before unlocking the kernel. Note that
|
|
|
|
* interrupt handlers always reschedule on exit so an explicit
|
|
|
|
* reschedule must not be performed in ISRs.
|
|
|
|
*
|
|
|
|
* @param[in] sp pointer to a @p semaphore_t structure
|
|
|
|
* @param[in] n the new value of the semaphore counter. The value must
|
|
|
|
* be non-negative.
|
|
|
|
*
|
|
|
|
* @iclass
|
|
|
|
*/
|
2013-09-03 13:50:15 +00:00
|
|
|
void chSemResetI(semaphore_t *sp, cnt_t n) {
|
2013-09-04 08:19:38 +00:00
|
|
|
thread_reference_t tr;
|
2013-09-01 08:19:24 +00:00
|
|
|
cnt_t cnt;
|
|
|
|
|
|
|
|
cnt = sp->cnt;
|
|
|
|
sp->cnt = n;
|
|
|
|
tr = nil.threads;
|
|
|
|
while (cnt < 0) {
|
|
|
|
/* Is this thread waiting on this semaphore?*/
|
|
|
|
if (tr->u1.semp == sp) {
|
|
|
|
|
2013-09-03 13:50:15 +00:00
|
|
|
chDbgAssert(NIL_THD_IS_WTSEM(tr), "not waiting");
|
2013-09-01 08:19:24 +00:00
|
|
|
|
|
|
|
cnt++;
|
2013-09-03 13:50:15 +00:00
|
|
|
chSchReadyI(tr, MSG_RESET);
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
tr++;
|
2013-09-04 08:19:38 +00:00
|
|
|
|
|
|
|
chDbgAssert(tr < &nil.threads[NIL_CFG_NUM_THREADS],
|
|
|
|
"pointer out of range");
|
2013-09-01 08:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-04 10:26:42 +00:00
|
|
|
/**
|
|
|
|
* @brief Adds a set of event flags directly to the specified @p thread_t.
|
|
|
|
*
|
|
|
|
* @param[in] tp the thread to be signaled
|
|
|
|
* @param[in] mask the event flags set to be ORed
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
void chEvtSignal(thread_t *tp, eventmask_t mask) {
|
|
|
|
|
|
|
|
chSysLock();
|
|
|
|
chEvtSignalI(tp, mask);
|
|
|
|
chSchRescheduleS();
|
|
|
|
chSysUnlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Adds a set of event flags directly to the specified @p thread_t.
|
|
|
|
* @post This function does not reschedule so a call to a rescheduling
|
|
|
|
* function must be performed before unlocking the kernel. Note that
|
|
|
|
* interrupt handlers always reschedule on exit so an explicit
|
|
|
|
* reschedule must not be performed in ISRs.
|
|
|
|
*
|
|
|
|
* @param[in] tp the thread to be signaled
|
|
|
|
* @param[in] mask the event flags set to be ORed
|
|
|
|
*
|
|
|
|
* @iclass
|
|
|
|
*/
|
|
|
|
void chEvtSignalI(thread_t *tp, eventmask_t mask) {
|
|
|
|
|
|
|
|
tp->epmask |= mask;
|
|
|
|
if (NIL_THD_IS_WTOREVT(tp) && ((tp->epmask & tp->u1.ewmask) != 0))
|
|
|
|
chSchReadyI(tp, MSG_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Waits for any of the specified events.
|
|
|
|
* @details The function waits for any event among those specified in
|
|
|
|
* @p mask to become pending then the events are cleared and
|
|
|
|
* returned.
|
|
|
|
*
|
|
|
|
* @param[in] mask mask of the event flags that the function should wait
|
|
|
|
* for, @p ALL_EVENTS enables all the events
|
|
|
|
* @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 mask of the served and cleared events.
|
|
|
|
* @retval 0 if the operation has timed out.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
eventmask_t chEvtWaitAnyTimeout(eventmask_t mask, systime_t timeout) {
|
|
|
|
thread_t *ctp = nil.current;
|
|
|
|
eventmask_t m;
|
|
|
|
|
|
|
|
chSysLock();
|
|
|
|
|
|
|
|
if ((m = (ctp->epmask & mask)) == 0) {
|
|
|
|
if (TIME_IMMEDIATE == timeout) {
|
|
|
|
chSysUnlock();
|
|
|
|
return (eventmask_t)0;
|
|
|
|
}
|
|
|
|
ctp->u1.ewmask = mask;
|
|
|
|
if (chSchGoSleepTimeoutS(NIL_STATE_WTOREVT, timeout) < MSG_OK) {
|
|
|
|
chSysUnlock();
|
|
|
|
return (eventmask_t)0;
|
|
|
|
}
|
|
|
|
m = ctp->epmask & mask;
|
|
|
|
}
|
|
|
|
ctp->epmask &= ~m;
|
|
|
|
|
|
|
|
chSysUnlock();
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2013-09-01 08:19:24 +00:00
|
|
|
/** @} */
|