Browse Source

support new embedded_hal serial API

Signed-off-by: Daniel Maslowski <[email protected]>
Daniel Maslowski 2 years ago
parent
commit
cebd1b29ea
4 changed files with 105 additions and 10 deletions
  1. 10 3
      Cargo.toml
  2. 8 1
      src/ecall/mod.rs
  3. 86 6
      src/legacy_stdio.rs
  4. 1 0
      src/lib.rs

+ 10 - 3
Cargo.toml

@@ -15,17 +15,24 @@ keywords = ["riscv", "sbi", "rustsbi"]
 categories = ["os", "embedded", "hardware-support", "no-std"]
 edition = "2021"
 
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
 [dependencies]
 riscv = "0.8"
 sbi-spec = { git = "https://github.com/rustsbi/sbi-spec.git", rev = "9d728bb" }
+# The following two dependencies are used to support legacy console feature
+embedded-hal = { version = "1.0.0-alpha.8", optional = true }
+nb = { version = "1.0", optional = true }
 
 [features]
 default = []
+# Support legacy extension; this feature is not included by default.
+legacy = ["embedded-hal", "nb"]
 # Dynamic pointer widths on SBI implementations; useful for developing hypervisors
 guest = []
 
 [package.metadata.docs.rs]
 default-target = "riscv64imac-unknown-none-elf"
-targets = ["riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf"]
+targets = [
+    "riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf",
+]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

+ 8 - 1
src/ecall/mod.rs

@@ -16,8 +16,10 @@ mod srst;
 mod pmu;
 
 use crate::{
-    ipi::send_ipi_many, legacy_stdio_getchar, legacy_stdio_putchar, reset::legacy_reset, HartMask,
+    ipi::send_ipi_many, reset::legacy_reset, HartMask,
 };
+#[cfg(feature = "legacy")]
+use crate::{legacy_stdio_getchar, legacy_stdio_putchar};
 use sbi_spec::{self as spec, binary::SbiRet};
 
 /// Supervisor environment call handler function
@@ -84,6 +86,7 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> Sbi
         // handle legacy callings.
         //
         // legacy 调用不使用 a1 返回值,总把 a1 填回 `SbiRet::value` 来模拟非 legacy 的行为。
+        #[cfg(feature = "legacy")]
         spec::legacy::LEGACY_SET_TIMER => {
             match () {
                 #[cfg(target_pointer_width = "64")]
@@ -96,6 +99,7 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> Sbi
                 value: param[1],
             }
         }
+        #[cfg(feature = "legacy")]
         spec::legacy::LEGACY_CONSOLE_PUTCHAR => {
             legacy_stdio_putchar(param[0] as _);
             SbiRet {
@@ -103,10 +107,12 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> Sbi
                 value: param[1],
             }
         }
+        #[cfg(feature = "legacy")]
         spec::legacy::LEGACY_CONSOLE_GETCHAR => SbiRet {
             error: legacy_stdio_getchar(),
             value: param[1],
         },
+        #[cfg(feature = "legacy")]
         spec::legacy::LEGACY_SEND_IPI => {
             send_ipi_many(unsafe { HartMask::legacy_from_addr(param[0]) });
             SbiRet {
@@ -114,6 +120,7 @@ pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> Sbi
                 value: param[1],
             }
         }
+        #[cfg(feature = "legacy")]
         spec::legacy::LEGACY_SHUTDOWN => legacy_reset(),
         _ => SbiRet::not_supported(),
     }

+ 86 - 6
src/legacy_stdio.rs

@@ -1,8 +1,10 @@
 //! 这个模块的两个宏应该公开
 //! 如果制造实例的时候,给定了stdout,那么就会打印到这个stdout里面
-
 use crate::util::AmoOnceRef;
 use core::fmt;
+use alloc::boxed::Box;
+use embedded_hal::serial::nb::{Read, Write};
+use nb::block;
 
 /// Legacy standard input/output
 pub trait LegacyStdio: Send + Sync {
@@ -18,25 +20,103 @@ pub trait LegacyStdio: Send + Sync {
     }
 }
 
