Commit fa863cc1 authored by Philippe Gerum's avatar Philippe Gerum
Browse files

initial commit



Signed-off-by: Philippe Gerum's avatarPhilippe Gerum <rpm@xenomai.org>
parents
/target
/Cargo.lock
*~
#*#
[package]
name = "revl"
version = "0.1.0"
edition = "2021"
[dependencies]
libc = "~0.2"
embedded-time = "~0.12"
evl-sys = { version = "^0.20.1", git = "https://source.denx.de/Xenomai/xenomai4/evl-sys" }
MIT License
Copyright (c) 2018-2021 Philippe Gerum and the libevl contributors.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//! Clock interface.
use libc::c_int;
use std::io;
use embedded_time::{
clock,
duration::{Nanoseconds, Seconds},
rate::*,
Clock, Instant,
};
use evl_sys::{
evl_read_clock,
evl_sleep_until,
timespec,
BuiltinClock
};
#[derive(Debug)]
pub struct MonoClock;
impl Clock for MonoClock {
type T = u64;
const SCALING_FACTOR: Fraction = Fraction::new(1, 1_000_000_000); // ns
fn try_now(&self) -> Result<Instant<Self>, clock::Error> {
let mut now = timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe { evl_read_clock(BuiltinClock::MONOTONIC as c_int, &mut now) };
let now_ns: u64 = now.tv_sec as u64 * 1_000_000_000 + now.tv_nsec as u64;
Ok(Instant::new(now_ns))
}
}
impl MonoClock {
pub fn sleep_until(&self, timeout: Instant<MonoClock>) -> Result<(), io::Error> {
let dur = timeout.duration_since_epoch();
let secs: Seconds<u64> = Seconds::try_from(dur).unwrap();
let nsecs: Nanoseconds<u64> = Nanoseconds::<u64>::try_from(dur).unwrap() % secs;
let date = timespec {
tv_sec: secs.integer() as i64,
tv_nsec: nsecs.integer() as i64,
};
let ret: c_int = unsafe { evl_sleep_until(BuiltinClock::MONOTONIC as c_int, &date) };
match ret {
0 => return Ok(()),
_ => return Err(io::Error::from_raw_os_error(-ret)),
};
}
pub fn now(&self) -> Instant<Self> {
self.try_now().unwrap()
}
}
//! Rust interface to the EVL real-time core.
//!
//! Provides an API to call the services of the Xenomai4 [real-time
//! core](https://evlproject.org/), aka EVL.
pub mod clock;
pub mod mutex;
pub mod sched;
pub mod thread;
pub mod semaphore;
//! Real-time mutex.
//!
//! The implementation borrows from
//! [freertos.rs](https://github.com/hashmismatch/freertos.rs),
//! adapted to the libevl call interface.
use std::ffi::CString;
use std::cell::UnsafeCell;
use std::io::Error;
use std::mem::{forget, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::fmt;
use std::ptr;
use evl_sys::{
evl_close_mutex,
evl_create_mutex,
evl_lock_mutex,
evl_mutex,
evl_unlock_mutex,
BuiltinClock,
CloneFlags,
MutexType,
};
pub struct Builder {
name: Option<String>,
visible: bool,
recursive: bool,
ceiling: u32,
}
impl Builder {
pub fn new() -> Self {
Self {
name: None,
visible: false,
recursive: false,
ceiling: 0,
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn public(mut self) -> Self {
self.visible = true;
self
}
pub fn private(mut self) -> Self {
self.visible = false;
self
}
pub fn recursive(mut self) -> Self {
self.recursive = true;
self
}
pub fn ceiling(mut self, ceiling: u32) -> Self {
self.ceiling = ceiling;
self
}
pub fn create<T>(self, data: T) -> Result<Mutex<T>, Error> {
Mutex::new(data, self)
}
}
pub struct Mutex<T: ?Sized> {
mutex: CoreMutex,
data: UnsafeCell<T>,
}
unsafe impl<T: Sync + Send> Send for Mutex<T> {}
unsafe impl<T: Sync + Send> Sync for Mutex<T> {}
impl<T> Mutex<T> {
pub fn new(data: T, builder: Builder) -> Result<Self, Error> {
Ok(Self {
mutex: CoreMutex::new(builder)?,
data: UnsafeCell::new(data),
})
}
pub fn lock(&self) -> Result<MutexGuard<T>, Error> {
self.mutex.lock()?;
Ok(MutexGuard {
__mutex: &self.mutex,
__data: &self.data,
})
}
pub fn into_inner(self) -> T {
unsafe {
let (mutex, data) = {
let Self {
ref mutex,
ref data,
} = self;
(ptr::read(mutex), ptr::read(data))
};
forget(self);
drop(mutex);
data.into_inner()
}
}
}
impl<T: ?Sized> fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Mutex address: {:?}", self.mutex)
}
}
pub struct MutexGuard<'a, T: ?Sized + 'a> {
__mutex: &'a CoreMutex,
__data: &'a UnsafeCell<T>,
}
impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> {
type Target = T;
fn deref<'a>(&'a self) -> &'a T {
unsafe { &*self.__data.get() }
}
}
impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
unsafe { &mut *self.__data.get() }
}
}
impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.__mutex.unlock();
}
}
pub struct CoreMutex(UnsafeCell<evl_mutex>);
impl Drop for CoreMutex {
fn drop(&mut self) {
unsafe {
evl_close_mutex(self.0.get());
}
}
}
impl fmt::Debug for CoreMutex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", &self.0 as *const _)
}
}
impl CoreMutex {
pub fn new(builder: Builder) -> Result<Self, Error> {
let this = Self(UnsafeCell::new(unsafe {
MaybeUninit::<evl_mutex>::zeroed().assume_init()
}));
let mut c_flags = CloneFlags::PRIVATE.bits() as c_int;
if builder.visible {
c_flags = CloneFlags::PUBLIC.bits() as c_int;
}
if builder.recursive {
c_flags |= MutexType::RECURSIVE.bits() as c_int;
}
let c_ceiling = builder.ceiling;
// Revisit: clock should be configurable.
let c_clockfd = BuiltinClock::MONOTONIC as i32;
let ret: c_int = unsafe {
if let Some(name) = builder.name {
let c_name = CString::new(name).expect("CString::new failed");
let c_fmt = CString::new("%s").expect("CString::new failed");
evl_create_mutex(
this.0.get(),
c_clockfd,
c_ceiling,
c_flags,
c_fmt.as_ptr(),
c_name.as_ptr())
} else {
evl_create_mutex(
this.0.get(),
c_clockfd,
c_ceiling,
c_flags,
ptr::null())
}
};
match ret {
0.. => return Ok(this),
_ => return Err(Error::from_raw_os_error(-ret)),
};
}
pub fn lock(&self) -> Result<(), Error> {
let ret: c_int = unsafe { evl_lock_mutex(self.0.get()) };
match ret {
0 => return Ok(()),
_ => return Err(Error::from_raw_os_error(-ret)),
};
}
pub fn unlock(&self) {
unsafe {
evl_unlock_mutex(self.0.get());
};
}
}
use core::mem::MaybeUninit;
use evl_sys::{
evl_sched_attrs,
SchedPolicy
};
// Other mods may need visibility on evl_sched_attrs (e.g. thread)
pub struct SchedAttrs(pub(crate) evl_sched_attrs);
pub struct SchedFifo {
prio: i32,
}
pub struct SchedRR {
prio: i32,
}
pub struct SchedWeak {
prio: i32,
}
pub struct SchedQuota {
group: i32,
prio: i32,
}
pub struct SchedTP {
part: i32,
prio: i32,
}
pub trait PolicyParam {
fn to_attr(&self) -> SchedAttrs;
}
fn get_zero_attrs() -> SchedAttrs {
SchedAttrs(unsafe { MaybeUninit::<evl_sched_attrs>::zeroed().assume_init() })
}
impl PolicyParam for SchedFifo {
fn to_attr(&self) -> SchedAttrs {
let mut x = get_zero_attrs();
x.0.sched_policy = SchedPolicy::FIFO as i32;
x.0.sched_priority = self.prio;
x
}
}
impl PolicyParam for SchedRR {
fn to_attr(&self) -> SchedAttrs {
let mut x = get_zero_attrs();
x.0.sched_policy = SchedPolicy::RR as i32;
x.0.sched_priority = self.prio;
x
}
}
impl PolicyParam for SchedWeak {
fn to_attr(&self) -> SchedAttrs {
let mut x = get_zero_attrs();
x.0.sched_policy = SchedPolicy::WEAK as i32;
x.0.sched_priority = self.prio;
x
}
}
impl PolicyParam for SchedQuota {
fn to_attr(&self) -> SchedAttrs {
let mut x = get_zero_attrs();
x.0.sched_policy = SchedPolicy::QUOTA as i32;
x.0.sched_priority = self.prio;
x.0.sched_u.quota.__sched_group = self.group;
x
}
}
impl PolicyParam for SchedTP {
fn to_attr(&self) -> SchedAttrs {
let mut x = get_zero_attrs();
x.0.sched_policy = SchedPolicy::TP as i32;
x.0.sched_priority = self.prio;
x.0.sched_u.tp.__sched_partition = self.part;
x
}
}
//! Real-time counting semaphore.
use std::cell::UnsafeCell;
use std::ffi::CString;
use std::io::Error;
use std::mem::MaybeUninit;
use std::os::raw::c_int;
use std::ptr;
use evl_sys::{
evl_close_sem,
evl_create_sem,
evl_get_sem,
evl_put_sem,
evl_sem,
evl_tryget_sem,
BuiltinClock,
CloneFlags,
};
pub struct Builder {
name: Option<String>,
visible: bool,
initval: u32,
}
impl Builder {
pub fn new() -> Self {
Self {
name: None,
visible: false,
initval: 0u32,
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn public(mut self) -> Self {
self.visible = true;
self
}
pub fn private(mut self) -> Self {
self.visible = false;
self
}
pub fn init_value(mut self, initval: u32) -> Self {
self.initval = initval;
self
}
pub fn create(self) -> Result<Semaphore, Error> {
Semaphore::new(self)
}
}
pub struct Semaphore(UnsafeCell<evl_sem>);
unsafe impl Send for Semaphore {}
unsafe impl Sync for Semaphore {}
impl Semaphore {
/// Create an EVL semaphore.
///
/// # Arguments
///
/// * [`builder`]: a builder struct containing the properties of
/// the new semaphore.
///
/// # Errors
///
/// * Error(AlreadyExists) means the semaphore name is conflicting
/// with an existing semaphore name.
///
/// * Error(InvalidInput) means that the semaphore name contains
/// invalid characters: such name must contain only valid
/// characters in the context of a Linux file name.
///
/// * Error(Other) may mean that either the EVL core is not
/// enabled in the kernel, or there is an ABI mismatch between the
/// underlying [evl-sys
/// crate](https://source.denx.de/Xenomai/xenomai4/evl-sys) and
/// the EVL core. See [these
/// explanations](https://evlproject.org/core/under-the-hood/abi/)
/// for the latter.
///
/// # Examples
///
/// ```rust
/// use revl::semaphore::{Builder, Semaphore};
///
/// fn create_a_semaphore(initval: u32) -> Result<Semaphore, std::io::Error> {
/// let props = Builder::new().name("a_sema4").public().init_value(initval);
/// let me = Semaphore::new(props)?;
/// Ok(me)
/// }
/// ```
///
pub fn new(builder: Builder) -> Result<Self, Error> {
let this = Self(UnsafeCell::new(unsafe {
MaybeUninit::<evl_sem>::zeroed().assume_init()
}));
let mut c_flags = CloneFlags::PRIVATE.bits() as c_int;
if builder.visible {
c_flags = CloneFlags::PUBLIC.bits() as c_int;
}
let c_initval = builder.initval as i32;
// Revisit: this is too restrictive.
let c_clockfd = BuiltinClock::MONOTONIC as i32;
let ret: c_int = unsafe {
if let Some(name) = builder.name {
let c_name = CString::new(name).expect("CString::new failed");
let c_fmt = CString::new("%s").expect("CString::new failed");
evl_create_sem(
this.0.get(),
c_clockfd,
c_initval,
c_flags,
c_fmt.as_ptr(),
c_name.as_ptr(),
)
} else {
evl_create_sem(this.0.get(),
c_clockfd,
c_initval,
c_flags,
ptr::null())
}
};
match ret {
0.. => return Ok(this),
_ => return Err(Error::from_raw_os_error(-ret)),
};
}
pub fn get(&self) -> Result<(), Error> {
let ret: c_int = unsafe { evl_get_sem(self.0.get()) };
match ret {
0.. => return Ok(()),
_ => return Err(Error::from_raw_os_error(-ret)),
};
}
pub fn tryget(&self) -> bool {
let ret: c_int = unsafe { evl_tryget_sem(self.0.get()) };
match ret {
0 => return true,
_ => return false,
};
}
pub fn put(&self) -> Result<(), Error> {
let ret: c_int = unsafe { evl_put_sem(self.0.get()) };
match ret {
0.. => return Ok(()),
_ => return Err(Error::from_raw_os_error(-ret)),
};
}
}
impl Drop for Semaphore {
fn drop(&mut self) {
unsafe {
evl_close_sem(self.0.get());
}
}
}
//! Real-time thread.
//!
//! EVL threads are native threads originally, which are extended with
//! real-time capabilities once attached to the EVL core.
use std::ptr;
use std::os::raw::c_int;
use std::io::Error;
use std::ffi::CString;
use evl_sys::{
evl_attach_thread,
evl_unblock_thread,
evl_demote_thread,
evl_sched_attrs,
evl_set_schedattr,
CloneFlags,
};
use crate::sched::*;
/// A thread factory, which can be used in order to configure the
/// properties of a new EVL thread.
pub struct Builder {
name: Option<String>,
visible: bool,
observable: bool,
unicast: bool,
}
impl Builder {
/// Create a thread factory with default settings.
///
/// Methods can be chained on the builder in order to configure
/// it.
///
/// The available configurations are:
///
/// - [`name`]: specifies an associated name for the thread
/// - [`visible`]: specifies the visibility for the thread in the
/// [/dev/evl file hierarchy](https://evlproject.org/core/user-api/#evl-fs-hierarchy)
/// - [`observable`]: specifies whether the thread may be observed
/// for health monitoring purpose.
/// - [`unicast`]: if observable, specifies whether notifications
/// should be sent to a single observer instead of broadcast
/// to all of them.
///
///
pub fn new() -> Self {
Self {
name: None,
visible: false,
observable: false,
unicast: false,
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn public(mut self) -> Self {
self.visible = true;
self
}
pub fn private(mut self) -> Self {
self.visible = false;
self