diff --git a/tool/src/access/lpc/mod.rs b/tool/src/access/lpc/mod.rs index fbdc735..6668de8 100644 --- a/tool/src/access/lpc/mod.rs +++ b/tool/src/access/lpc/mod.rs @@ -17,3 +17,8 @@ mod direct; pub use self::linux::AccessLpcLinux; #[cfg(all(feature = "std", target_os = "linux"))] mod linux; + +#[cfg(feature = "std")] +mod sim; +#[cfg(feature = "std")] +pub use self::sim::AccessLpcSim; diff --git a/tool/src/access/lpc/sim.rs b/tool/src/access/lpc/sim.rs new file mode 100644 index 0000000..d43a0f6 --- /dev/null +++ b/tool/src/access/lpc/sim.rs @@ -0,0 +1,124 @@ +use std::{ + io, + net::UdpSocket, + time::Duration, +}; + +use crate::{ + Access, + Error, + StdTimeout, + Timeout, + timeout, +}; + +use super::*; + +pub struct AccessLpcSim { + socket: UdpSocket, + timeout: StdTimeout, +} + +impl AccessLpcSim { + pub unsafe fn new(timeout: Duration) -> Result { + let socket = UdpSocket::bind("127.0.0.1:0")?; + socket.connect("127.0.0.1:8587")?; + let mut access = Self { + socket, + timeout: StdTimeout::new(timeout), + }; + access.transaction(0x00, 0, 0)?; + Ok(access) + } + + fn transaction(&mut self, kind: u8, addr: u16, value: u8) -> io::Result { + let addr = addr.to_le_bytes(); + let request = [kind as u8, addr[0], addr[1], value]; + if self.socket.send(&request)? != request.len() { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Socket request incorrect size" + )); + } + + let mut response = [0]; + if self.socket.recv(&mut response)? != response.len() { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Socket response incorrect size" + )); + } + + Ok(response[0]) + } + + pub fn inb(&mut self, addr: u16) -> Result { + Ok(self.transaction(0x01, addr, 0)?) + } + + pub fn outb(&mut self, addr: u16, value: u8) -> Result<(), Error> { + self.transaction(0x02, addr, value)?; + Ok(()) + } + + /// Read from the command space + unsafe fn read_cmd(&mut self, addr: u8) -> Result { + self.inb(SMFI_CMD_BASE + u16::from(addr)) + } + + /// Write to the command space + unsafe fn write_cmd(&mut self, addr: u8, data: u8) -> Result<(), Error> { + self.outb(SMFI_CMD_BASE + u16::from(addr), data) + } + + /// Read from the debug space + //TODO: better public interface + pub unsafe fn read_debug(&mut self, addr: u8) -> Result { + self.inb(SMFI_DBG_BASE + u16::from(addr)) + } + + /// Returns Ok if a command can be sent + unsafe fn command_check(&mut self) -> Result<(), Error> { + if self.read_cmd(SMFI_CMD_CMD)? == 0 { + Ok(()) + } else { + Err(Error::WouldBlock) + } + } +} + +impl Access for AccessLpcSim { + unsafe fn command(&mut self, cmd: u8, data: &mut [u8]) -> Result { + // Test data length + if data.len() > self.data_size() { + return Err(Error::DataLength(data.len())); + } + + // All previous commands should be finished + self.command_check()?; + + // Write data bytes, index should be valid due to length test above + for i in 0..data.len() { + self.write_cmd(i as u8 + SMFI_CMD_DATA, data[i])?; + } + + // Write command byte, which starts command + self.write_cmd(SMFI_CMD_CMD, cmd as u8)?; + + // Wait for command to finish with timeout + self.timeout.reset(); + timeout!(self.timeout, self.command_check())?; + + // Read data bytes, index should be valid due to length test above + for i in 0..data.len() { + data[i] = self.read_cmd(i as u8 + SMFI_CMD_DATA)?; + } + + // Return response byte + self.read_cmd(SMFI_CMD_RES) + } + + fn data_size(&self) -> usize { + SMFI_CMD_SIZE - SMFI_CMD_DATA as usize + } +}