zephyr/kernel/microkernel/k_timer.c
Juan Manuel Cruz 787b41c8a3 debug: adds object tracing capability to microkernel timers
Microkernel timers are defined at compile time as a static list
but they are allocated dynamically in kernel execution.

The object tracing list will only list those timers that are
currently allocated at debug time. For this reason, timers
can be removed from the tracing list at any time.

A very simple double linked list was implemented to reduce the
complexity of the action to remove an item from the list from O(n)
to O(1) and simplify the remove implementation.

Change-Id: Ib7ea718b52e7c719a32b3fa4ff1d7e6b00482c28
Signed-off-by: Juan Manuel Cruz <juan.m.cruz.alcaraz@intel.com>
2016-03-11 22:13:15 +00:00

397 lines
8.2 KiB
C

/* timer kernel services */
/*
* Copyright (c) 1997-2015 Wind River Systems, Inc.
*
* 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
*
* http://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.
*/
#include <microkernel.h>
#include <toolchain.h>
#include <sections.h>
#include <micro_private.h>
#include <drivers/system_timer.h>
#include <misc/debug/object_tracing_common.h>
extern struct k_timer _k_timer_blocks[];
struct k_timer *_k_timer_list_head;
struct k_timer *_k_timer_list_tail;
/**
* @brief Insert a timer into the timer queue
* @param T Timer
* @return N/A
*/
void _k_timer_enlist(struct k_timer *T)
{
struct k_timer *P = _k_timer_list_head;
struct k_timer *Q = NULL;
while (P && (T->duration > P->duration)) {
T->duration -= P->duration;
Q = P;
P = P->next;
}
if (P) {
P->duration -= T->duration;
P->prev = T;
} else {
_k_timer_list_tail = T;
}
if (Q) {
Q->next = T;
} else {
_k_timer_list_head = T;
}
T->next = P;
T->prev = Q;
}
/**
* @brief Remove a timer from the timer queue
* @param T Timer
* @return N/A
*/
void _k_timer_delist(struct k_timer *T)
{
struct k_timer *P = T->next;
struct k_timer *Q = T->prev;
if (P) {
P->duration += T->duration;
P->prev = Q;
} else
_k_timer_list_tail = Q;
if (Q)
Q->next = P;
else
_k_timer_list_head = P;
T->duration = -1;
}
/**
* @brief Allocate timer used for command packet timeout
*
* Allocates timer for command packet and inserts it into the timer queue.
* @param P Arguments
* @return N/A
*/
void _k_timeout_alloc(struct k_args *P)
{
struct k_timer *T;
GETTIMER(T);
T->duration = P->Time.ticks;
T->period = 0;
T->args = P;
_k_timer_enlist(T);
P->Time.timer = T;
SYS_TRACING_OBJ_INIT_DLL(micro_timer, T);
}
/**
* @brief Cancel timer used for command packet timeout
*
* Cancels timer (if not already expired), then reschedules the command packet
* for further processing.
*
* The command that is processed following cancellation is typically NOT the
* command that would have occurred had the timeout expired on its own.
*
* @return N/A
*/
void _k_timeout_cancel(struct k_args *A)
{
struct k_timer *T = A->Time.timer;
if (T->duration != -1) {
_k_timer_delist(T);
TO_ALIST(&_k_command_stack, A);
}
}
/**
* @brief Free timer used for command packet timeout
*
* Cancels timer (if not already expired), then frees it.
* @param T Timer
* @return N/A
*/
void _k_timeout_free(struct k_timer *T)
{
if (T->duration != -1)
_k_timer_delist(T);
FREETIMER(T);
SYS_TRACING_OBJ_REMOVE_DLL(micro_timer, T);
}
/**
* @brief Handle expired timers
*
* Process the sorted list of timers associated with waiting tasks and
* activate each task whose timer has now expired.
*
* With tickless idle, a tick announcement may encompass multiple ticks.
* Due to limitations of the underlying timer driver, the number of elapsed
* ticks may -- under very rare circumstances -- exceed the first timer's
* remaining tick count, although never by more a single tick. This means that
* a task timer may occasionally expire one tick later than it was scheduled to,
* and that a periodic timer may exhibit a slow, ever-increasing degree of drift
* from the main system timer over long intervals.
*
* @param ticks Number of ticks
* @return N/A
*/
void _k_timer_list_update(int ticks)
{
struct k_timer *T;
while (_k_timer_list_head != NULL) {
_k_timer_list_head->duration -= ticks;
if (_k_timer_list_head->duration > 0) {
return;
}
T = _k_timer_list_head;
if (T == _k_timer_list_tail) {
_k_timer_list_head = _k_timer_list_tail = NULL;
} else {
_k_timer_list_head = T->next;
_k_timer_list_head->prev = NULL;
}
if (T->period) {
T->duration = T->period;
_k_timer_enlist(T);
} else {
T->duration = -1;
}
TO_ALIST(&_k_command_stack, T->args);
ticks = 0; /* don't decrement duration for subsequent timer(s) */
}
}
/**
* @brief Handle timer allocation request
*
* This routine, called by _k_server(), handles the request for allocating a
* timer.
*
* @param P Pointer to timer allocation request arguments.
*
* @return N/A
*/
void _k_timer_alloc(struct k_args *P)
{
struct k_timer *T;
struct k_args *A;
GETTIMER(T);
P->args.c1.timer = T;
GETARGS(A);
T->args = A;
T->duration = -1; /* -1 indicates that timer is disabled */
SYS_TRACING_OBJ_INIT_DLL(micro_timer, T);
}
ktimer_t task_timer_alloc(void)
{
struct k_args A;
A.Comm = _K_SVC_TIMER_ALLOC;
KERNEL_ENTRY(&A);
return (ktimer_t)A.args.c1.timer;
}
/**
*
* @brief Handle timer deallocation request
*
* This routine, called by _k_server(), handles the request for deallocating a
* timer.
* @param P Pointer to timer deallocation request arguments.
* @return N/A
*/
void _k_timer_dealloc(struct k_args *P)
{
struct k_timer *T = P->args.c1.timer;
struct k_args *A = T->args;
if (T->duration != -1)
_k_timer_delist(T);
FREETIMER(T);
FREEARGS(A);
SYS_TRACING_OBJ_REMOVE_DLL(micro_timer, T);
}
void task_timer_free(ktimer_t timer)
{
struct k_args A;
A.Comm = _K_SVC_TIMER_DEALLOC;
A.args.c1.timer = (struct k_timer *)timer;
KERNEL_ENTRY(&A);
}
/**
* @brief Handle start timer request
*
* This routine, called by _k_server(), handles the start timer request from
* both task_timer_start() and task_timer_restart().
*
* @param P Pointer to timer start request arguments.
*
* @return N/A
*/
void _k_timer_start(struct k_args *P)
{
struct k_timer *T = P->args.c1.timer; /* ptr to the timer to start */
if (T->duration != -1) { /* Stop the timer if it is active */
_k_timer_delist(T);
}
T->duration = (int32_t)P->args.c1.time1; /* Set the initial delay */
T->period = P->args.c1.time2; /* Set the period */
/*
* Either the initial delay and/or the period is invalid. Mark
* the timer as inactive.
*/
if ((T->duration <= 0) || (T->period < 0)) {
T->duration = -1;
return;
}
/* Track the semaphore to signal for when the timer expires. */
if (P->args.c1.sema != _USE_CURRENT_SEM) {
T->args->Comm = _K_SVC_SEM_SIGNAL;
T->args->args.s1.sema = P->args.c1.sema;
}
_k_timer_enlist(T);
}
void task_timer_start(ktimer_t timer, int32_t duration, int32_t period,
ksem_t sema)
{
struct k_args A;
A.Comm = _K_SVC_TIMER_START;
A.args.c1.timer = (struct k_timer *)timer;
A.args.c1.time1 = (int64_t)duration;
A.args.c1.time2 = period;
A.args.c1.sema = sema;
KERNEL_ENTRY(&A);
}
/**
*
* @brief Handle stop timer request
*
* This routine, called by _k_server(), handles the request for stopping a
* timer.
*
* @return N/A
*/
void _k_timer_stop(struct k_args *P)
{
struct k_timer *T = P->args.c1.timer;
if (T->duration != -1)
_k_timer_delist(T);
}
void task_timer_stop(ktimer_t timer)
{
struct k_args A;
A.Comm = _K_SVC_TIMER_STOP;
A.args.c1.timer = (struct k_timer *)timer;
KERNEL_ENTRY(&A);
}
/**
*
* @brief Handle internally issued task wakeup request
*
* This routine, called by _k_server(), handles the request for waking a task
* at the end of its sleep period.
*
* @return N/A
*/
void _k_task_wakeup(struct k_args *P)
{
struct k_timer *T;
struct k_task *X;
X = P->Ctxt.task;
T = P->Time.timer;
FREETIMER(T);
_k_state_bit_reset(X, TF_TIME);
SYS_TRACING_OBJ_REMOVE_DLL(micro_timer, T);
}
/**
*
* @brief Handle task sleep request
*
* This routine, called by _k_server(), handles the request for putting a task
* to sleep.
*
* @param P Pointer to timer sleep request arguments.
* @return N/A
*/
void _k_task_sleep(struct k_args *P)
{
struct k_timer *T;
if ((P->Time.ticks) <= 0) {
return;
}
GETTIMER(T);
T->duration = P->Time.ticks;
T->period = 0;
T->args = P;
P->Comm = _K_SVC_TASK_WAKEUP;
P->Ctxt.task = _k_current_task;
P->Time.timer = T;
_k_timer_enlist(T);
_k_state_bit_set(_k_current_task, TF_TIME);
SYS_TRACING_OBJ_INIT_DLL(micro_timer, T);
}
void task_sleep(int32_t ticks)
{
struct k_args A;
A.Comm = _K_SVC_TASK_SLEEP;
A.Time.ticks = ticks;
KERNEL_ENTRY(&A);
}