use std::path::Path;
use std::fs;
use std::io::{self, prelude::*, SeekFrom, };
use std::convert::{TryInto};
use super::{AccessMethod, AccessError};
use crate::device::{Address, Device};
pub const DEV_PORT_PATH: &'static str = "/dev/port";
const CONFIG_ADDRESS: u32 = 0xCF8;
#[derive(Debug)]
pub struct DevPort<'a> {
path: &'a Path,
address: Address,
offset: u8,
}
impl<'a> DevPort<'a> {
pub fn new(path: &'a Path, address: Address) -> Self {
Self { path, address, offset: 0 }
}
fn config_address(&self) -> u32 {
let Address { bus, device, function, .. } = self.address;
let (bus, device, function, offset) =
(bus as u32, device as u32, function as u32, self.offset as u32);
(bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC) | 0x80000000
}
}
impl<'a> Read for DevPort<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(self.path)?;
let init_offset = self.offset as usize;
let end = self.offset.saturating_add((buf.len()).min(u8::MAX as usize) as u8);
while self.offset < end {
if file.seek(SeekFrom::Start(CONFIG_ADDRESS.into())).is_err() {
break;
}
let data_address = self.config_address().to_le_bytes();
if !matches!(file.write(&data_address), Ok(4)) {
break;
}
if file.seek(SeekFrom::Current((self.offset % 4).into())).is_err() {
break;
}
let buf_start = self.offset as usize - init_offset;
let buf_end = buf.len().min(buf_start + 4 - (self.offset % 4) as usize);
if let Ok(bytes_read @ 1..=4) = file.read(&mut buf[buf_start..buf_end]) {
self.offset = self.offset.saturating_add(bytes_read as u8);
} else {
break;
}
}
Ok(self.offset as usize - init_offset)
}
}
impl<'a> Seek for DevPort<'a> {
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
let offset = match style {
SeekFrom::Start(n) => n as i64,
SeekFrom::End(n) => n + u8::MAX as i64,
SeekFrom::Current(n) => n + self.offset as i64,
};
self.offset = offset.try_into()
.map_err(|_| io::Error::new(
io::ErrorKind::InvalidInput,
format!("invalid seek to position: {}", offset)
))?;
Ok(self.offset as u64)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn config_address() {
let addr = Address { domain: 0, bus: 1, device: 2, function: 3 };
let dev_port = DevPort::new(Path::new("/dev/null"), addr);
let address = dev_port.config_address();
assert_eq!(0x80011300u32, address);
}
#[test]
fn read() {
let temp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(&temp_path).unwrap();
file.write_all(&[0u8; 0x1000]).unwrap();
file.seek(SeekFrom::Start((CONFIG_ADDRESS + 4).into())).unwrap();
file.write_all(&[0x00,0x55,0xAA,0xFF]).unwrap();
file.sync_all().unwrap();
let addr = Address { domain: 0, bus: 0, device: 0, function: 0 };
let mut dev_port = DevPort::new(&temp_path, addr);
let mut one = [0u8; 1];
dev_port.read_exact(&mut one).unwrap(); assert_eq!([0x00], one, "One byte");
let mut two = [0u8; 2];
dev_port.read_exact(&mut two).unwrap(); assert_eq!([0x55,0xAA], two, "Two bytes");
let mut three = [0u8; 3];
dev_port.read_exact(&mut three).unwrap(); assert_eq!([0xFF,0x00,0x55], three, "Three bytes");
let fill = std::iter::repeat([0xAA,0xFF,0x00,0x55,])
.flatten()
.take(250)
.collect::<Vec<_>>();
let mut buf = vec![0u8; 250];
dev_port.read(&mut buf).unwrap(); assert_eq!(fill, buf, "Fill whole buf");
let mut overflow = [0u8; 4];
dev_port.read(&mut overflow).unwrap();
assert_eq!([0u8; 4], overflow, "Overflow");
}
#[test]
fn seek() {
let temp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&temp_path).unwrap();
file.write_all(&[0u8; 0x1000]).unwrap();
file.seek(SeekFrom::Start((CONFIG_ADDRESS + 4).into())).unwrap();
file.write_all(&[0x00,0x55,0xAA,0xFF]).unwrap();
file.sync_all().unwrap();
let addr = Address { domain: 0, bus: 0, device: 0, function: 0 };
let mut dev_port = DevPort::new(&temp_path, addr);
let x = rand::random::<u8>();
let mut start = [0];
let mut end = [0];
dev_port.seek(SeekFrom::Start(x as u64)).unwrap();
dev_port.read(&mut start).unwrap();
dev_port.seek(SeekFrom::End(x as i64 - 255)).unwrap();
dev_port.read(&mut end).unwrap();
assert_eq!(start, end, "Start: {}, End: {}", start[0], end[0]);
let x = rand::random::<f32>().clamp(4.0, 251.0) as u64;
let mut fwd = [0];
let mut bwd = [0];
dev_port.seek(SeekFrom::Start(x)).unwrap();
dev_port.seek(SeekFrom::Current(4)).unwrap();
dev_port.read(&mut fwd).unwrap();
dev_port.seek(SeekFrom::Current(-9)).unwrap();
dev_port.read(&mut bwd).unwrap();
assert_eq!(fwd, bwd, "Forward: {}, Backward: {}", fwd[0], bwd[0]);
}
}