use std::{fs::OpenOptions, io, num::NonZero, path::Path, ptr::NonNull, time::Duration};
use nix::sys::mman::{MapFlags, ProtFlags, mmap, munmap};
use xvc_server::XvcServer;
use crate::backends::memory_mapped::{MAP_SIZE, MemoryMappedBackend};
pub struct DevMemBackend(MemoryMappedBackend);
impl DevMemBackend {
pub fn new(address: i64, poll_timeout: Duration) -> io::Result<DevMemBackend> {
Self::new_with_path("/dev/mem", address, poll_timeout)
}
pub fn new_with_path(
path: impl AsRef<Path>,
address: i64,
poll_timeout: Duration,
) -> io::Result<DevMemBackend> {
let device_path = path.as_ref();
log::debug!("Opening DevMem device: {}", device_path.display());
let file = OpenOptions::new()
.read(true)
.write(true)
.open(device_path)?;
log::debug!("DevMem file opened successfully");
let mem = unsafe {
log::debug!(
"Mapping DevMem (address=0x{:x}; size=0x{:x})",
address,
MAP_SIZE
);
let ptr = mmap(
None,
NonZero::new(MAP_SIZE).unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_SHARED,
file,
address,
)?;
log::info!("DevMem memory mapped successfully");
ptr.as_ptr() as *mut u32
};
Ok(DevMemBackend(MemoryMappedBackend::new(mem, poll_timeout)))
}
}
impl Drop for DevMemBackend {
fn drop(&mut self) {
if let Some(ptr) = NonNull::new(self.0.mem) {
unsafe {
let _ = munmap(ptr.cast(), MAP_SIZE);
}
}
}
}
impl XvcServer for DevMemBackend {
type Err = io::Error;
fn set_tck(&self, period_ns: u32) -> Result<u32, Self::Err> {
Ok(period_ns)
}
fn shift(
&self,
num_bits: u32,
tms: &[u8],
tdi: &[u8],
tdo: &mut [u8],
) -> Result<(), Self::Err> {
self.0.shift_data(num_bits, tms, tdi, tdo)
}
}