Skip to content

Commit

Permalink
libosdp: Remove std entrengement in file transfer module
Browse files Browse the repository at this point in the history
Signed-off-by: Siddharth Chandrasekaran <[email protected]>
  • Loading branch information
sidcha committed Mar 15, 2024
1 parent 0e7870b commit 194f73e
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 174 deletions.
47 changes: 42 additions & 5 deletions libosdp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
//! (PD) on the OSDP bus. It can send commands to and receive events from PDs.

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

Expand Down Expand Up @@ -174,13 +175,49 @@ impl ControlPanel {
let idx = pd % 8;
buf[pos as usize] & (1 << idx) != 0
}

/// Get status of the ongoing file transfer of a PD, identified by the
/// offset number (in PdInfo vector in [`ControlPanel::new`]). Returns
/// (size, offset) of the current file transfer operation.
pub fn file_transfer_status(&self, pd: i32) -> 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,
pd,
&mut size as *mut i32,
&mut offset as *mut i32,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("Not not in progress"))
} else {
Ok((size, offset))
}
}

/// Register a file operations handler for a PD. See [`crate::OsdpFileOps`]
/// trait documentation for more details.
pub fn register_file_ops(&mut self, pd: i32, 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,
pd,
&mut fops as *mut libosdp_sys::osdp_file_ops,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("ops register"))
} else {
Ok(())
}
}
}

impl Drop for ControlPanel {
fn drop(&mut self) {
unsafe { libosdp_sys::osdp_cp_teardown(self.ctx) }
}
}

#[cfg(feature = "std")]
impl_osdp_file_ops_for!(ControlPanel);
223 changes: 66 additions & 157 deletions libosdp/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,192 +6,101 @@
//! OSDP provides a means to send files from CP to a Peripheral Device (PD).
//! This module adds the required components to achieve this effect.

use crate::OsdpError;
use std::{ffi::c_void, fs::File, path::PathBuf};
use core::ffi::c_void;

#[cfg(not(target_os = "windows"))]
use std::os::unix::prelude::FileExt;
#[cfg(target_os = "windows")]
use std::os::windows::fs::FileExt;
type Result<T> = core::result::Result<T, crate::OsdpError>;

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

trait OffsetRead {
fn pread(&self, buf: &mut [u8], offset: u64) -> std::io::Result<usize>;
fn pwrite(&self, buf: &[u8], offset: u64) -> std::io::Result<usize>;
}

impl OffsetRead for std::fs::File {
#[inline(always)]
fn pread(&self, buf: &mut [u8], offset: u64) -> std::io::Result<usize> {
#[cfg(not(target_os = "windows"))]
return self.read_at(buf, offset);

#[cfg(target_os = "windows")]
return self.seek_read(buf, offset);
}

#[inline(always)]
fn pwrite(&self, buf: &[u8], offset: u64) -> std::io::Result<usize> {
#[cfg(not(target_os = "windows"))]
return self.write_at(buf, offset);

#[cfg(target_os = "windows")]
return self.seek_write(buf, offset);
}
}

