blob: 19d87b057f14a28b2bf0935af28c35eff18ed1a1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* This file contains code that is shared by both sim implementations (signals
* and no-signals).
*/
#include "os/mynewt.h"
#include <hal/hal_bsp.h>
#ifdef __APPLE__
#define _XOPEN_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <assert.h>
#include "sim/sim.h"
#include "sim_priv.h"
#define sim_setjmp(__jb) sigsetjmp(__jb, 0)
#define sim_longjmp(__jb, __ret) siglongjmp(__jb, __ret)
pid_t sim_pid;
void
sim_switch_tasks(void)
{
struct os_task *t, *next_t;
struct stack_frame *sf;
int rc;
OS_ASSERT_CRITICAL();
t = os_sched_get_current_task();
next_t = os_sched_next_task();
if (t == next_t) {
/*
* Context switch not needed - just return.
*/
return;
}
if (t) {
sf = (struct stack_frame *) t->t_stackptr;
rc = sim_setjmp(sf->sf_jb);
if (rc != 0) {
OS_ASSERT_CRITICAL();
return;
}
}
os_sched_ctx_sw_hook(next_t);
os_sched_set_current_task(next_t);
sf = (struct stack_frame *) next_t->t_stackptr;
sim_longjmp(sf->sf_jb, 1);
}
void
sim_tick(void)
{
struct timeval time_now, time_diff;
int ticks;
static struct timeval time_last;
static int time_inited;
OS_ASSERT_CRITICAL();
if (!time_inited) {
gettimeofday(&time_last, NULL);
time_inited = 1;
}
gettimeofday(&time_now, NULL);
if (timercmp(&time_now, &time_last, <)) {
/*
* System time going backwards.
*/
time_last = time_now;
} else {
timersub(&time_now, &time_last, &time_diff);
ticks = time_diff.tv_sec * OS_TICKS_PER_SEC;
ticks += time_diff.tv_usec / OS_USEC_PER_TICK;
/*
* Update 'time_last' but account for the remainder usecs that did not
* contribute towards whole 'ticks'.
*/
time_diff.tv_sec = 0;
time_diff.tv_usec %= OS_USEC_PER_TICK;
timersub(&time_now, &time_diff, &time_last);
os_time_advance(ticks);
}
}
static void
sim_start_timer(void)
{
struct itimerval it;
int rc;
memset(&it, 0, sizeof(it));
it.it_value.tv_sec = 0;
it.it_value.tv_usec = OS_USEC_PER_TICK;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = OS_USEC_PER_TICK;
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
static void
sim_stop_timer(void)
{
struct itimerval it;
int rc;
memset(&it, 0, sizeof(it));
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
/*
* Called from 'os_arch_frame_init()' when setjmp returns indirectly via
* longjmp. The return value of setjmp is passed to this function as 'rc'.
*/
void
sim_task_start(struct stack_frame *sf, int rc)
{
struct os_task *task;
/*
* Interrupts are disabled when a task starts executing. This happens in
* two different ways:
* - via sim_os_start() for the first task.
* - via os_sched() for all other tasks.
*
* Enable interrupts before starting the task.
*/
OS_EXIT_CRITICAL(0);
task = sf->sf_task;
task->t_func(task->t_arg);
/* If a unit test is executing, the test is complete when a task handler
* returns.
*/
#if MYNEWT_VAL(SELFTEST)
void tu_restart(void);
tu_restart();
#else
/* Otherwise, a task handler should never return. */
assert(0);
#endif
}
os_stack_t *
sim_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size)
{
struct stack_frame *sf;
sf = (struct stack_frame *) ((uint8_t *) stack_top - sizeof(*sf));
sf->sf_task = t;
os_arch_frame_init(sf);
return ((os_stack_t *)sf);
}
os_error_t
sim_os_start(void)
{
struct stack_frame *sf;
struct os_task *t;
os_sr_t sr;
/*
* Disable interrupts before enabling any interrupt sources. Pending
* interrupts will be recognized when the first task starts executing.
*/
OS_ENTER_CRITICAL(sr);
assert(sr == 0);
/* Enable the interrupt sources */
sim_start_timer();
t = os_sched_next_task();
os_sched_set_current_task(t);
g_os_started = 1;
sf = (struct stack_frame *) t->t_stackptr;
sim_longjmp(sf->sf_jb, 1);
return 0;
}
/**
* Stops the tick timer and clears the "started" flag. This function is only
* implemented for sim.
*/
void
sim_os_stop(void)
{
sim_stop_timer();
sim_signals_cleanup();
g_os_started = 0;
}
os_error_t
sim_os_init(void)
{
sim_pid = getpid();
g_current_task = NULL;
STAILQ_INIT(&g_os_task_list);
TAILQ_INIT(&g_os_run_list);
TAILQ_INIT(&g_os_sleep_list);
sim_signals_init();
os_init_idle_task();
return OS_OK;
}