mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
libports: system RTC server and test
For now in libports because it relies on contrib sources from musl doing the time data conversion. Issue #3450
This commit is contained in:
parent
0037edfeee
commit
99cb585b6e
99
repos/libports/run/system_rtc.run
Normal file
99
repos/libports/run/system_rtc.run
Normal file
@ -0,0 +1,99 @@
|
||||
# RTC test
|
||||
|
||||
assert_spec x86
|
||||
|
||||
if {[expr ![have_include power_on/qemu]]} {
|
||||
puts "Test requires Qemu."
|
||||
exit 0
|
||||
}
|
||||
|
||||
set build_components {
|
||||
core init timer drivers/rtc server/system_rtc test/system_rtc
|
||||
server/report_rom
|
||||
}
|
||||
|
||||
build $build_components
|
||||
|
||||
create_boot_directory
|
||||
|
||||
set config {
|
||||
<config prio_levels="2" verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Timer"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config verbose="yes">
|
||||
<policy label="rtc_drv -> set_rtc" report="test-system_rtc -> drv_set_rtc"/>
|
||||
<policy label="system_rtc -> set_rtc" report="test-system_rtc -> sys_set_rtc"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="rtc_drv" priority="-1">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Rtc"/></provides>
|
||||
<config allow_setting_rtc="true"/>
|
||||
<route>
|
||||
<service name="ROM" label="set_rtc">
|
||||
<child name="report_rom"/>
|
||||
</service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="system_rtc" priority="-1">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Rtc"/></provides>
|
||||
<config allow_setting_rtc="true"/>
|
||||
<route>
|
||||
<service name="ROM" label="set_rtc"> <child name="report_rom"/> </service>
|
||||
<service name="Rtc"> <child name="rtc_drv"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="test-system_rtc" priority="-1">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config/>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="Rtc"> <child name="system_rtc"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
set boot_components {
|
||||
core ld.lib.so init timer rtc_drv test-system_rtc system_rtc
|
||||
report_rom
|
||||
}
|
||||
|
||||
build_boot_image $boot_components
|
||||
|
||||
append qemu_args " -nographic "
|
||||
|
||||
run_genode_until ".*--- system RTC test finished ---.*\n" 60
|
17
repos/libports/src/server/system_rtc/README
Normal file
17
repos/libports/src/server/system_rtc/README
Normal file
@ -0,0 +1,17 @@
|
||||
The system RTC component provides access to a synthetic real time clock. While
|
||||
it can get its initial value from a hardware RTC, it will interpolate the time
|
||||
by using a Timer session at runtime.
|
||||
|
||||
Like the RTC driver component, if the 'allow_setting_rtc' attribute in the
|
||||
'config' node is set to 'yes', it will use the content of the 'set_rtc' ROM to
|
||||
update the RTC value. A valid ROM must contain a top node with the following
|
||||
attributes:
|
||||
|
||||
* 'year' (e.g. 2019)
|
||||
* 'month' (1 - 12)
|
||||
* 'day' (1 - 31)
|
||||
* 'hour' (0 - 23)
|
||||
* 'minute' (0 - 59)
|
||||
* 'second' (0 - 59)
|
||||
|
||||
The component will always report the time as 24h day in UTC.
|
291
repos/libports/src/server/system_rtc/main.cc
Normal file
291
repos/libports/src/server/system_rtc/main.cc
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* \brief System RTC server
|
||||
* \author Josef Soentgen
|
||||
* \date 2019-07-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/registry.h>
|
||||
#include <root/component.h>
|
||||
#include <rtc_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
namespace Contrib {
|
||||
#include <tm.h>
|
||||
}; /* Contrib */
|
||||
|
||||
|
||||
namespace Util {
|
||||
|
||||
Genode::int64_t convert(Rtc::Timestamp const &ts)
|
||||
{
|
||||
Contrib::tm tm;
|
||||
Genode::memset(&tm, 0, sizeof(Contrib::tm));
|
||||
tm.tm_sec = ts.second;
|
||||
tm.tm_min = ts.minute;
|
||||
tm.tm_hour = ts.hour;
|
||||
tm.tm_mday = ts.day;
|
||||
tm.tm_mon = ts.month - 1;
|
||||
tm.tm_year = ts.year - 1900;
|
||||
|
||||
return Contrib::tm_to_secs(&tm);
|
||||
}
|
||||
|
||||
Rtc::Timestamp convert(Genode::int64_t t)
|
||||
{
|
||||
Contrib::tm tm;
|
||||
Genode::memset(&tm, 0, sizeof(Contrib::tm));
|
||||
|
||||
int const err = Contrib::secs_to_tm((long long)t, &tm);
|
||||
if (err) { Genode::warning("could not convert timestamp"); }
|
||||
|
||||
return Rtc::Timestamp {
|
||||
.microsecond = 0,
|
||||
.second = err ? 0 : (unsigned)tm.tm_sec,
|
||||
.minute = err ? 0 : (unsigned)tm.tm_min,
|
||||
.hour = err ? 0 : (unsigned)tm.tm_hour,
|
||||
.day = err ? 1 : (unsigned)tm.tm_mday,
|
||||
.month = err ? 1 : (unsigned)tm.tm_mon + 1,
|
||||
.year = err ? 1970 : (unsigned)tm.tm_year + 1900
|
||||
};
|
||||
}
|
||||
|
||||
struct Point_in_time
|
||||
{
|
||||
Genode::int64_t rtc_seconds;
|
||||
Genode::int64_t curr_seconds;
|
||||
};
|
||||
|
||||
Rtc::Timestamp generate(Point_in_time const &p, Genode::uint64_t secs)
|
||||
{
|
||||
Genode::int64_t const s = ((Genode::int64_t)secs - p.curr_seconds) + p.rtc_seconds;
|
||||
return convert(s);
|
||||
}
|
||||
|
||||
} /* namespace Util */
|
||||
|
||||
|
||||
namespace Rtc {
|
||||
using namespace Genode;
|
||||
|
||||
struct Time;
|
||||
struct Session_component;
|
||||
struct Root;
|
||||
struct Main;
|
||||
} /* namespace Rtc */
|
||||
|
||||
|
||||
struct Rtc::Time
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Signal_context_capability _notify_sigh;
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
Rtc::Connection _rtc { _env };
|
||||
|
||||
Util::Point_in_time _time_base { };
|
||||
|
||||
void _update_time(Timestamp const &ts)
|
||||
{
|
||||
_time_base.rtc_seconds = Util::convert(ts);
|
||||
_time_base.curr_seconds = _timer.curr_time().trunc_to_plain_ms().value / 1000;
|
||||
if (_notify_sigh.valid()) {
|
||||
Signal_transmitter(_notify_sigh).submit();
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_rtc_set()
|
||||
{
|
||||
Timestamp ts = _rtc.current_time();
|
||||
log("Set RTC base from RTC driver to ", ts);
|
||||
_update_time(ts);
|
||||
}
|
||||
|
||||
Signal_handler<Time> _rtc_set_sigh {
|
||||
_env.ep(), *this, &Time::_handle_rtc_set };
|
||||
|
||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||
bool const _set_rtc {
|
||||
_config_rom.xml().attribute_value("allow_setting_rtc", false) };
|
||||
|
||||
Constructible<Attached_rom_dataspace> _set_rtc_rom { };
|
||||
|
||||
Signal_handler<Time> _set_rtc_sigh {
|
||||
_env.ep(), *this, &Time::_handle_set_rtc_rom };
|
||||
|
||||
void _handle_set_rtc_rom()
|
||||
{
|
||||
_set_rtc_rom->update();
|
||||
|
||||
if (!_set_rtc_rom->valid()) { return; }
|
||||
|
||||
Genode::Xml_node node = _set_rtc_rom->xml();
|
||||
|
||||
bool const complete = node.has_attribute("year")
|
||||
&& node.has_attribute("month")
|
||||
&& node.has_attribute("day")
|
||||
&& node.has_attribute("hour")
|
||||
&& node.has_attribute("minute")
|
||||
&& node.has_attribute("second");
|
||||
|
||||
if (!complete) {
|
||||
Genode::warning("set_rtc: ignoring incomplete RTC update");
|
||||
return;
|
||||
}
|
||||
|
||||
Timestamp ts { };
|
||||
|
||||
ts.second = node.attribute_value("second", 0u);
|
||||
if (ts.second > 59) {
|
||||
Genode::error("set_rtc: second invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
ts.minute = node.attribute_value("minute", 0u);
|
||||
if (ts.minute > 59) {
|
||||
Genode::error("set_rtc: minute invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
ts.hour = node.attribute_value("hour", 0u);
|
||||
if (ts.hour > 23) {
|
||||
Genode::error("set_rtc: hour invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
ts.day = node.attribute_value("day", 1u);
|
||||
if (ts.day > 31 || ts.day == 0) {
|
||||
Genode::error("set_rtc: day invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
ts.month = node.attribute_value("month", 1u);
|
||||
if (ts.month > 12 || ts.month == 0) {
|
||||
Genode::error("set_rtc: month invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
ts.year = node.attribute_value("year", 2019u);
|
||||
|
||||
ts.microsecond = 0;
|
||||
|
||||
log("Set RTC base from 'set_rtc' ROM to ", ts);
|
||||
_update_time(ts);
|
||||
}
|
||||
|
||||
Time(Env &env, Signal_context_capability notify_sigh)
|
||||
: _env { env }, _notify_sigh { notify_sigh }
|
||||
{
|
||||
_rtc.set_sigh(_rtc_set_sigh);
|
||||
_handle_rtc_set();
|
||||
|
||||
if (_set_rtc) {
|
||||
_set_rtc_rom.construct(env, "set_rtc");
|
||||
_set_rtc_rom->sigh(_set_rtc_sigh);
|
||||
}
|
||||
}
|
||||
|
||||
Timestamp current_time()
|
||||
{
|
||||
Milliseconds curr_ms = _timer.curr_time().trunc_to_plain_ms();
|
||||
return Util::generate(_time_base, curr_ms.value / 1000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Rtc::Session_component : public Genode::Rpc_object<Session>
|
||||
{
|
||||
Rtc::Time &_time;
|
||||
|
||||
Signal_context_capability _set_sig_cap { };
|
||||
|
||||
Session_component(Rtc::Time &time) : _time { time } { }
|
||||
|
||||
virtual ~Session_component() { }
|
||||
|
||||
void set_sigh(Signal_context_capability sigh) override
|
||||
{
|
||||
_set_sig_cap = sigh;
|
||||
}
|
||||
|
||||
Timestamp current_time() override
|
||||
{
|
||||
return _time.current_time();
|
||||
}
|
||||
|
||||
void notify_client()
|
||||
{
|
||||
if (_set_sig_cap.valid()) {
|
||||
Signal_transmitter(_set_sig_cap).submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Rtc::Root : public Genode::Root_component<Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Signal_handler<Rtc::Root> _set_sigh;
|
||||
|
||||
void _handle_set_signal()
|
||||
{
|
||||
_sessions.for_each([&] (Session_component &session) {
|
||||
session.notify_client();
|
||||
});
|
||||
}
|
||||
|
||||
Time _time;
|
||||
|
||||
Registry<Registered<Session_component> > _sessions { };
|
||||
|
||||
protected:
|
||||
|
||||
Session_component *_create_session(const char *) override
|
||||
{
|
||||
return new (md_alloc())
|
||||
Registered<Session_component>(_sessions, _time);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root(Env &env, Allocator &md_alloc)
|
||||
:
|
||||
Genode::Root_component<Session_component> { &env.ep().rpc_ep(), &md_alloc },
|
||||
_set_sigh { env.ep(), *this, &Rtc::Root::_handle_set_signal },
|
||||
_time { env, _set_sigh }
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
struct Rtc::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
||||
|
||||
Root _root { _env, _sliced_heap };
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
env.parent().announce(env.ep().manage(_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
static Rtc::Main main(env);
|
||||
}
|
10
repos/libports/src/server/system_rtc/target.mk
Normal file
10
repos/libports/src/server/system_rtc/target.mk
Normal file
@ -0,0 +1,10 @@
|
||||
TARGET := system_rtc
|
||||
SRC_CC := main.cc
|
||||
LIBS := base
|
||||
|
||||
# musl-libc contrib sources
|
||||
MUSL_TM = $(REP_DIR)/src/lib/musl_tm
|
||||
SRC_C = secs_to_tm.c tm_to_secs.c
|
||||
INC_DIR += $(MUSL_TM)
|
||||
|
||||
vpath %.c $(MUSL_TM)
|
167
repos/libports/src/test/system_rtc/main.cc
Normal file
167
repos/libports/src/test/system_rtc/main.cc
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* \brief Test for system RTC server
|
||||
* \author Josef Soentgen
|
||||
* \date 2019-07-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/env.h>
|
||||
#include <os/reporter.h>
|
||||
#include <rtc_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
Rtc::Connection _rtc { _env };
|
||||
|
||||
void _set_rtc(Reporter &reporter, Rtc::Timestamp const &ts)
|
||||
{
|
||||
Reporter::Xml_generator xml(reporter, [&] () {
|
||||
xml.attribute("year", ts.year);
|
||||
xml.attribute("month", ts.month);
|
||||
xml.attribute("day", ts.day);
|
||||
xml.attribute("hour", ts.hour);
|
||||
xml.attribute("minute", ts.minute);
|
||||
xml.attribute("second", ts.second);
|
||||
});
|
||||
}
|
||||
|
||||
Signal_handler<Main> _set_sigh {
|
||||
_env.ep(), *this, &Main::_handle_set_signal };
|
||||
|
||||
Rtc::Timestamp _ts { };
|
||||
|
||||
bool _sys_rtc_set { false };
|
||||
bool _drv_rtc_set { false };
|
||||
|
||||
static bool _equal(Rtc::Timestamp const &ts1,
|
||||
Rtc::Timestamp const &ts2)
|
||||
{
|
||||
return ts1.year == ts2.year
|
||||
&& ts1.month == ts2.month
|
||||
&& ts1.day == ts2.day
|
||||
&& ts1.hour == ts2.hour
|
||||
&& ts1.minute == ts2.minute;
|
||||
}
|
||||
|
||||
bool _check_rtc(Rtc::Timestamp const &ts1,
|
||||
Rtc::Timestamp const &ts2, bool system)
|
||||
{
|
||||
Genode::log("Set ", system ? "system" : "driver",
|
||||
" RTC to: '", ts1, "' got: '", ts2, "' (ignoring seconds)");
|
||||
|
||||
if (!_equal(ts1, ts2)) {
|
||||
error("updating ", system ? "system" : "driver", " RTC failed");
|
||||
_parent_exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _handle_set_signal()
|
||||
{
|
||||
Rtc::Timestamp got = _rtc.current_time();
|
||||
|
||||
if (_sys_rtc_set) {
|
||||
_sys_rtc_set = false;
|
||||
|
||||
if (!_check_rtc(_ts, got, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ts.year = 2018;
|
||||
_ts.month = 2;
|
||||
_ts.day = 17;
|
||||
_ts.hour = 10;
|
||||
_ts.minute = 15;
|
||||
_ts.second = 3;
|
||||
|
||||
log("Set driver RTC value: ", _ts);
|
||||
|
||||
_drv_rtc_set = true;
|
||||
_set_rtc(*_drv_reporter, _ts);
|
||||
|
||||
_timer.msleep(5000);
|
||||
} else if (_drv_rtc_set) {
|
||||
_drv_rtc_set = false;
|
||||
|
||||
if (!_check_rtc(_ts, got, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ts.year = 2019;
|
||||
_ts.month = 12;
|
||||
_ts.day = 17;
|
||||
_ts.hour = 11;
|
||||
_ts.minute = 15;
|
||||
_ts.second = 22;
|
||||
|
||||
log("Set system RTC value: ", _ts);
|
||||
|
||||
_set_rtc(*_sys_reporter, _ts);
|
||||
_timer.msleep(3500);
|
||||
} else {
|
||||
log("RTC value: ", _ts);
|
||||
|
||||
_parent_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Constructible<Reporter> _sys_reporter { };
|
||||
Constructible<Reporter> _drv_reporter { };
|
||||
|
||||
void _parent_exit(int exit_code)
|
||||
{
|
||||
Genode::log("--- system RTC test finished ---");
|
||||
_env.parent().exit(exit_code);
|
||||
}
|
||||
|
||||
Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
_sys_reporter.construct(_env, "sys_set_rtc");
|
||||
_sys_reporter->enabled(true);
|
||||
|
||||
_drv_reporter.construct(_env, "drv_set_rtc");
|
||||
_drv_reporter->enabled(true);
|
||||
|
||||
Genode::log("--- system RTC test started ---");
|
||||
|
||||
_rtc.set_sigh(_set_sigh);
|
||||
|
||||
_ts = _rtc.current_time();
|
||||
log("Initial RTC value: ", _ts);
|
||||
|
||||
_ts.year = 2020;
|
||||
_ts.month = 7;
|
||||
_ts.day = 16;
|
||||
_ts.hour = 12;
|
||||
_ts.minute = 23;
|
||||
_ts.second = 1;
|
||||
|
||||
log("Set system RTC value: ", _ts);
|
||||
|
||||
_sys_rtc_set = true;
|
||||
_set_rtc(*_sys_reporter, _ts);
|
||||
|
||||
_timer.msleep(5000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Main main(env); }
|
3
repos/libports/src/test/system_rtc/target.mk
Normal file
3
repos/libports/src/test/system_rtc/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-system_rtc
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
Loading…
Reference in New Issue
Block a user