-static STDIO: AmoOnceRef<dyn LegacyStdio> = AmoOnceRef::new();
+
+/// Legacy standard input/output
+pub trait LegacyStdio: Send {
+    /// Get a character from legacy stdin
+    fn getchar(&mut self) -> u8;
+    /// Put a character into legacy stdout
+    fn putchar(&mut self, ch: u8);
+}
+
+/// Use serial in `embedded-hal` as legacy standard input/output
+struct EmbeddedHalSerial<T> {
+    inner: T,
+}
+
+impl<T> EmbeddedHalSerial<T> {
+    /// Create a wrapper with a value
+    #[inline]
+    fn new(inner: T) -> Self {
+        Self { inner }
+    }
+}
+
+impl<T: Send> LegacyStdio for EmbeddedHalSerial<T>
+where
+    T: Read<u8> + Write<u8>,
+{
+    #[inline]
+    fn getchar(&mut self) -> u8 {
+        // 直接调用embedded-hal里面的函数
+        // 关于unwrap:因为这个是legacy函数,这里没有详细的处理流程,就panic掉
+        block!(self.inner.read()).ok().unwrap()
+    }
+
+    #[inline]
+    fn putchar(&mut self, ch: u8) {
+        // 直接调用函数写一个字节
+        block!(self.inner.write(ch)).ok();
+        // 写一次flush一次,因为是legacy,就不考虑效率了
+        block!(self.inner.flush()).ok();
+    }
+}
+
+struct Fused<T, R>(T, R);
+
+// 和上面的原理差不多,就是分开了
+impl<T, R> LegacyStdio for Fused<T, R>
+where
+    T: Write<u8> + Send + 'static,
+    R: Read<u8> + Send + 'static,
+{
+    #[inline]
+    fn getchar(&mut self) -> u8 {
+        block!(self.1.read()).ok().unwrap()
+    }
+
+    #[inline]
+    fn putchar(&mut self, ch: u8) {
+        block!(self.0.write(ch)).ok();
+        block!(self.0.flush()).ok();
+    }
+}
+
+static LEGACY_STDIO: AmoOnceRef<dyn LegacyStdio> = AmoOnceRef::new();
 
 #[inline]
 pub fn init_legacy_stdio(stdio: &'static dyn LegacyStdio) {
-    if !STDIO.try_call_once(stdio) {
+    if !LEGACY_STDIO.try_call_once(stdio) {
         panic!("load sbi module when already loaded")
     }
 }
 
+#[doc(hidden)] // use through a macro
+pub fn init_legacy_stdio_embedded_hal<T: Read<u8> + Write<u8> + Send + 'static>(serial: T) {
+    let serial = EmbeddedHalSerial::new(serial);
+    *LEGACY_STDIO.lock() = Some(Box::new(serial));
+}
+
+#[doc(hidden)] // use through a macro
+pub fn init_legacy_stdio_embedded_hal_fuse<T, R>(tx: T, rx: R)
+where
+    T: Write<u8> + Send + 'static,
+    R: Read<u8> + Send + 'static,
+{
+    let serial = Fused(tx, rx);
+    *LEGACY_STDIO.lock() = Some(Box::new(serial));
+}
+
 #[inline]
 pub fn legacy_stdio_putchar(ch: u8) {
-    if let Some(stdio) = STDIO.get() {
+    if let Some(stdio) = LEGACY_STDIO.get() {
         stdio.putchar(ch)
     }
 }
 
 #[inline]
 pub fn legacy_stdio_getchar() -> usize {
-    if let Some(stdio) = STDIO.get() {
+    if let Some(stdio) = LEGACY_STDIO.get() {
         stdio.getchar() as usize
     } else {
         // According to RISC-V SBI spec 0.3.1-rc1, Section 4.3, this function returns -1
@@ -50,7 +130,7 @@ struct Stdout;
 impl fmt::Write for Stdout {
     #[inline]
     fn write_str(&mut self, s: &str) -> fmt::Result {
-        if let Some(stdio) = STDIO.get() {
+        if let Some(stdio) = LEGACY_STDIO.get() {
             stdio.write_str(s);
         }
         Ok(())

+ 1 - 0
src/lib.rs

@@ -217,6 +217,7 @@ pub use ecall::handle_ecall as ecall;
 pub use hart_mask::HartMask;
 pub use hsm::{init_hsm, Hsm};
 pub use ipi::{init_ipi, Ipi};
+#[cfg(feature = "legacy")]
 #[doc(hidden)]
 pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
 pub use pmu::{init_pmu, Pmu};