Fix write and read op bugs found in Deck test

This commit is contained in:
NGnius (Graham) 2024-01-11 17:41:17 -05:00
parent b7fb4de8ff
commit 1b9bbbd0d5
4 changed files with 219 additions and 64 deletions

View file

@ -6,7 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
smokepatio = { version = "0.1", path = "../" }
smokepatio = { version = "0.1", path = "../", features = [ "std" ] }
clap = { version = "4.4", features = [ "derive" ] }
clap-num = "1.0"
libc = "0.2"

View file

@ -7,6 +7,13 @@ fn main() {
println!("args: {:?}", cli);
let mut ec = EmbeddedController::new(cli.data_address, cli.cmd_address);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe {
libc::ioperm(cli.data_address as _, 1, 1);
libc::ioperm(cli.cmd_address as _, 1, 1);
}
match cli.op {
args::Operation::ReadAll => todo!(),
args::Operation::Read { address } => {

View file

@ -1,28 +1,45 @@
pub trait SetValue<S> {
pub trait SetValue<S, RV = u8> {
const SETTING: S;
fn raw_value(&self) -> u8;
fn raw_value(&self) -> RV;
}
pub trait ControllerSet<S>: embedded_io::ErrorType {
fn set<V: SetValue<S>>(&mut self, value: V) -> Result<(), Self::Error>;
pub trait ControllerSet<S, RV = u8>: embedded_io::ErrorType {
fn set<V: SetValue<S, RV>>(&mut self, value: V) -> Result<(), Self::Error>;
}
pub trait GetValue<S> {
pub trait GetValue<S, RV = u8> {
const SETTING: S;
fn raw_value(value: u8) -> Self;
fn raw_value(value: RV) -> Self;
}
pub trait ControllerGet<S>: embedded_io::ErrorType {
fn get<V: GetValue<S>>(&mut self) -> Result<V, Self::Error>;
pub trait ControllerGet<S, RV = u8>: embedded_io::ErrorType {
fn get<V: GetValue<S, RV>>(&mut self) -> Result<V, Self::Error>;
}
/// Size of values to write and read from ports.
/// System endianness is assumed.
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum ValueSize {
Byte = 1, // u8
Word = 2, // u16
Long = 4, // u32
}
#[cfg(feature = "std")]
const WAIT_SLEEP: std::time::Duration = std::time::Duration::from_micros(10);
#[cfg(feature = "std")]
const MAX_WAIT_CHECKS: u16 = 0x07ff;
#[cfg(not(feature = "std"))]
const MAX_WAIT_CHECKS: u16 = 0x7fff;
pub struct EmbeddedController {
data_address: u16,
cmd_address: u16,
cursor: u16,
is_cmd_requested: bool,
cmd_requested: u16,
rw_size: ValueSize,
}
impl EmbeddedController {
@ -30,55 +47,84 @@ impl EmbeddedController {
Self {
data_address: data_addr,
cmd_address: cmd_addr,
cursor: 0,
is_cmd_requested: false,
cmd_requested: 0,
rw_size: ValueSize::Byte,
}
}
fn request_if_not_already(&mut self) {
if !self.is_cmd_requested {
super::ports::outb(self.data_address, 0x81);
self.is_cmd_requested = true;
if self.cmd_requested == 0 {
super::ports::outb(self.cmd_address, 0x81);
self.cmd_requested = 1;
} else {
self.cmd_requested += 1;
}
}
fn wait_for_write_ready(&mut self) {
use embedded_io::WriteReady;
while !self.write_ready().unwrap() {
#[cfg(feature = "std")]
std::thread::sleep(WAIT_SLEEP);
}
}
fn wait_for_read_ready(&mut self) {
use embedded_io::ReadReady;
while !self.read_ready().unwrap() {
#[cfg(feature = "std")]
std::thread::sleep(WAIT_SLEEP);
}
}
pub fn set(&mut self, setting: u16, value: u8) {
use embedded_io::{Write, Seek};
self.seek(embedded_io::SeekFrom::Start(setting as _)).unwrap();
use embedded_io::Write;
self.write(&[setting as u8]).unwrap();
self.cmd_requested = 1; // don't re-request, but still wait
self.write(&[value]).unwrap();
}
pub fn get(&mut self, setting: u16) -> u8 {
use embedded_io::{Read, Seek};
self.seek(embedded_io::SeekFrom::Start(setting as _)).unwrap();
use embedded_io::{Read, Write};
self.write(&[setting as u8]).unwrap();
self.cmd_requested = 1; // don't re-request, but still wait
let mut buf = [0u8; 1];
self.read(&mut buf).unwrap();
buf[0]
}
pub fn set_value_size(&mut self, size: ValueSize) {
self.rw_size = size;
}
}
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<u64, Self::Error> {
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<usize, Self::Error> {
let size = self.rw_size as u8;
match self.rw_size {
ValueSize::Byte => for &b in buf {
self.wait_for_write_ready();
self.cmd_requested = 1; // don't re-request, but still wait
super::ports::outb(self.data_address, b);
},
ValueSize::Word => for i in (0..buf.len()).step_by(size as usize) {
self.wait_for_write_ready();
self.cmd_requested = 1; // don't re-request, but still wait
let w = [buf[i], buf[i+1]];
super::ports::outw(self.data_address, u16::from_ne_bytes(w));
},
ValueSize::Long => for i in (0..buf.len()).step_by(size as usize) {
self.wait_for_write_ready();
self.cmd_requested = 1; // don't re-request, but still wait
let l = [buf[i], buf[i+1], buf[i+2], buf[i+3]];
super::ports::outl(self.data_address, u32::from_ne_bytes(l));
}
}
impl embedded_io::Write for EmbeddedController {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
use embedded_io::WriteReady;
for &b in buf {
while !self.write_ready()? { }
super::ports::outb(self.cursor, b);
}
Ok(buf.len())
}
@ -90,9 +136,9 @@ impl embedded_io::Write for EmbeddedController {
impl embedded_io::WriteReady for EmbeddedController {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
self.request_if_not_already();
let ready = super::ports::inb(self.cmd_address) & 2 == 0;
let ready = super::ports::inb(self.cmd_address) & 2 == 0 || self.cmd_requested == MAX_WAIT_CHECKS;
if ready {
self.is_cmd_requested = false;
self.cmd_requested = 0;
}
Ok(ready)
}
@ -100,10 +146,29 @@ impl embedded_io::WriteReady for EmbeddedController {
impl embedded_io::Read for EmbeddedController {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
use embedded_io::ReadReady;
for b in buf.iter_mut() {
while !self.read_ready()? { }
*b = super::ports::inb(self.cursor)
let size = self.rw_size as u8;
match self.rw_size {
ValueSize::Byte => for b in buf.iter_mut() {
self.wait_for_read_ready();
self.cmd_requested = 1; // don't re-request, but still wait
*b = super::ports::inb(self.data_address);
},
ValueSize::Word => for i in (0..buf.len()).step_by(size as usize) {
self.wait_for_read_ready();
self.cmd_requested = 1; // don't re-request, but still wait
let w = super::ports::inw(self.data_address).to_ne_bytes();
for j in 0..(size as usize) {
buf[i + j] = w[j];
}
},
ValueSize::Long => for i in (0..buf.len()).step_by(size as usize) {
self.wait_for_read_ready();
self.cmd_requested = 1; // don't re-request, but still wait
let l = super::ports::inl(self.data_address).to_ne_bytes();
for j in 0..(size as usize) {
buf[i + j] = l[j];
}
}
}
Ok(buf.len())
}
@ -112,9 +177,9 @@ impl embedded_io::Read for EmbeddedController {
impl embedded_io::ReadReady for EmbeddedController {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
self.request_if_not_already();
let ready = super::ports::inb(self.cmd_address) & 1 != 0;
let ready = super::ports::inb(self.cmd_address) & 1 != 0 || self.cmd_requested == MAX_WAIT_CHECKS;
if ready {
self.is_cmd_requested = false;
self.cmd_requested = 0;
}
Ok(ready)
}

View file

@ -1,30 +1,111 @@
// x86 and x86_64
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64")))]
mod ports_internal {
#[inline]
pub(crate) fn outb(port: u16, val: u8) {
//let val: i16 = val.into();
//println!("outb port:{:#04x} value:{:#02x}", port, val);
unsafe {
std::arch::asm!("out dx, al", in("dx") port, in("al") val);
//std::arch::asm!("outb %al, %dx", in("al") val, in("dx") port, options(att_syntax));
//std::arch::asm!("out dx,al", in("al") val, in("dx") port);
}
//println!("outb done");
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline]
pub(crate) fn outw(port: u16, val: u16) {
//println!("outw port:{:#04x} value:{:#02x}", port, val);
unsafe {
std::arch::asm!("out dx, ax", in("dx") port, in("ax") val);
//std::arch::asm!("outb %al, %dx", in("al") val, in("dx") port, options(att_syntax));
//std::arch::asm!("out dx,al", in("al") val, in("dx") port);
}
//println!("outw done");
}
#[inline]
pub(crate) fn outl(port: u16, val: u32) {
//println!("outl port:{:#04x} value:{:#02x}", port, val);
unsafe {
std::arch::asm!("out dx, eax", in("dx") port, in("eax") val);
//std::arch::asm!("outb %al, %dx", in("al") val, in("dx") port, options(att_syntax));
//std::arch::asm!("out dx,al", in("al") val, in("dx") port);
}
//println!("outl done");
}
#[inline]
pub(crate) fn inb(port: u16) -> u8 {
let out: u8;
//println!("inb port:{:#04x}", port);
unsafe {
std::arch::asm!("in al, dx", in("dx") port, lateout("al") out);
// asm!("inb %dx, %al", in("dx") port, out("al") ret, options(att_syntax));
//std::arch::asm!("inb %dx, %al", in("dx") port, out("al") out, options(att_syntax));
//std::arch::asm!("in al,dx", in("dx") port, out("al") out);
}
//println!("inb port:{:#04x} -> value:{:#02x}", port, out);
out
}
#[inline]
pub(crate) fn inw(port: u16) -> u16 {
let out: u16;
//println!("inb port:{:#04x}", port);
unsafe {
std::arch::asm!("in ax, dx", in("dx") port, lateout("ax") out);
//std::arch::asm!("inb %dx, %al", in("dx") port, out("al") out, options(att_syntax));
//std::arch::asm!("in al,dx", in("dx") port, out("al") out);
}
//println!("inb port:{:#04x} -> value:{:#02x}", port, out);
out
}
#[inline]
pub(crate) fn inl(port: u16) -> u32 {
let out: u32;
//println!("inb port:{:#04x}", port);
unsafe {
std::arch::asm!("in eax, dx", in("dx") port, lateout("eax") out);
//std::arch::asm!("inb %dx, %al", in("dx") port, out("al") out, options(att_syntax));
//std::arch::asm!("in al,dx", in("dx") port, out("al") out);
}
//println!("inb port:{:#04x} -> value:{:#02x}", port, out);
out
}
}
// Fallback (compiler error)
#[cfg(not(any(any(target_arch = "x86", target_arch = "x86_64"), )))]
#[cfg(not(any(all(any(target_arch = "x86", target_arch = "x86_64")), target_os = "linux")))]
mod ports_internal {
#[inline]
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"), )))]
#[inline]
pub(crate) fn outw(port: u16, val: u16) {
compile_error!("outw not supported on this platform")
}
#[inline]
pub(crate) fn outl(port: u16, val: u32) {
compile_error!("outl not supported on this platform")
}
#[inline]
pub(crate) fn inb(port: u16) -> u8 {
compile_error!("inb not supported on this platform")
}
#[inline]
pub(crate) fn inw(port: u16) -> u16 {
compile_error!("inw not supported on this platform")
}
#[inline]
pub(crate) fn inl(port: u16) -> u32 {
compile_error!("inl not supported on this platform")
}
}
pub(crate) use ports_internal::*;