#[cfg(not(feature = "std"))] use alloc::{ boxed::Box, vec, }; use crate::{ Access, Error, Spi, SpiTarget, }; #[derive(Clone, Copy, Debug)] #[repr(u8)] enum Cmd { // None = 0, Probe = 1, Board = 2, Version = 3, Print = 4, Spi = 5, Reset = 6, FanGet = 7, FanSet = 8, KeymapGet = 9, KeymapSet = 10, } const CMD_SPI_FLAG_READ: u8 = 1 << 0; const CMD_SPI_FLAG_DISABLE: u8 = 1 << 1; const CMD_SPI_FLAG_SCRATCH: u8 = 1 << 2; const CMD_SPI_FLAG_BACKUP: u8 = 1 << 3; /// Run EC commands using a provided access method pub struct Ec { access: A, version: u8, } impl Ec { /// Probes for a compatible EC pub unsafe fn new(access: A) -> Result { // Create EC struct with provided access method and timeout let mut ec = Ec { access, version: 0, }; // Read version of protocol ec.version = ec.probe()?; // Make sure protocol version is supported match ec.version { 1 => (), _ => return Err(Error::Version(ec.version)), } Ok(ec) } /// Unsafe access to access pub unsafe fn access(&mut self) -> &mut A { &mut self.access } unsafe fn command(&mut self, cmd: Cmd, data: &mut [u8]) -> Result<(), Error> { match self.access.command(cmd as u8, data)? { 0 => Ok(()), err => Err(Error::Protocol(err)), } } /// Probe for EC pub unsafe fn probe(&mut self) -> Result { let mut data = [0; 3]; self.command(Cmd::Probe, &mut data)?; let signature = (data[0], data[1]); if signature == (0x76, 0xEC) { let version = data[2]; Ok(version) } else { Err(Error::Signature(signature)) } } /// Read board from EC pub unsafe fn board(&mut self, data: &mut [u8]) -> Result { self.command(Cmd::Board, data)?; let mut i = 0; while i < data.len() { if data[i] == 0 { break; } i += 1; } Ok(i) } /// Read version from EC pub unsafe fn version(&mut self, data: &mut [u8]) -> Result { self.command(Cmd::Version, data)?; let mut i = 0; while i < data.len() { if data[i] == 0 { break; } i += 1; } Ok(i) } /// Print data to EC console pub unsafe fn print(&mut self, data: &[u8]) -> Result { //TODO: use self.access.data_size() let flags = 0; for chunk in data.chunks(256 - 4) { let mut data = [0; 256 - 2]; data[0] = flags; data[1] = chunk.len() as u8; for i in 0..chunk.len() { data[i + 2] = chunk[i]; } self.command(Cmd::Print, &mut data)?; if data[1] != chunk.len() as u8 { return Err(Error::Verify); } } Ok(data.len()) } /// Access EC SPI bus pub unsafe fn spi(&mut self, target: SpiTarget, scratch: bool) -> Result, Error> { let data_size = self.access.data_size(); let mut spi = EcSpi { ec: self, target, scratch, buffer: vec![0; data_size].into_boxed_slice(), }; spi.reset()?; Ok(spi) } /// Reset EC. Will also power off computer. pub unsafe fn reset(&mut self) -> Result<(), Error> { self.command(Cmd::Reset, &mut []) } /// Read fan duty cycle by fan index pub unsafe fn fan_get(&mut self, index: u8) -> Result { let mut data = [ index, 0 ]; self.command(Cmd::FanGet, &mut data)?; Ok(data[1]) } /// Set fan duty cycle by fan index pub unsafe fn fan_set(&mut self, index: u8, duty: u8) -> Result<(), Error> { let mut data = [ index, duty ]; self.command(Cmd::FanSet, &mut data) } /// Read keymap data by layout, output pin, and input pin pub unsafe fn keymap_get(&mut self, layer: u8, output: u8, input: u8) -> Result { let mut data = [ layer, output, input, 0, 0 ]; self.command(Cmd::KeymapGet, &mut data)?; Ok( (data[3] as u16) | ((data[4] as u16) << 8) ) } /// Set keymap data by layout, output pin, and input pin pub unsafe fn keymap_set(&mut self, layer: u8, output: u8, input: u8, value: u16) -> Result<(), Error> { let mut data = [ layer, output, input, value as u8, (value >> 8) as u8 ]; self.command(Cmd::KeymapSet, &mut data) } } pub struct EcSpi<'a, A: Access> { ec: &'a mut Ec, target: SpiTarget, scratch: bool, buffer: Box<[u8]>, } impl<'a, A: Access> EcSpi<'a, A> { fn flags(&self, read: bool, disable: bool) -> u8 { let mut flags = 0; if read { flags |= CMD_SPI_FLAG_READ; } if disable { flags |= CMD_SPI_FLAG_DISABLE; } if self.scratch { flags |= CMD_SPI_FLAG_SCRATCH; } match self.target { SpiTarget::Main => (), SpiTarget::Backup => { flags |= CMD_SPI_FLAG_BACKUP; }, } flags } } impl<'a, A: Access> Spi for EcSpi<'a, A> { fn target(&self) -> SpiTarget { self.target } /// Disable SPI chip, must be done before and after a transaction unsafe fn reset(&mut self) -> Result<(), Error> { let flags = self.flags(false, true); self.buffer[0] = flags; self.buffer[1] = 0; self.ec.command(Cmd::Spi, &mut self.buffer[..2])?; if self.buffer[1] != 0 { return Err(Error::Verify); } Ok(()) } /// SPI read unsafe fn read(&mut self, data: &mut [u8]) -> Result { let flags = self.flags(true, false); for chunk in data.chunks_mut(self.buffer.len() - 2) { self.buffer[0] = flags; self.buffer[1] = chunk.len() as u8; self.ec.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?; if self.buffer[1] != chunk.len() as u8 { return Err(Error::Verify); } for i in 0..chunk.len() { chunk[i] = self.buffer[i + 2]; } } Ok(data.len()) } /// SPI write unsafe fn write(&mut self, data: &[u8]) -> Result { let flags = self.flags(false, false); for chunk in data.chunks(self.buffer.len() - 2) { self.buffer[0] = flags; self.buffer[1] = chunk.len() as u8; for i in 0..chunk.len() { self.buffer[i + 2] = chunk[i]; } self.ec.command(Cmd::Spi, &mut self.buffer[..(chunk.len() + 2)])?; if self.buffer[1] != chunk.len() as u8 { return Err(Error::Verify); } } Ok(data.len()) } } impl<'a, A: Access> Drop for EcSpi<'a, A> { fn drop(&mut self) { unsafe { let _ = self.reset(); } } }