2015-04-25 22:36:39 +02:00
|
|
|
/**
|
2015-09-27 18:58:30 +02:00
|
|
|
* Copyright (C) 2013 Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
2015-04-25 22:36:39 +02:00
|
|
|
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
|
|
* directory for more details.
|
2017-06-22 15:43:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2018-06-01 12:17:51 +02:00
|
|
|
* @ingroup cpu_native
|
2017-06-22 15:43:17 +02:00
|
|
|
* @ingroup drivers_periph_timer
|
2015-04-25 22:36:39 +02:00
|
|
|
* @{
|
2017-06-22 15:43:17 +02:00
|
|
|
*
|
2015-04-25 22:36:39 +02:00
|
|
|
* @file
|
2017-06-22 15:43:17 +02:00
|
|
|
* @brief Native CPU periph/timer.h implementation
|
2015-04-25 22:36:39 +02:00
|
|
|
*
|
|
|
|
* Uses POSIX realtime clock and POSIX itimer to mimic hardware.
|
|
|
|
*
|
2015-09-27 18:58:30 +02:00
|
|
|
* This is based on native's hwtimer implementation by Ludwig Knüpfer.
|
2023-03-06 12:03:26 +01:00
|
|
|
* I removed the multiplexing, as ztimer does the same. (kaspar)
|
2015-04-25 22:36:39 +02:00
|
|
|
*
|
2017-06-22 15:43:17 +02:00
|
|
|
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
|
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
*
|
2015-04-25 22:36:39 +02:00
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
2023-10-23 13:11:23 +02:00
|
|
|
#include <err.h>
|
2015-04-25 22:36:39 +02:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2023-10-23 13:11:23 +02:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
2015-04-25 22:36:39 +02:00
|
|
|
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "cpu_conf.h"
|
|
|
|
#include "native_internal.h"
|
|
|
|
#include "periph/timer.h"
|
2023-10-23 13:11:23 +02:00
|
|
|
#include "time_units.h"
|
2015-04-25 22:36:39 +02:00
|
|
|
|
2020-10-22 11:34:00 +02:00
|
|
|
#define ENABLE_DEBUG 0
|
2015-04-25 22:36:39 +02:00
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
#define NATIVE_TIMER_SPEED 1000000
|
|
|
|
|
|
|
|
static unsigned long time_null;
|
|
|
|
|
2016-02-17 12:18:24 +01:00
|
|
|
static timer_cb_t _callback;
|
|
|
|
static void *_cb_arg;
|
2015-04-25 22:36:39 +02:00
|
|
|
|
2023-10-23 13:11:23 +02:00
|
|
|
static struct itimerspec its;
|
|
|
|
|
|
|
|
static timer_t itimer_monotonic;
|
2015-04-25 22:36:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* returns ticks for give timespec
|
|
|
|
*/
|
|
|
|
static unsigned long ts2ticks(struct timespec *tp)
|
|
|
|
{
|
|
|
|
/* TODO: check for overflow */
|
2021-02-23 09:52:05 +01:00
|
|
|
return (((unsigned long)tp->tv_sec * NATIVE_TIMER_SPEED) + (tp->tv_nsec / 1000));
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* native timer signal handler
|
|
|
|
*
|
|
|
|
* set new system timer, call timer interrupt handler
|
|
|
|
*/
|
|
|
|
void native_isr_timer(void)
|
|
|
|
{
|
|
|
|
DEBUG("%s\n", __func__);
|
|
|
|
|
2016-02-17 12:18:24 +01:00
|
|
|
_callback(_cb_arg, 0);
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
2020-10-30 12:14:35 +01:00
|
|
|
int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
2015-04-25 22:36:39 +02:00
|
|
|
{
|
2015-10-03 22:52:33 +02:00
|
|
|
(void)freq;
|
2015-04-25 22:36:39 +02:00
|
|
|
DEBUG("%s\n", __func__);
|
|
|
|
if (dev >= TIMER_NUMOF) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-03 22:52:33 +02:00
|
|
|
if (freq != NATIVE_TIMER_SPEED) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-04-25 22:36:39 +02:00
|
|
|
|
|
|
|
/* initialize time delta */
|
|
|
|
time_null = 0;
|
|
|
|
time_null = timer_read(0);
|
|
|
|
|
2016-02-17 12:18:24 +01:00
|
|
|
_callback = cb;
|
|
|
|
_cb_arg = arg;
|
2023-10-23 13:11:23 +02:00
|
|
|
|
|
|
|
if (timer_create(CLOCK_MONOTONIC, NULL, &itimer_monotonic) != 0) {
|
|
|
|
DEBUG_PUTS("Failed to create a monotonic itimer");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-01-14 15:34:53 +01:00
|
|
|
if (register_interrupt(SIGALRM, native_isr_timer) != 0) {
|
2023-10-23 13:11:23 +02:00
|
|
|
DEBUG_PUTS("Failed to register SIGALRM handler");
|
|
|
|
return -1;
|
2017-01-14 15:34:53 +01:00
|
|
|
}
|
2015-04-25 22:36:39 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-01 14:18:18 +01:00
|
|
|
static void do_timer_set(unsigned int offset, bool periodic)
|
2015-04-25 22:36:39 +02:00
|
|
|
{
|
|
|
|
DEBUG("%s\n", __func__);
|
|
|
|
|
|
|
|
if (offset && offset < NATIVE_TIMER_MIN_RES) {
|
|
|
|
offset = NATIVE_TIMER_MIN_RES;
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:11:23 +02:00
|
|
|
memset(&its, 0, sizeof(its));
|
|
|
|
its.it_value.tv_sec = offset / NATIVE_TIMER_SPEED;
|
|
|
|
its.it_value.tv_nsec = (offset % NATIVE_TIMER_SPEED) * (NS_PER_SEC / NATIVE_TIMER_SPEED);
|
2021-02-22 22:09:06 +01:00
|
|
|
if (periodic) {
|
2023-10-23 13:11:23 +02:00
|
|
|
its.it_interval = its.it_value;
|
2021-02-22 22:09:06 +01:00
|
|
|
}
|
2015-04-25 22:36:39 +02:00
|
|
|
|
2023-10-23 13:11:23 +02:00
|
|
|
DEBUG("timer_set(): setting %lu.%09lu\n", (unsigned long)its.it_value.tv_sec, its.it_value.tv_nsec);
|
2015-07-09 15:08:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int timer_set(tim_t dev, int channel, unsigned int offset)
|
|
|
|
{
|
|
|
|
DEBUG("%s\n", __func__);
|
|
|
|
|
2015-09-23 14:48:23 +02:00
|
|
|
if (channel != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-07-09 15:08:02 +02:00
|
|
|
if (!offset) {
|
|
|
|
offset = NATIVE_TIMER_MIN_RES;
|
|
|
|
}
|
|
|
|
|
2022-03-01 14:18:18 +01:00
|
|
|
do_timer_set(offset, false);
|
|
|
|
timer_start(dev);
|
2015-04-25 22:36:39 +02:00
|
|
|
|
2019-09-11 13:50:26 +02:00
|
|
|
return 0;
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
|
|
|
{
|
|
|
|
uint32_t now = timer_read(dev);
|
2016-01-06 13:39:49 +01:00
|
|
|
return timer_set(dev, channel, value - now);
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
2021-02-22 22:09:06 +01:00
|
|
|
int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags)
|
|
|
|
{
|
|
|
|
(void)flags;
|
|
|
|
|
|
|
|
if (channel != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-03-01 14:18:18 +01:00
|
|
|
do_timer_set(value, true);
|
|
|
|
|
|
|
|
if (!(flags & TIM_FLAG_SET_STOPPED)) {
|
|
|
|
timer_start(dev);
|
|
|
|
}
|
2021-02-22 22:09:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-25 22:36:39 +02:00
|
|
|
int timer_clear(tim_t dev, int channel)
|
|
|
|
{
|
2015-07-09 15:08:02 +02:00
|
|
|
(void)channel;
|
|
|
|
|
2022-03-01 14:18:18 +01:00
|
|
|
do_timer_set(0, false);
|
|
|
|
timer_start(dev);
|
2015-07-09 15:08:02 +02:00
|
|
|
|
2019-09-11 13:50:26 +02:00
|
|
|
return 0;
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void timer_start(tim_t dev)
|
|
|
|
{
|
|
|
|
(void)dev;
|
|
|
|
DEBUG("%s\n", __func__);
|
2021-02-22 22:09:06 +01:00
|
|
|
|
|
|
|
_native_syscall_enter();
|
2023-10-23 13:11:23 +02:00
|
|
|
if (timer_settime(itimer_monotonic, 0, &its, NULL) == -1) {
|
|
|
|
err(EXIT_FAILURE, "timer_start: timer_settime");
|
2021-02-22 22:09:06 +01:00
|
|
|
}
|
|
|
|
_native_syscall_leave();
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void timer_stop(tim_t dev)
|
|
|
|
{
|
|
|
|
(void)dev;
|
|
|
|
DEBUG("%s\n", __func__);
|
2021-02-22 22:09:06 +01:00
|
|
|
|
|
|
|
_native_syscall_enter();
|
2023-10-23 13:11:23 +02:00
|
|
|
struct itimerspec zero = {0};
|
|
|
|
if (timer_settime(itimer_monotonic, 0, &zero, &its) == -1) {
|
|
|
|
err(EXIT_FAILURE, "timer_stop: timer_settime");
|
2021-02-22 22:09:06 +01:00
|
|
|
}
|
|
|
|
_native_syscall_leave();
|
|
|
|
|
2023-10-23 13:11:23 +02:00
|
|
|
DEBUG("time left: %lu.%09lu\n", (unsigned long)its.it_value.tv_sec, its.it_value.tv_nsec);
|
2015-04-25 22:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int timer_read(tim_t dev)
|
|
|
|
{
|
|
|
|
if (dev >= TIMER_NUMOF) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec t;
|
|
|
|
|
|
|
|
DEBUG("timer_read()\n");
|
|
|
|
|
|
|
|
_native_syscall_enter();
|
|
|
|
|
2023-04-17 15:01:49 +02:00
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &t) == -1) {
|
2015-04-25 22:36:39 +02:00
|
|
|
err(EXIT_FAILURE, "timer_read: clock_gettime");
|
|
|
|
}
|
|
|
|
|
|
|
|
_native_syscall_leave();
|
|
|
|
|
|
|
|
return ts2ticks(&t) - time_null;
|
|
|
|
}
|