legacy_stdio.rs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //! 这个模块的两个宏应该公开
  2. //! 如果制造实例的时候,给定了stdout,那么就会打印到这个stdout里面
  3. use crate::util::AmoMutex;
  4. use alloc::boxed::Box;
  5. use embedded_hal::serial::{Read, Write};
  6. use nb::block;
  7. /// Legacy standard input/output
  8. pub trait LegacyStdio: Send {
  9. /// Get a character from legacy stdin
  10. fn getchar(&mut self) -> u8;
  11. /// Put a character into legacy stdout
  12. fn putchar(&mut self, ch: u8);
  13. }
  14. /// Use serial in `embedded-hal` as legacy standard input/output
  15. struct EmbeddedHalSerial<T> {
  16. inner: T,
  17. }
  18. impl<T> EmbeddedHalSerial<T> {
  19. /// Create a wrapper with a value
  20. #[inline]
  21. fn new(inner: T) -> Self {
  22. Self { inner }
  23. }
  24. }
  25. impl<T: Send> LegacyStdio for EmbeddedHalSerial<T>
  26. where
  27. T: Read<u8> + Write<u8>,
  28. {
  29. #[inline]
  30. fn getchar(&mut self) -> u8 {
  31. // 直接调用embedded-hal里面的函数
  32. // 关于unwrap:因为这个是legacy函数,这里没有详细的处理流程,就panic掉
  33. block!(self.inner.read()).ok().unwrap()
  34. }
  35. #[inline]
  36. fn putchar(&mut self, ch: u8) {
  37. // 直接调用函数写一个字节
  38. block!(self.inner.write(ch)).ok();
  39. // 写一次flush一次,因为是legacy,就不考虑效率了
  40. block!(self.inner.flush()).ok();
  41. }
  42. }
  43. struct Fused<T, R>(T, R);
  44. // 和上面的原理差不多,就是分开了
  45. impl<T, R> LegacyStdio for Fused<T, R>
  46. where
  47. T: Write<u8> + Send + 'static,
  48. R: Read<u8> + Send + 'static,
  49. {
  50. #[inline]
  51. fn getchar(&mut self) -> u8 {
  52. block!(self.1.read()).ok().unwrap()
  53. }
  54. #[inline]
  55. fn putchar(&mut self, ch: u8) {
  56. block!(self.0.write(ch)).ok();
  57. block!(self.0.flush()).ok();
  58. }
  59. }
  60. static LEGACY_STDIO: AmoMutex<Option<Box<dyn LegacyStdio>>> = AmoMutex::new(None);
  61. #[doc(hidden)] // use through a macro
  62. pub fn init_legacy_stdio_embedded_hal<T: Read<u8> + Write<u8> + Send + 'static>(serial: T) {
  63. let serial = EmbeddedHalSerial::new(serial);
  64. *LEGACY_STDIO.lock() = Some(Box::new(serial));
  65. }
  66. #[doc(hidden)] // use through a macro
  67. pub fn init_legacy_stdio_embedded_hal_fuse<T, R>(tx: T, rx: R)
  68. where
  69. T: Write<u8> + Send + 'static,
  70. R: Read<u8> + Send + 'static,
  71. {
  72. let serial = Fused(tx, rx);
  73. *LEGACY_STDIO.lock() = Some(Box::new(serial));
  74. }
  75. #[inline]
  76. pub fn legacy_stdio_putchar(ch: u8) {
  77. if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
  78. stdio.putchar(ch)
  79. }
  80. }
  81. #[inline]
  82. pub fn legacy_stdio_getchar() -> usize {
  83. if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
  84. stdio.getchar() as usize
  85. } else {
  86. // According to RISC-V SBI spec 0.3.1-rc1, Section 4.3, this function returns -1
  87. // when fails to read from debug console. Thank you @duskmoon314
  88. usize::from_ne_bytes(isize::to_ne_bytes(-1))
  89. }
  90. }
  91. use core::fmt;
  92. struct Stdout;
  93. impl fmt::Write for Stdout {
  94. #[inline]
  95. fn write_str(&mut self, s: &str) -> fmt::Result {
  96. if let Some(stdio) = LEGACY_STDIO.lock().as_mut() {
  97. for byte in s.as_bytes() {
  98. stdio.putchar(*byte)
  99. }
  100. }
  101. Ok(())
  102. }
  103. }
  104. #[inline]
  105. #[doc(hidden)]
  106. pub fn _print(args: fmt::Arguments) {
  107. use fmt::Write;
  108. Stdout.write_fmt(args).unwrap();
  109. }
  110. /// Prints to the legacy debug console.
  111. ///
  112. /// This is only supported when there exists legacy extension;
  113. /// otherwise platform caller should use an early kernel input/output device
  114. /// declared in platform specific hardware.
  115. #[macro_export]
  116. macro_rules! print {
  117. ($($arg:tt)*) => ($crate::legacy_stdio::_print(core::format_args!($($arg)*)));
  118. }
  119. /// Prints to the legacy debug console, with a newline.
  120. ///
  121. /// This is only supported when there exists legacy extension;
  122. /// otherwise platform caller should use an early kernel input/output device
  123. /// declared in platform specific hardware.
  124. #[macro_export]
  125. macro_rules! println {
  126. () => ($crate::print!("\r\n"));
  127. ($($arg:tt)*) => {
  128. $crate::legacy_stdio::_print(core::format_args!($($arg)*));
  129. $crate::print!("\r\n");
  130. }
  131. }