pinebuds/rtos/rtx5/rtx_timer.c

463 lines
12 KiB
C

/*
* Copyright (c) 2013-2019 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* -----------------------------------------------------------------------------
*
* Project: CMSIS-RTOS RTX
* Title: Timer functions
*
* -----------------------------------------------------------------------------
*/
#include "rtx_lib.h"
// OS Runtime Object Memory Usage
#if ((defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0)))
osRtxObjectMemUsage_t osRtxTimerMemUsage
__attribute__((section(".data.os.timer.obj"))) = {0U, 0U, 0U};
#endif
// ==== Helper functions ====
/// Insert Timer into the Timer List sorted by Time.
/// \param[in] timer timer object.
/// \param[in] tick timer tick.
static void TimerInsert(os_timer_t *timer, uint32_t tick) {
os_timer_t *prev, *next;
prev = NULL;
next = osRtxInfo.timer.list;
while ((next != NULL) && (next->tick <= tick)) {
tick -= next->tick;
prev = next;
next = next->next;
}
timer->tick = tick;
timer->prev = prev;
timer->next = next;
if (next != NULL) {
next->tick -= timer->tick;
next->prev = timer;
}
if (prev != NULL) {
prev->next = timer;
} else {
osRtxInfo.timer.list = timer;
}
}
/// Remove Timer from the Timer List.
/// \param[in] timer timer object.
static void TimerRemove(const os_timer_t *timer) {
if (timer->next != NULL) {
timer->next->tick += timer->tick;
timer->next->prev = timer->prev;
}
if (timer->prev != NULL) {
timer->prev->next = timer->next;
} else {
osRtxInfo.timer.list = timer->next;
}
}
/// Unlink Timer from the Timer List Head.
/// \param[in] timer timer object.
static void TimerUnlink(const os_timer_t *timer) {
if (timer->next != NULL) {
timer->next->prev = timer->prev;
}
osRtxInfo.timer.list = timer->next;
}
// ==== Library functions ====
/// Timer Tick (called each SysTick).
static void osRtxTimerTick(void) {
os_timer_t *timer;
osStatus_t status;
timer = osRtxInfo.timer.list;
if (timer == NULL) {
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return;
}
timer->tick--;
while ((timer != NULL) && (timer->tick == 0U)) {
TimerUnlink(timer);
status = osMessageQueuePut(osRtxInfo.timer.mq, &timer->finfo, 0U, 0U);
if (status != osOK) {
(void)osRtxErrorNotify(osRtxErrorTimerQueueOverflow, timer);
}
if (timer->type == osRtxTimerPeriodic) {
TimerInsert(timer, timer->load);
} else {
timer->state = osRtxTimerStopped;
}
timer = osRtxInfo.timer.list;
}
}
/// Timer Thread
__WEAK __NO_RETURN void osRtxTimerThread(void *argument) {
os_timer_finfo_t finfo;
osStatus_t status;
(void)argument;
osRtxInfo.timer.mq = osRtxMessageQueueId(
osMessageQueueNew(osRtxConfig.timer_mq_mcnt, sizeof(os_timer_finfo_t),
osRtxConfig.timer_mq_attr));
osRtxInfo.timer.tick = osRtxTimerTick;
for (;;) {
// lint -e{934} "Taking address of near auto variable"
status = osMessageQueueGet(osRtxInfo.timer.mq, &finfo, NULL, osWaitForever);
if (status == osOK) {
EvrRtxTimerCallback(finfo.func, finfo.arg);
(finfo.func)(finfo.arg);
}
}
}
// ==== Service Calls ====
/// Create and Initialize a timer.
/// \note API identical to osTimerNew
static osTimerId_t svcRtxTimerNew(osTimerFunc_t func, osTimerType_t type,
void *argument, const osTimerAttr_t *attr) {
os_timer_t *timer;
uint8_t flags;
const char *name;
// Check parameters
if ((func == NULL) || ((type != osTimerOnce) && (type != osTimerPeriodic))) {
EvrRtxTimerError(NULL, (int32_t)osErrorParameter);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return NULL;
}
// Process attributes
if (attr != NULL) {
name = attr->name;
// lint -e{9079} "conversion from pointer to void to pointer to other type"
// [MISRA Note 6]
timer = attr->cb_mem;
if (timer != NULL) {
// lint -e(923) -e(9078) "cast from pointer to unsigned int" [MISRA Note
// 7]
if ((((uint32_t)timer & 3U) != 0U) ||
(attr->cb_size < sizeof(os_timer_t))) {
EvrRtxTimerError(NULL, osRtxErrorInvalidControlBlock);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return NULL;
}
} else {
if (attr->cb_size != 0U) {
EvrRtxTimerError(NULL, osRtxErrorInvalidControlBlock);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return NULL;
}
}
} else {
name = NULL;
timer = NULL;
}
// Allocate object memory if not provided
if (timer == NULL) {
if (osRtxInfo.mpi.timer != NULL) {
// lint -e{9079} "conversion from pointer to void to pointer to other
// type" [MISRA Note 5]
timer = osRtxMemoryPoolAlloc(osRtxInfo.mpi.timer);
} else {
// lint -e{9079} "conversion from pointer to void to pointer to other
// type" [MISRA Note 5]
timer = osRtxMemoryAlloc(osRtxInfo.mem.common, sizeof(os_timer_t), 1U);
}
#if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
if (timer != NULL) {
uint32_t used;
osRtxTimerMemUsage.cnt_alloc++;
used = osRtxTimerMemUsage.cnt_alloc - osRtxTimerMemUsage.cnt_free;
if (osRtxTimerMemUsage.max_used < used) {
osRtxTimerMemUsage.max_used = used;
}
}
#endif
flags = osRtxFlagSystemObject;
} else {
flags = 0U;
}
if (timer != NULL) {
// Initialize control block
timer->id = osRtxIdTimer;
timer->state = osRtxTimerStopped;
timer->flags = flags;
timer->type = (uint8_t)type;
timer->name = name;
timer->prev = NULL;
timer->next = NULL;
timer->tick = 0U;
timer->load = 0U;
timer->finfo.func = func;
timer->finfo.arg = argument;
EvrRtxTimerCreated(timer, timer->name);
} else {
EvrRtxTimerError(NULL, (int32_t)osErrorNoMemory);
}
return timer;
}
/// Get name of a timer.
/// \note API identical to osTimerGetName
static const char *svcRtxTimerGetName(osTimerId_t timer_id) {
os_timer_t *timer = osRtxTimerId(timer_id);
// Check parameters
if ((timer == NULL) || (timer->id != osRtxIdTimer)) {
EvrRtxTimerGetName(timer, NULL);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return NULL;
}
EvrRtxTimerGetName(timer, timer->name);
return timer->name;
}
/// Start or restart a timer.
/// \note API identical to osTimerStart
static osStatus_t svcRtxTimerStart(osTimerId_t timer_id, uint32_t ticks) {
os_timer_t *timer = osRtxTimerId(timer_id);
// Check parameters
if ((timer == NULL) || (timer->id != osRtxIdTimer) || (ticks == 0U)) {
EvrRtxTimerError(timer, (int32_t)osErrorParameter);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return osErrorParameter;
}
if (timer->state == osRtxTimerRunning) {
TimerRemove(timer);
} else {
if (osRtxInfo.timer.tick == NULL) {
EvrRtxTimerError(timer, (int32_t)osErrorResource);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return osErrorResource;
} else {
timer->state = osRtxTimerRunning;
timer->load = ticks;
}
}
TimerInsert(timer, ticks);
EvrRtxTimerStarted(timer);
return osOK;
}
/// Stop a timer.
/// \note API identical to osTimerStop
static osStatus_t svcRtxTimerStop(osTimerId_t timer_id) {
os_timer_t *timer = osRtxTimerId(timer_id);
// Check parameters
if ((timer == NULL) || (timer->id != osRtxIdTimer)) {
EvrRtxTimerError(timer, (int32_t)osErrorParameter);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return osErrorParameter;
}
// Check object state
if (timer->state != osRtxTimerRunning) {
EvrRtxTimerError(timer, (int32_t)osErrorResource);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return osErrorResource;
}
timer->state = osRtxTimerStopped;
TimerRemove(timer);
EvrRtxTimerStopped(timer);
return osOK;
}
/// Check if a timer is running.
/// \note API identical to osTimerIsRunning
static uint32_t svcRtxTimerIsRunning(osTimerId_t timer_id) {
os_timer_t *timer = osRtxTimerId(timer_id);
uint32_t is_running;
// Check parameters
if ((timer == NULL) || (timer->id != osRtxIdTimer)) {
EvrRtxTimerIsRunning(timer, 0U);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return 0U;
}
if (timer->state == osRtxTimerRunning) {
EvrRtxTimerIsRunning(timer, 1U);
is_running = 1U;
} else {
EvrRtxTimerIsRunning(timer, 0U);
is_running = 0;
}
return is_running;
}
/// Delete a timer.
/// \note API identical to osTimerDelete
static osStatus_t svcRtxTimerDelete(osTimerId_t timer_id) {
os_timer_t *timer = osRtxTimerId(timer_id);
// Check parameters
if ((timer == NULL) || (timer->id != osRtxIdTimer)) {
EvrRtxTimerError(timer, (int32_t)osErrorParameter);
// lint -e{904} "Return statement before end of function" [MISRA Note 1]
return osErrorParameter;
}
if (timer->state == osRtxTimerRunning) {
TimerRemove(timer);
}
// Mark object as inactive and invalid
timer->state = osRtxTimerInactive;
timer->id = osRtxIdInvalid;
// Free object memory
if ((timer->flags & osRtxFlagSystemObject) != 0U) {
if (osRtxInfo.mpi.timer != NULL) {
(void)osRtxMemoryPoolFree(osRtxInfo.mpi.timer, timer);
} else {
(void)osRtxMemoryFree(osRtxInfo.mem.common, timer);
}
#if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
osRtxTimerMemUsage.cnt_free++;
#endif
}
EvrRtxTimerDestroyed(timer);
return osOK;
}
// Service Calls definitions
// lint ++flb "Library Begin" [MISRA Note 11]
SVC0_4(TimerNew, osTimerId_t, osTimerFunc_t, osTimerType_t, void *,
const osTimerAttr_t *)
SVC0_1(TimerGetName, const char *, osTimerId_t)
SVC0_2(TimerStart, osStatus_t, osTimerId_t, uint32_t)
SVC0_1(TimerStop, osStatus_t, osTimerId_t)
SVC0_1(TimerIsRunning, uint32_t, osTimerId_t)
SVC0_1(TimerDelete, osStatus_t, osTimerId_t)
// lint --flb "Library End"
// ==== Public API ====
/// Create and Initialize a timer.
osTimerId_t osTimerNew(osTimerFunc_t func, osTimerType_t type, void *argument,
const osTimerAttr_t *attr) {
osTimerId_t timer_id;
EvrRtxTimerNew(func, type, argument, attr);
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerError(NULL, (int32_t)osErrorISR);
timer_id = NULL;
} else {
timer_id = __svcTimerNew(func, type, argument, attr);
}
return timer_id;
}
/// Get name of a timer.
const char *osTimerGetName(osTimerId_t timer_id) {
const char *name;
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerGetName(timer_id, NULL);
name = NULL;
} else {
name = __svcTimerGetName(timer_id);
}
return name;
}
/// Start or restart a timer.
osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks) {
osStatus_t status;
EvrRtxTimerStart(timer_id, ticks);
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerError(timer_id, (int32_t)osErrorISR);
status = osErrorISR;
} else {
status = __svcTimerStart(timer_id, ticks);
}
return status;
}
/// Stop a timer.
osStatus_t osTimerStop(osTimerId_t timer_id) {
osStatus_t status;
EvrRtxTimerStop(timer_id);
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerError(timer_id, (int32_t)osErrorISR);
status = osErrorISR;
} else {
status = __svcTimerStop(timer_id);
}
return status;
}
/// Check if a timer is running.
uint32_t osTimerIsRunning(osTimerId_t timer_id) {
uint32_t is_running;
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerIsRunning(timer_id, 0U);
is_running = 0U;
} else {
is_running = __svcTimerIsRunning(timer_id);
}
return is_running;
}
/// Delete a timer.
osStatus_t osTimerDelete(osTimerId_t timer_id) {
osStatus_t status;
EvrRtxTimerDelete(timer_id);
if (IsIrqMode() || IsIrqMasked()) {
EvrRtxTimerError(timer_id, (int32_t)osErrorISR);
status = osErrorISR;
} else {
status = __svcTimerDelete(timer_id);
}
return status;
}