Skip to content

Commit

Permalink
Merge pull request #148 from Sympatron/no_std
Browse files Browse the repository at this point in the history
rust: no_std support
  • Loading branch information
sidcha committed Dec 10, 2023
2 parents b8f9713 + 91be589 commit 0fa4334
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 101 deletions.
9 changes: 8 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ bindgen = "0.68.1"

[dependencies]
bitflags = "2.4.0"
embedded-io = "0.6.1"
env_logger = "0.10.0"
lazy_static = "1.4.0"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
log = "0.4.20"
multiqueue = "0.3.2"
once_cell = "1.18.0"
parking_lot = "0.12.1"
serde = { version = "1.0.192", features = ["derive"] }
serde_with = "3.4.0"
spin = "0.9.8"
thiserror = "1.0.50"

[features]
default = ["std"]
std = []
33 changes: 17 additions & 16 deletions rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ fn get_repo_root() -> std::io::Result<String> {
let has_git = std::fs::read_dir(p)?
.into_iter()
.any(|p| {
let ent = p.unwrap();
if ent.file_type().unwrap().is_dir() {
ent.file_name() == OsString::from(".git")
} else {
false
}
});
let ent = p.unwrap();
if ent.file_type().unwrap().is_dir() {
ent.file_name() == OsString::from(".git")
} else {
false
}
});
if has_git {
return Ok(PathBuf::from(p).into_os_string().into_string().unwrap())
}
Expand All @@ -32,8 +32,8 @@ fn get_repo_root() -> std::io::Result<String> {
while let Some(p) = path_ancestors.next() {
let has_cargo =
std::fs::read_dir(p)?
.into_iter()
.any(|p| p.unwrap().file_name() == OsString::from("Cargo.lock"));
.into_iter()
.any(|p| p.unwrap().file_name() == OsString::from("Cargo.lock"));
if has_cargo {
return Ok(PathBuf::from(p).into_os_string().into_string().unwrap())
}
Expand Down Expand Up @@ -107,13 +107,13 @@ fn generate_osdp_build_headers(out_dir: &str) -> Result<()> {
std::fs::copy(&src, &dest)
.context(format!("Failed: copy {src} -> {dest}"))?;
configure_file(&dest, vec![
("PROJECT_VERSION", env!("CARGO_PKG_VERSION")),
("PROJECT_VERSION", env!("CARGO_PKG_VERSION")),
("PROJECT_NAME", format!("{}-rust", env!("CARGO_PKG_NAME")).as_str()),
("GIT_BRANCH", git.branch.as_str()),
("GIT_REV", git.rev.as_ref()),
("GIT_TAG", git.tag.as_ref()),
("GIT_DIFF", git.diff.as_ref()),
("REPO_ROOT", git.root.as_ref()),
("GIT_BRANCH", git.branch.as_str()),
("GIT_REV", git.rev.as_ref()),
("GIT_TAG", git.tag.as_ref()),
("GIT_DIFF", git.diff.as_ref()),
("REPO_ROOT", git.root.as_ref()),
])
}

Expand Down Expand Up @@ -219,7 +219,8 @@ fn main() -> Result<()> {
build.compile("libosdp.a");

let bindings = bindgen::Builder::default()
.header( "vendor/include/osdp.h")
.use_core()
.header("vendor/include/osdp.h")
.generate()
.context("Unable to generate bindings")?;

Expand Down
131 changes: 108 additions & 23 deletions rust/src/channel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,47 @@
//! This module provides a way to define an OSDP channel and export it to
//! LibOSDP.

#[cfg(feature = "std")]
pub mod unix_channel;
#[cfg(feature = "std")]
pub use unix_channel::UnixChannel;

use crate::osdp_sys;
use lazy_static::lazy_static;
use std::{
collections::{hash_map::DefaultHasher, HashMap},
ffi::c_void,
hash::{Hash, Hasher},
io::{Read, Write},
sync::{Mutex, Arc},
};
#[cfg(not(feature = "std"))]
use alloc::collections::BTreeMap as HashMap;
use alloc::{boxed::Box, format, sync::Arc, vec};
use core::ffi::c_void;
#[cfg(feature = "std")]
use std::{collections::{hash_map::DefaultHasher, HashMap}, hash::{Hash, Hasher}};

#[cfg(feature = "std")]
use parking_lot::Mutex;
#[cfg(not(feature = "std"))]
use spin::Mutex;

lazy_static! {
static ref CHANNELS: Mutex<HashMap<i32, Arc<Mutex<Box<dyn Channel>>>>> = Mutex::new(HashMap::new());
static ref CHANNELS: Mutex<HashMap<i32, Arc<Mutex<Box<dyn Channel>>>>> =
Mutex::new(HashMap::new());
}

impl std::fmt::Debug for OsdpChannel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl alloc::fmt::Debug for OsdpChannel {
fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
f.debug_struct("OsdpChannel")
.field(
"stream",
&format!("{}", self.stream.lock().unwrap().get_id()))
.field("stream", &format!("{}", self.stream.lock().get_id()))
.finish()
}
}

unsafe extern "C" fn raw_read(data: *mut c_void, buf: *mut u8, len: i32) -> i32 {
let key = data as i32;
let mut channels = CHANNELS.lock().unwrap();
let mut stream = channels.get_mut(&key).unwrap().lock().unwrap();
let mut channels = CHANNELS.lock();
let mut stream = channels.get_mut(&key).unwrap().lock();
let mut read_buf = vec![0u8; len as usize];
match stream.read(&mut read_buf) {
Ok(n) => {
let src_ptr = read_buf.as_mut_ptr();
std::ptr::copy_nonoverlapping(src_ptr, buf, len as usize);
core::ptr::copy_nonoverlapping(src_ptr, buf, len as usize);
n as i32
}
Err(_) => -1,
Expand All @@ -55,10 +60,10 @@ unsafe extern "C" fn raw_read(data: *mut c_void, buf: *mut u8, len: i32) -> i32

unsafe extern "C" fn raw_write(data: *mut c_void, buf: *mut u8, len: i32) -> i32 {
let key = data as i32;
let mut channels = CHANNELS.lock().unwrap();
let mut stream = channels.get_mut(&key).unwrap().lock().unwrap();
let mut channels = CHANNELS.lock();
let mut stream = channels.get_mut(&key).unwrap().lock();
let mut write_buf = vec![0u8; len as usize];
std::ptr::copy_nonoverlapping(buf, write_buf.as_mut_ptr(), len as usize);
core::ptr::copy_nonoverlapping(buf, write_buf.as_mut_ptr(), len as usize);
match stream.write(&write_buf) {
Ok(n) => n as i32,
Err(_) => -1,
Expand All @@ -67,11 +72,90 @@ unsafe extern "C" fn raw_write(data: *mut c_void, buf: *mut u8, len: i32) -> i32

unsafe extern "C" fn raw_flush(data: *mut c_void) {
let key = data as i32;
let mut channels = CHANNELS.lock().unwrap();
let mut stream = channels.get_mut(&key).unwrap().lock().unwrap();
let mut channels = CHANNELS.lock();
let mut stream = channels.get_mut(&key).unwrap().lock();
let _ = stream.flush();
}

#[cfg(not(feature = "std"))]
/// The error type for I/O operations of the [`Read`] and [`Write`] associated traits.
pub type RWError = Box<dyn embedded_io::Error>;
#[cfg(feature = "std")]
/// The error type for I/O operations of the [`Read`] and [`Write`] associated traits.
pub type RWError = std::io::Error;

/// The `Read` trait allows for reading bytes from a source.
///
/// Wrapper around either [`std::io::Read`] or [`embedded_io::Read`] depending on the availability of `std`.
pub trait Read {
/// Pull some bytes from this source into the specified buffer, returning
/// how many bytes were read.
///
/// Wrapper around either [`std::io::Read::read`] or [`embedded_io::Read::read`] depending on the availability of `std`.
fn read(&mut self, buf: &mut [u8]) -> Result<usize, RWError>;
}
/// A trait for objects which are byte-oriented sinks.
///
/// Wrapper around either [`std::io::Write`] or [`embedded_io::Write`] depending on the availability of `std`.
pub trait Write {
/// Write a buffer into this writer, returning how many bytes were written.
///
/// Wrapper around either [`std::io::Write::write`] or [`embedded_io::Write::write`] depending on the availability of `std`.
fn write(&mut self, buf: &[u8]) -> Result<usize, RWError>;
/// Flush this output stream, ensuring that all intermediately buffered
/// contents reach their destination.
///
/// Wrapper around either [`std::io::Write::flush`] or [`embedded_io::Write::flush`] depending on the availability of `std`.
fn flush(&mut self) -> Result<(), RWError>;
}

#[cfg(feature = "std")]
impl<T: std::io::Read> Read for T {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, RWError> {
self.read(buf)
}
}
#[cfg(feature = "std")]
impl<T: std::io::Write> Write for T {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> Result<usize, RWError> {
self.write(buf)
}
#[inline(always)]
fn flush(&mut self) -> Result<(), RWError> {
self.flush()
}
}

#[cfg(not(feature = "std"))]
impl<T: embedded_io::Read> Read for T
where
T::Error: 'static,
{
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, RWError> {
self.read(buf)
.map_err(|e| Box::new(e) as Box<dyn embedded_io::Error>)
}
}
#[cfg(not(feature = "std"))]
impl<T: embedded_io::Write> Write for T
where
T::Error: 'static,
{
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> Result<usize, RWError> {
self.write(buf)
.map_err(|e| Box::new(e) as Box<dyn embedded_io::Error>)
}
#[inline(always)]
fn flush(&mut self) -> Result<(), RWError> {
self.flush()
.map_err(|e| Box::new(e) as Box<dyn embedded_io::Error>)
}
}

/// The Channel trait acts as an interface for all channel implementors. See
/// module description for the definition of a "channel" in LibOSDP.
pub trait Channel: Read + Write + Send + Sync {
Expand Down Expand Up @@ -100,8 +184,8 @@ impl OsdpChannel {
/// exports the channel to LibOSDP as a C struct.
pub fn as_struct(&self) -> osdp_sys::osdp_channel {
let stream = self.stream.clone();
let id = stream.lock().unwrap().get_id();
CHANNELS.lock().unwrap().insert(id, stream);
let id = stream.lock().get_id();
CHANNELS.lock().insert(id, stream);
osdp_sys::osdp_channel {
id,
data: id as *mut c_void,
Expand All @@ -112,6 +196,7 @@ impl OsdpChannel {
}
}

#[cfg(feature = "std")]
fn str_to_channel_id(key: &str) -> i32 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
Expand Down
3 changes: 2 additions & 1 deletion rust/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
//! such commands though [`OsdpCommand`].

use crate::{osdp_sys, ConvertEndian};
use serde::{Serialize, Deserialize};
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};

/// LED Colors as specified in OSDP for the on_color/off_color parameters.
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
Expand Down
29 changes: 13 additions & 16 deletions rust/src/cp.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
//! The CP is responsible to connecting to and managing multiple PDs. It is able
//! to send commands to and receive events from PDs.

#[cfg(feature = "std")]
use crate::file::{impl_osdp_file_ops_for, OsdpFile, OsdpFileOps};
use crate::{
commands::OsdpCommand,
events::OsdpEvent,
file::{OsdpFile, OsdpFileOps, impl_osdp_file_ops_for},
osdp_sys,
OsdpError,
commands::OsdpCommand, events::OsdpEvent, osdp_sys, OsdpError, OsdpFlag, PdCapability, PdId,
PdInfo,
OsdpFlag,
PdCapability,
PdId,
};
use alloc::vec::Vec;
use core::ffi::c_void;
use log::{debug, error, info, warn};
use std::ffi::c_void;

type Result<T> = std::result::Result<T, OsdpError>;
type Result<T> = core::result::Result<T, OsdpError>;
type EventCallback =
unsafe extern "C" fn(data: *mut c_void, pd: i32, event: *mut osdp_sys::osdp_event) -> i32;

unsafe extern "C" fn log_handler(
log_level: ::std::os::raw::c_int,
_file: *const ::std::os::raw::c_char,
_line: ::std::os::raw::c_ulong,
msg: *const ::std::os::raw::c_char,
log_level: ::core::ffi::c_int,
_file: *const ::core::ffi::c_char,
_line: ::core::ffi::c_ulong,
msg: *const ::core::ffi::c_char,
) {
let msg = crate::cstr_to_string(msg);
let msg = msg.trim();
Expand Down Expand Up @@ -74,7 +70,7 @@ fn cp_setup(info: Vec<osdp_sys::osdp_pd_info_t>) -> Result<*mut c_void> {
/// OSDP CP device context.
#[derive(Debug)]
pub struct ControlPanel {
ctx: *mut std::ffi::c_void,
ctx: *mut core::ffi::c_void,
}

impl ControlPanel {
Expand Down Expand Up @@ -149,7 +145,7 @@ impl ControlPanel {
/// vector in [`ControlPanel::new`]).
pub fn get_pd_id(&self, pd: i32) -> Result<PdId> {
let mut pd_id: osdp_sys::osdp_pd_id =
unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let rc = unsafe { osdp_sys::osdp_cp_get_pd_id(self.ctx, pd, &mut pd_id) };
if rc < 0 {
Err(OsdpError::Query("PdId"))
Expand Down Expand Up @@ -208,4 +204,5 @@ impl Drop for ControlPanel {
}
}

#[cfg(feature = "std")]
impl_osdp_file_ops_for!(ControlPanel);
5 changes: 3 additions & 2 deletions rust/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
//! module is responsible to handling such events though [`OsdpEvent`].

use crate::{osdp_sys, ConvertEndian, OsdpError};
use serde::{Serialize, Deserialize};
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};

type Result<T> = std::result::Result<T, OsdpError>;
type Result<T> = core::result::Result<T, OsdpError>;

/// Various card formats that a PD can support. This is sent to CP when a PD
/// must report a card read
Expand Down
Loading

0 comments on commit 0fa4334

Please sign in to comment.