I was able to come up with a working version of what I was asking about, so I figured I'd post it in case you, or anyone else who read this was interested. I wasn't able to use Box since I'm using no_std, so I mixed your examples with the original information I was basing this off of that had everything contained in a single ReadWrite trait. It basically just boils down to this.
pub trait PortRead {
unsafe fn read_from_port(port: u16) -> Self;
}
pub trait PortWrite {
unsafe fn write_to_port(port: u16, value: Self);
}
pub trait PortReadWrite: PortRead + PortWrite {}
impl PortRead for u8 {
unsafe fn read_from_port(port: u16) -> u8 {}
}
impl PortRead for u16 {
unsafe fn read_from_port(port: u16) -> u16 {}
}
impl PortRead for u32 {
unsafe fn read_from_port(port: u16) -> u32 {}
}
impl PortWrite for u8 {
unsafe fn write_to_port(port: u16, value u8) {}
}
impl PortWrite for u16 {
unsafe fn write_to_port(port: u16, value u16) {}
}
impl PortWrite for u32 {
unsafe fn write_to_port(port: u16, value u32) {}
}
impl PortReadWrite for u8 {} // Placeholder so I don't have to rewrite read/write
impl PortReadWrite for u16 {} // Placeholder so I don't have to rewrite read/write
impl PortReadWrite for u32 {} // Placeholder so I don't have to rewrite read/write
pub struct ReadonlyPort<T: PortRead> {
port: u16,
phantom: PhantomData<T>,
}
pub struct WriteOnlyPort<T: PortWrite> {
port: u16,
phantom: PhantomData<T>,
}
pub struct Port<T: PortReadWrite> {
port: u16,
phantom: PhantomData<T>,
}
impl<T: PortRead> ReadonlyPort<T> {
#[inline]
pub unsafe fn read(&self) -> T {
T::read_from_port(self.port)
}
}
impl<T:PortWrite> WriteOnlyPort<T> {
#[inline]
pub unsafe fn write(&mut self, value: T) {
T::write_to_port(self.port, value)
}
}
impl<T:PortReadWrite> Port<T> {
#[inline]
pub unsafe fn read(&self) -> T {
T::read_from_port(self.port)
}
#[inline]
pub unsafe fn write(&mut self, value: T) {
T::write_to_port(self.port, value)
}
}
I left out the impl code for u8 -> u32 because it's just different versions of inb/outb assembly depending on the size of the data. I'm not sure if this is the cleanest/best way to implement what I was talking about, but doing it this way allows me to be able to create structs like so:
struct GeneralRegisters {
input_status_0: ReadonlyPort<u8>,
input_status_1_mono: ReadonlyPort<u8>,
input_status_1_color: ReadonlyPort<u8>,
feature_control_read: ReadonlyPort<u8>,
feature_control_mono_write: WriteOnlyPort<u8>,
feature_control_color_write: WriteOnlyPort<u8>,
miscellaneous_output_read: ReadonlyPort<u8>,
miscellaneous_output_write: WriteOnlyPort<u8>,
}
and make it so it's impossible to call write on readonly ports, as well as support different data sizes. Thanks for taking the time to help me out.