diff --git a/Cargo.toml b/Cargo.toml index bdda972..c81d211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,12 @@ description = "Low-level hardware interfaces for Valve's Steam Deck" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +std = ["embedded-io/std"] + [dependencies] # logging log = "0.4" + +# io +embedded-io = { version = "0.6" } diff --git a/src/ec/controller.rs b/src/ec/controller.rs new file mode 100644 index 0000000..1062d7c --- /dev/null +++ b/src/ec/controller.rs @@ -0,0 +1,87 @@ +pub struct EmbeddedController { + data_address: u16, + cmd_address: u16, + cursor: u16, + is_cmd_requested: bool, +} + +impl EmbeddedController { + pub fn new(data_addr: u16, cmd_addr: u16) -> Self { + Self { + data_address: data_addr, + cmd_address: cmd_addr, + cursor: 0, + is_cmd_requested: false, + } + } + + fn request_if_not_already(&mut self) { + if !self.is_cmd_requested { + super::ports::outb(self.data_address, 0x81); + self.is_cmd_requested = true; + } + } +} + +impl embedded_io::ErrorType for EmbeddedController { + type Error = core::convert::Infallible; +} + +impl embedded_io::Seek for EmbeddedController { + fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + self.cursor = match pos { + embedded_io::SeekFrom::Start(x) => x as u16, + embedded_io::SeekFrom::End(x) => (u16::MAX as i64 + x) as u16, + embedded_io::SeekFrom::Current(x) => (self.cursor as i64 + x) as u16 + }; + Ok(self.cursor as _) + } +} + +impl embedded_io::Write for EmbeddedController { + fn write(&mut self, buf: &[u8]) -> Result { + use embedded_io::WriteReady; + for &b in buf { + while !self.write_ready()? { } + super::ports::outb(self.cursor, b); + } + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl embedded_io::WriteReady for EmbeddedController { + fn write_ready(&mut self) -> Result { + self.request_if_not_already(); + let ready = super::ports::inb(self.cmd_address) & 2 == 0; + if ready { + self.is_cmd_requested = false; + } + Ok(ready) + } +} + +impl embedded_io::Read for EmbeddedController { + fn read(&mut self, buf: &mut [u8]) -> Result { + use embedded_io::ReadReady; + for b in buf.iter_mut() { + while !self.read_ready()? { } + *b = super::ports::inb(self.cursor) + } + Ok(buf.len()) + } +} + +impl embedded_io::ReadReady for EmbeddedController { + fn read_ready(&mut self) -> Result { + self.request_if_not_already(); + let ready = super::ports::inb(self.cmd_address) & 1 != 0; + if ready { + self.is_cmd_requested = false; + } + Ok(ready) + } +} diff --git a/src/ec/mod.rs b/src/ec/mod.rs index 64aedb5..f223507 100644 --- a/src/ec/mod.rs +++ b/src/ec/mod.rs @@ -7,6 +7,9 @@ pub mod raw_io; pub mod led; +mod controller; +mod ports; +pub use controller::EmbeddedController; use std::io::Error; diff --git a/src/ec/ports.rs b/src/ec/ports.rs new file mode 100644 index 0000000..8a520ba --- /dev/null +++ b/src/ec/ports.rs @@ -0,0 +1,27 @@ +// x86 and x86_64 +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub(crate) fn outb(port: u16, val: u8) { + unsafe { + std::arch::asm!("outb", in("dx") port, in("al") val); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub(crate) fn inb(port: u16) -> u8 { + let out: u8; + unsafe { + std::arch::asm!("inb", in("dx") port, lateout("al") out); + } + out +} + +// Fallback (compiler error) +#[cfg(not(any(any(target_arch = "x86", target_arch = "x86_64"), )))] +pub(crate) fn outb(port: u16, val: u8) { + compile_error!("outb not supported on this platform") +} + +#[cfg(not(any(any(target_arch = "x86", target_arch = "x86_64"), )))] +pub(crate) fn inb(port: u16) -> u8 { + compile_error!("inb not supported on this platform") +}