use crate::{
Channel, OsdpCommand, OsdpError, OsdpEvent, OsdpFileOps, PdCapability, PdInfo, PdInfoBuilder,
};
use alloc::{boxed::Box, vec::Vec};
use core::ffi::c_void;
#[cfg(feature = "defmt-03")]
use defmt::{debug, error, info, warn};
#[cfg(all(feature = "log", not(feature = "defmt-03")))]
use log::{debug, error, info, warn};
type Result<T> = core::result::Result<T, OsdpError>;
type CommandCallback =
unsafe extern "C" fn(data: *mut c_void, event: *mut libosdp_sys::osdp_cmd) -> i32;
unsafe extern "C" fn log_handler(
_log_level: ::core::ffi::c_int,
_file: *const ::core::ffi::c_char,
_line: ::core::ffi::c_ulong,
_msg: *const ::core::ffi::c_char,
) {
#[cfg(any(feature = "log", feature = "defmt-03"))]
{
let msg = crate::cstr_to_string(_msg);
let msg = msg.trim();
match _log_level as libosdp_sys::osdp_log_level_e {
libosdp_sys::osdp_log_level_e_OSDP_LOG_EMERG => error!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_ALERT => error!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_CRIT => error!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_ERROR => error!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_WARNING => warn!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_NOTICE => warn!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_INFO => info!("PD: {}", msg),
libosdp_sys::osdp_log_level_e_OSDP_LOG_DEBUG => debug!("PD: {}", msg),
_ => panic!("Unknown log level"),
};
}
}
extern "C" fn trampoline<F>(data_ptr: *mut c_void, cmd_ptr: *mut libosdp_sys::osdp_cmd) -> i32
where
F: FnMut(&mut OsdpCommand) -> i32,
{
let mut cmd: OsdpCommand = unsafe { cmd_ptr.read().into() };
let callback: &mut F = unsafe { &mut *(data_ptr as *mut F) };
let res = callback(&mut cmd);
unsafe {
cmd_ptr.write(cmd.into());
}
res
}
fn get_trampoline<F>(_closure: &F) -> CommandCallback
where
F: FnMut(&mut OsdpCommand) -> i32,
{
trampoline::<F>
}
fn pd_setup(info: PdInfo) -> Result<*mut c_void> {
let info: crate::OsdpPdInfoHandle = info.into();
let ctx = unsafe { libosdp_sys::osdp_pd_setup(&*info) };
if ctx.is_null() {
Err(OsdpError::Setup)
} else {
Ok(ctx)
}
}
#[derive(Debug)]
pub struct PeripheralDevice {
ctx: *mut libosdp_sys::osdp_t,
}
unsafe impl Send for PeripheralDevice {}
impl PeripheralDevice {
pub fn new(info: PdInfoBuilder, channel: Box<dyn Channel>) -> Result<Self> {
unsafe { libosdp_sys::osdp_set_log_callback(Some(log_handler)) };
let info = info.channel(channel.into()).build();
Ok(Self {
ctx: pd_setup(info)?,
})
}
pub fn refresh(&mut self) {
unsafe { libosdp_sys::osdp_pd_refresh(self.ctx) }
}
pub fn set_capabilities(&mut self, cap: &[PdCapability]) {
let cap: Vec<libosdp_sys::osdp_pd_cap> = cap
.iter()
.map(|c| -> libosdp_sys::osdp_pd_cap { c.clone().into() })
.collect();
unsafe { libosdp_sys::osdp_pd_set_capabilities(self.ctx, cap.as_ptr()) }
}
pub fn flush_events(&mut self) {
let _ = unsafe { libosdp_sys::osdp_pd_flush_events(self.ctx) };
}
pub fn notify_event(&mut self, event: OsdpEvent) -> Result<()> {
let rc = unsafe { libosdp_sys::osdp_pd_notify_event(self.ctx, &event.into()) };
if rc < 0 {
Err(OsdpError::Event)
} else {
Ok(())
}
}
pub fn set_command_callback<F>(&mut self, closure: F)
where
F: FnMut(&mut OsdpCommand) -> i32,
{
unsafe {
let callback = get_trampoline(&closure);
libosdp_sys::osdp_pd_set_command_callback(
self.ctx,
Some(callback),
Box::into_raw(Box::new(closure)).cast(),
)
}
}
pub fn is_online(&self) -> bool {
let mut buf: u8 = 0;
unsafe { libosdp_sys::osdp_get_status_mask(self.ctx, &mut buf as *mut u8) };
buf != 0
}
pub fn is_sc_active(&self) -> bool {
let mut buf: u8 = 0;
unsafe { libosdp_sys::osdp_get_sc_status_mask(self.ctx, &mut buf as *mut u8) };
buf != 0
}
pub fn file_transfer_status(&self) -> Result<(i32, i32)> {
let mut size: i32 = 0;
let mut offset: i32 = 0;
let rc = unsafe {
libosdp_sys::osdp_get_file_tx_status(
self.ctx,
0,
&mut size as *mut i32,
&mut offset as *mut i32,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("Not not in progress"))
} else {
Ok((size, offset))
}
}
pub fn register_file_ops(&mut self, fops: Box<dyn OsdpFileOps>) -> Result<()> {
let mut fops: libosdp_sys::osdp_file_ops = fops.into();
let rc = unsafe {
libosdp_sys::osdp_file_register_ops(
self.ctx,
0,
&mut fops as *mut libosdp_sys::osdp_file_ops,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("ops register"))
} else {
Ok(())
}
}
}
impl Drop for PeripheralDevice {
fn drop(&mut self) {
unsafe { libosdp_sys::osdp_pd_teardown(self.ctx) }
}
}