/// OSDP file transfer context
#[derive(Debug)]
pub struct OsdpFile {
id: i32,
path: PathBuf,
file: Option<File>,
size: usize,
/// File operations handler trait. Any type that implements this trait can be
/// registered with [`crate::ControlPanel::register_file_ops`] or
/// [`crate::PeripheralDevice::register_file_ops`].
pub trait OsdpFileOps {
/// Open a file, with pre-agreed File-ID [`id`]; returns the size of the
/// file that was opened or [`crate::OsdpError::FileTransfer`].
fn open(&mut self, id: i32, read_only: bool) -> Result<usize>;
/// Read bytes into buffer [`buf`] from offset [`off`] of the file; returns
/// number of bytes read or [`crate::OsdpError::FileTransfer`].
fn offset_read(&self, buf: &mut [u8], off: u64) -> Result<usize>;
/// Write bytes from buffer [`buf`] at offset [`off`] of the file; returns
/// number of bytes written or [`crate::OsdpError::FileTransfer`].
fn offset_write(&self, buf: &[u8], off: u64) -> Result<usize>;
/// Close the currently open file; returns [`crate::OsdpError::FileTransfer`]
/// if close failed.
fn close(&mut self) -> Result<()>;
}

unsafe extern "C" fn raw_file_open(data: *mut c_void, file_id: i32, size: *mut i32) -> i32 {
let ctx = &mut *(data as *mut OsdpFile);
if ctx.file.is_some() || file_id != ctx.id {
return -1;
}
let file = match File::open(ctx.path.as_os_str()) {
Ok(f) => f,
Err(_) => return -1,
};
ctx.size = file.metadata().unwrap().len() as usize;
ctx.file = Some(file);
unsafe {
*size = ctx.size as i32;
unsafe extern "C" fn file_open(data: *mut c_void, file_id: i32, size: *mut i32) -> i32 {
let ctx: *mut Box<dyn OsdpFileOps> = data as *mut _;
let ctx = ctx.as_mut().unwrap();
let read_only = *size == 0;
match ctx.open(file_id, read_only) {
Ok(file_size) => {
if read_only {
*size = file_size as i32;
}
0
}
Err(e) => {
log::error!("open: {:?}", e);
-1
}
}
0
}

unsafe extern "C" fn raw_file_read(
data: *mut c_void,
buf: *mut c_void,
size: i32,
offset: i32,
) -> i32 {
let ctx = &mut *(data as *mut OsdpFile);
if ctx.file.is_none() {
return -1;
}
let file = ctx.file.as_ref().unwrap();
unsafe extern "C" fn file_read(data: *mut c_void, buf: *mut c_void, size: i32, offset: i32) -> i32 {
let ctx: *mut Box<dyn OsdpFileOps> = data as *mut _;
let ctx = ctx.as_ref().unwrap();
let mut read_buf = vec![0u8; size as usize];
let len = match file.pread(&mut read_buf, offset as u64) {
let len = match ctx.offset_read(&mut read_buf, offset as u64) {
Ok(len) => len as i32,
Err(_) => -1,
Err(e) => {
log::error!("file_read: {:?}", e);
-1
}
};
std::ptr::copy_nonoverlapping(read_buf.as_mut_ptr(), buf as *mut u8, len as usize);
len
}

unsafe extern "C" fn raw_file_write(
unsafe extern "C" fn file_write(
data: *mut c_void,
buf: *const c_void,
size: i32,
offset: i32,
) -> i32 {
let ctx = &mut *(data as *mut OsdpFile);
if ctx.file.is_none() {
return -1;
}
let ctx: *mut Box<dyn OsdpFileOps> = data as *mut _;
let ctx = ctx.as_ref().unwrap();
let mut write_buf = vec![0u8; size as usize];
std::ptr::copy_nonoverlapping(buf as *mut u8, write_buf.as_mut_ptr(), size as usize);
let file = ctx.file.as_ref().unwrap();
match file.pwrite(&write_buf, offset as u64) {
match ctx.offset_write(&write_buf, offset as u64) {
Ok(len) => len as i32,
Err(_) => -1,
}
}

unsafe extern "C" fn raw_file_close(data: *mut c_void) -> i32 {
let ctx = &mut *(data as *mut OsdpFile);
if ctx.file.is_none() {
return -1;
Err(e) => {
log::error!("file_write: {:?}", e);
-1
}
}
let _ = ctx.file.take().unwrap();
0
}

impl OsdpFile {
/// Create a new file transfer context for a given ID and path.
///
/// # Arguments
///
/// * `id` - An ID to associate to file. This ID must be pre-shared between
/// CP and PD.
/// * `path` - Path to file to read from (CP) or write to (PD).
pub fn new(id: i32, path: PathBuf) -> Self {
Self {
id,
path,
file: None,
size: 0,
unsafe extern "C" fn file_close(data: *mut c_void) -> i32 {
let ctx: *mut Box<dyn OsdpFileOps> = data as *mut _;
let ctx = ctx.as_mut().unwrap();
match ctx.close() {
Ok(_) => 0,
Err(e) => {
log::error!("file_close: {:?}", e);
-1
}
}
}

/// For internal use by {cp,pd}.register_file() methods.
pub fn get_ops_struct(&mut self) -> libosdp_sys::osdp_file_ops {
impl From<Box<dyn OsdpFileOps>> for libosdp_sys::osdp_file_ops {
fn from(value: Box<dyn OsdpFileOps>) -> Self {
let data = Box::into_raw(Box::new(value));
libosdp_sys::osdp_file_ops {
arg: self as *mut _ as *mut c_void,
open: Some(raw_file_open),
read: Some(raw_file_read),
write: Some(raw_file_write),
close: Some(raw_file_close),
arg: data as *mut _ as *mut c_void,
open: Some(file_open),
read: Some(file_read),
write: Some(file_write),
close: Some(file_close),
}
}
}

/// A OSDP File transfer Ops trait.
pub trait OsdpFileOps {
/// Method used to register a file transfer operation. The `pd` must be
/// set to zero for PeripheralDevice
///
/// TODO: Remove the `pd` arg for PD mode.
fn register_file(&mut self, pd: i32, fm: &mut OsdpFile) -> Result<()>;

/// Method used check the status of an ongoing transfer. The `pd` must be
/// set to zero for PeripheralDevice
///
/// TODO: Remove the `pd` arg for PD mode.
fn get_file_transfer_status(&self, pd: i32) -> Result<(i32, i32)>;
}

macro_rules! impl_osdp_file_ops_for {
($($t:ty),+ $(,)?) => ($(
impl OsdpFileOps for $t {
fn register_file(&mut self, pd: i32, fm: &mut OsdpFile) -> Result<()> {
let mut ops = fm.get_ops_struct();
let rc = unsafe {
libosdp_sys::osdp_file_register_ops(self.ctx, pd, &mut ops as *mut libosdp_sys::osdp_file_ops)
};
if rc < 0 {
Err(OsdpError::FileTransfer("ops register"))
} else {
Ok(())
}
}

fn get_file_transfer_status(&self, pd: i32) -> 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,
pd,
&mut size as *mut i32,
&mut offset as *mut i32,
)
};
if rc < 0 {
Err(OsdpError::FileTransfer("transfer status query"))
} else {
Ok((size, offset))
}
}
}
)+)
}
pub(crate) use impl_osdp_file_ops_for;
4 changes: 2 additions & 2 deletions libosdp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ mod channel;
mod commands;
mod cp;
mod events;
pub mod file;
#[cfg(feature = "std")]
mod file;
mod pd;
mod pdcap;
mod pdid;
Expand All @@ -78,6 +77,7 @@ mod pdinfo;
pub use channel::*;
pub use commands::*;
pub use events::*;
pub use file::*;
pub use pdcap::*;
pub use pdid::*;
pub use pdinfo::*;
Expand Down
Loading

0 comments on commit 194f73e

Please sign in to comment.