From 1af49ed2a95411c08e8c06df1808bca2598edf5a Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Thu, 14 Sep 2023 13:08:15 +0800 Subject: [PATCH 1/5] feat: Supports 32-bit registers --- uart8250/src/registers.rs | 65 +++++++++++++++++++++++++++++++-------- uart8250/src/uart.rs | 18 +++++------ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/uart8250/src/registers.rs b/uart8250/src/registers.rs index 85b5c39..da4ee65 100644 --- a/uart8250/src/registers.rs +++ b/uart8250/src/registers.rs @@ -22,21 +22,62 @@ use volatile_register::{RO, RW}; /// | +5 | x | Read | LSR | Line Status Register | /// | +6 | x | Read | MSR | Modem Status Register | /// | +7 | x | Read/Write | SR | Scratch Register | -#[repr(C, packed)] -pub struct Registers { - pub thr_rbr_dll: RW, - pub ier_dlh: RW, - pub iir_fcr: RW, - pub lcr: RW, - pub mcr: RW, - pub lsr: RO, - pub msr: RO, - pub scratch: RW, +#[repr(C)] +pub struct Registers { + pub thr_rbr_dll: RwReg, + pub ier_dlh: RwReg, + pub iir_fcr: RwReg, + pub lcr: RwReg, + pub mcr: RwReg, + pub lsr: RoReg, + pub msr: RoReg, + pub scratch: RwReg, } -impl Registers { +#[repr(C)] +pub struct RoReg([RO; W]); + +impl RoReg { + /// Reads the value of the register + #[inline(always)] + pub fn read(&self) -> T { + self.0[0].read() + } +} + +#[repr(C)] +pub struct RwReg([RW; W]); + +impl RwReg { + /// Performs a read-modify-write operation + /// + /// NOTE: `unsafe` because writes to a register are side effectful + #[inline(always)] + pub unsafe fn modify(&self, f: F) + where + F: FnOnce(T) -> T, + { + self.0[0].modify(f); + } + + /// Reads the value of the register + #[inline(always)] + pub fn read(&self) -> T { + self.0[0].read() + } + + /// Writes a `value` into the register + /// + /// NOTE: `unsafe` because writes to a register are side effectful + #[inline(always)] + pub unsafe fn write(&self, value: T) { + self.0[0].write(value); + } +} + +impl Registers { /// Constructs a new instance of the UART registers starting at the given base address. pub unsafe fn from_base_address(base_address: usize) -> &'static mut Self { - &mut *(base_address as *mut crate::registers::Registers) + &mut *(base_address as *mut Registers) } } diff --git a/uart8250/src/uart.rs b/uart8250/src/uart.rs index d13b345..8586c6e 100644 --- a/uart8250/src/uart.rs +++ b/uart8250/src/uart.rs @@ -112,11 +112,11 @@ impl Display for TransmitError { /// # MMIO version of an 8250 UART. /// /// **Note** This is only tested on the NS16550 compatible UART used in QEMU 5.0 virt machine of RISC-V. -pub struct MmioUart8250<'a> { - reg: &'a mut Registers, +pub struct MmioUart8250<'a, const W: usize> { + reg: &'a mut Registers, } -impl<'a> MmioUart8250<'a> { +impl<'a, const W: usize> MmioUart8250<'a, W> { /// Creates a new UART. /// /// # Safety @@ -670,7 +670,7 @@ impl<'a> MmioUart8250<'a> { /// /// A simple implementation, may be changed in the future #[cfg(feature = "fmt")] -impl<'a> fmt::Write for MmioUart8250<'a> { +impl<'a, const W: usize> fmt::Write for MmioUart8250<'a, W> { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.as_bytes() { // If buffer is full, keep retrying. @@ -681,7 +681,7 @@ impl<'a> fmt::Write for MmioUart8250<'a> { } #[cfg(feature = "embedded")] -impl embedded_hal::serial::Read for MmioUart8250<'_> { +impl embedded_hal::serial::Read for MmioUart8250<'_, W> { type Error = Infallible; fn read(&mut self) -> nb::Result { @@ -690,7 +690,7 @@ impl embedded_hal::serial::Read for MmioUart8250<'_> { } #[cfg(feature = "embedded")] -impl embedded_hal::serial::Write for MmioUart8250<'_> { +impl embedded_hal::serial::Write for MmioUart8250<'_, W> { type Error = Infallible; fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { @@ -720,7 +720,7 @@ mod tests { // Create a fake UART using an in-memory buffer, and check that it is initialised as // expected. let mut fake_registers: [u8; 8] = [0xff; 8]; - let uart = unsafe { MmioUart8250::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; uart.init(11_059_200, 115200); @@ -732,7 +732,7 @@ mod tests { #[test] fn write() { let mut fake_registers: [u8; 8] = [0; 8]; - let uart = unsafe { MmioUart8250::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; // Pretend that the transmit buffer is full. fake_registers[5] = 0; @@ -748,7 +748,7 @@ mod tests { #[test] fn read() { let mut fake_registers: [u8; 8] = [0; 8]; - let uart = unsafe { MmioUart8250::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; // First try to read when there is nothing available. assert_eq!(uart.read_byte(), None); From 308c3611b3d21c2ea50913f322369edf7cf45ed9 Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Thu, 14 Sep 2023 13:09:35 +0800 Subject: [PATCH 2/5] fix: ignore .idea --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c41cc9e..cc48954 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/target \ No newline at end of file +/target +.idea \ No newline at end of file From de6e461716de497e672dabb1ae199f7455a1a3c7 Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Thu, 14 Sep 2023 21:26:14 +0800 Subject: [PATCH 3/5] feat: new abstraction --- uart8250/README.md | 4 +-- uart8250/src/registers.rs | 67 +++++++++++++++++++++++++-------------- uart8250/src/uart.rs | 20 ++++++------ 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/uart8250/README.md b/uart8250/README.md index 43c7cd7..bbac070 100644 --- a/uart8250/README.md +++ b/uart8250/README.md @@ -13,7 +13,7 @@ Besides, this crate currently is not following [Rust API Guidelines](https://rus ## Usage ```rust -let uart = MmioUart8250::new(0x1000_0000); +let uart = MmioUart8250::::new(0x1000_0000); uart.init(11_059_200, 115200); if let Some(c) = uart.read_byte() { //... @@ -23,7 +23,7 @@ if let Some(c) = uart.read_byte() { If you turn on feature `fmt` ```rust -let uart = MmioUart8250::new(0x1000_0000); +let uart = MmioUart8250::::new(0x1000_0000); uart.init(11_059_200, 115200); pub fn print_uart(args: fmt::Arguments) { diff --git a/uart8250/src/registers.rs b/uart8250/src/registers.rs index da4ee65..6679156 100644 --- a/uart8250/src/registers.rs +++ b/uart8250/src/registers.rs @@ -23,61 +23,82 @@ use volatile_register::{RO, RW}; /// | +6 | x | Read | MSR | Modem Status Register | /// | +7 | x | Read/Write | SR | Scratch Register | #[repr(C)] -pub struct Registers { - pub thr_rbr_dll: RwReg, - pub ier_dlh: RwReg, - pub iir_fcr: RwReg, - pub lcr: RwReg, - pub mcr: RwReg, - pub lsr: RoReg, - pub msr: RoReg, - pub scratch: RwReg, +pub struct Registers { + pub thr_rbr_dll: RwReg, + pub ier_dlh: RwReg, + pub iir_fcr: RwReg, + pub lcr: RwReg, + pub mcr: RwReg, + pub lsr: RoReg, + pub msr: RoReg, + pub scratch: RwReg, } #[repr(C)] -pub struct RoReg([RO; W]); +pub struct RoReg(RO); -impl RoReg { +impl RoReg { /// Reads the value of the register #[inline(always)] - pub fn read(&self) -> T { - self.0[0].read() + pub fn read(&self) -> u8 { + self.0.read().val() } } #[repr(C)] -pub struct RwReg([RW; W]); +pub struct RwReg(RW); -impl RwReg { +impl RwReg { /// Performs a read-modify-write operation /// /// NOTE: `unsafe` because writes to a register are side effectful #[inline(always)] pub unsafe fn modify(&self, f: F) where - F: FnOnce(T) -> T, + F: FnOnce(u8) -> u8, { - self.0[0].modify(f); + self.0.write(f(self.read()).into()); } /// Reads the value of the register #[inline(always)] - pub fn read(&self) -> T { - self.0[0].read() + pub fn read(&self) -> u8 { + self.0.read().val() } /// Writes a `value` into the register /// /// NOTE: `unsafe` because writes to a register are side effectful #[inline(always)] - pub unsafe fn write(&self, value: T) { - self.0[0].write(value); + pub unsafe fn write(&self, value: u8) { + self.0.write(value.into()) } } -impl Registers { +impl Registers { /// Constructs a new instance of the UART registers starting at the given base address. pub unsafe fn from_base_address(base_address: usize) -> &'static mut Self { - &mut *(base_address as *mut Registers) + &mut *(base_address as *mut Registers) + } +} + +pub trait Register: From { + /// 取出寄存器中的有效位。 + fn val(self) -> u8; +} + +/// 寄存器的 8 位模式。 +impl Register for u8 { + #[inline] + fn val(self) -> u8 { + self + } +} + +/// 寄存器的 32 位模式。 +impl Register for u32 { + #[inline] + fn val(self) -> u8 { + self as _ } } diff --git a/uart8250/src/uart.rs b/uart8250/src/uart.rs index 8586c6e..ebe8a8a 100644 --- a/uart8250/src/uart.rs +++ b/uart8250/src/uart.rs @@ -3,7 +3,7 @@ use bitflags::bitflags; use core::convert::Infallible; use core::fmt::{self, Display, Formatter}; -use crate::registers::Registers; +use crate::registers::{Register, Registers}; bitflags! { /// Interrupt Enable Register (bitflags) @@ -112,11 +112,11 @@ impl Display for TransmitError { /// # MMIO version of an 8250 UART. /// /// **Note** This is only tested on the NS16550 compatible UART used in QEMU 5.0 virt machine of RISC-V. -pub struct MmioUart8250<'a, const W: usize> { - reg: &'a mut Registers, +pub struct MmioUart8250<'a, R: Register + Copy> { + reg: &'a mut Registers, } -impl<'a, const W: usize> MmioUart8250<'a, W> { +impl<'a, R: Register + Copy + 'static> MmioUart8250<'a, R> { /// Creates a new UART. /// /// # Safety @@ -670,7 +670,7 @@ impl<'a, const W: usize> MmioUart8250<'a, W> { /// /// A simple implementation, may be changed in the future #[cfg(feature = "fmt")] -impl<'a, const W: usize> fmt::Write for MmioUart8250<'a, W> { +impl<'a, R: Register + Copy + 'static> fmt::Write for MmioUart8250<'a, R> { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.as_bytes() { // If buffer is full, keep retrying. @@ -681,7 +681,7 @@ impl<'a, const W: usize> fmt::Write for MmioUart8250<'a, W> { } #[cfg(feature = "embedded")] -impl embedded_hal::serial::Read for MmioUart8250<'_, W> { +impl embedded_hal::serial::Read for MmioUart8250<'_, R> { type Error = Infallible; fn read(&mut self) -> nb::Result { @@ -690,7 +690,7 @@ impl embedded_hal::serial::Read for MmioUart8250<'_, W> { } #[cfg(feature = "embedded")] -impl embedded_hal::serial::Write for MmioUart8250<'_, W> { +impl embedded_hal::serial::Write for MmioUart8250<'_, R> { type Error = Infallible; fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { @@ -720,7 +720,7 @@ mod tests { // Create a fake UART using an in-memory buffer, and check that it is initialised as // expected. let mut fake_registers: [u8; 8] = [0xff; 8]; - let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::::new(&mut fake_registers as *mut u8 as usize) }; uart.init(11_059_200, 115200); @@ -732,7 +732,7 @@ mod tests { #[test] fn write() { let mut fake_registers: [u8; 8] = [0; 8]; - let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::::new(&mut fake_registers as *mut u8 as usize) }; // Pretend that the transmit buffer is full. fake_registers[5] = 0; @@ -748,7 +748,7 @@ mod tests { #[test] fn read() { let mut fake_registers: [u8; 8] = [0; 8]; - let uart = unsafe { MmioUart8250::<1>::new(&mut fake_registers as *mut u8 as usize) }; + let uart = unsafe { MmioUart8250::::new(&mut fake_registers as *mut u8 as usize) }; // First try to read when there is nothing available. assert_eq!(uart.read_byte(), None); From bc35b9b68272eee757d4aed7abd0c9fa448df6aa Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Sun, 17 Sep 2023 17:40:33 +0800 Subject: [PATCH 4/5] update bitflags version --- Cargo.lock | 10 ++++++++-- uart8250/Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56ea37d..c7ee68a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "embedded-hal" version = "0.2.7" @@ -37,7 +43,7 @@ checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" name = "uart8250" version = "0.6.0" dependencies = [ - "bitflags", + "bitflags 2.4.0", "embedded-hal", "nb 1.0.0", "volatile-register", @@ -47,7 +53,7 @@ dependencies = [ name = "uart_xilinx" version = "0.1.0" dependencies = [ - "bitflags", + "bitflags 1.2.1", "volatile-register", ] diff --git a/uart8250/Cargo.toml b/uart8250/Cargo.toml index e139a20..63c425e 100644 --- a/uart8250/Cargo.toml +++ b/uart8250/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bitflags = "1" +bitflags = "2" embedded-hal = { version = "0.2.7", optional = true } nb = { version = "1.0.0", optional = true } volatile-register = "0.2" From f9f3e96a2feaae59ec0e18fc0f56e2bc798edcfc Mon Sep 17 00:00:00 2001 From: Godones <1925466036@qq.com> Date: Mon, 8 Jul 2024 18:46:39 +0800 Subject: [PATCH 5/5] hide unsafe code --- uart8250/src/lib.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/uart8250/src/lib.rs b/uart8250/src/lib.rs index a142387..4466631 100644 --- a/uart8250/src/lib.rs +++ b/uart8250/src/lib.rs @@ -11,4 +11,37 @@ This crate provides a struct with many methods to operate an 8250 UART. mod registers; mod uart; -pub use uart::{ChipFifoInfo, InterruptType, MmioUart8250, Parity, TransmitError}; +use core::ops::{Deref, DerefMut}; +pub use uart::{ChipFifoInfo, InterruptType, Parity, TransmitError}; +use crate::registers::{Register}; + +pub struct MmioUart8250(uart::MmioUart8250<'static, R>); + + +unsafe impl Send for MmioUart8250 {} +unsafe impl Sync for MmioUart8250 {} + + +impl MmioUart8250 { + pub fn new(base_addr: usize) -> Self { + let uart_raw = unsafe { uart::MmioUart8250::::new(base_addr) }; + MmioUart8250(uart_raw) + } + pub fn set_base_address(&mut self, base_address: usize) { + unsafe { self.0.set_base_address(base_address); } + } +} + +impl Deref for MmioUart8250 { + type Target = uart::MmioUart8250<'static, u32>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for MmioUart8250 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} \ No newline at end of file