serial.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. use super::Port8;
  2. use crate::{
  3. kernel::{
  4. block::make_device, console::set_console, constants::EIO, interrupt::register_irq_handler,
  5. task::KernelStack, CharDevice, CharDeviceType, Terminal, TerminalDevice,
  6. },
  7. prelude::*,
  8. };
  9. use alloc::{collections::vec_deque::VecDeque, format, sync::Arc};
  10. use bitflags::bitflags;
  11. use core::pin::pin;
  12. use eonix_runtime::{run::FutureRun, scheduler::Scheduler};
  13. use eonix_sync::{SpinIrq as _, WaitList};
  14. bitflags! {
  15. struct LineStatus: u8 {
  16. const RX_READY = 0x01;
  17. const TX_READY = 0x20;
  18. }
  19. }
  20. #[allow(dead_code)]
  21. struct Serial {
  22. id: u32,
  23. name: Arc<str>,
  24. terminal: Spin<Option<Arc<Terminal>>>,
  25. worker_wait: WaitList,
  26. working: Spin<bool>,
  27. tx_buffer: Spin<VecDeque<u8>>,
  28. tx_rx: Port8,
  29. int_ena: Port8,
  30. int_ident: Port8,
  31. line_control: Port8,
  32. modem_control: Port8,
  33. line_status: Port8,
  34. modem_status: Port8,
  35. scratch: Port8,
  36. }
  37. impl Serial {
  38. const COM0_BASE: u16 = 0x3f8;
  39. const COM1_BASE: u16 = 0x2f8;
  40. const COM0_IRQ: u8 = 4;
  41. const COM1_IRQ: u8 = 3;
  42. fn enable_interrupts(&self) {
  43. // Enable interrupt #0: Received data available
  44. self.int_ena.write(0x03);
  45. }
  46. fn disable_interrupts(&self) {
  47. // Disable interrupt #0: Received data available
  48. self.int_ena.write(0x02);
  49. }
  50. fn line_status(&self) -> LineStatus {
  51. LineStatus::from_bits_truncate(self.line_status.read())
  52. }
  53. async fn wait_for_interrupt(&self) {
  54. let mut wait = pin!(self.worker_wait.prepare_to_wait());
  55. {
  56. let mut working = self.working.lock_irq();
  57. self.enable_interrupts();
  58. wait.as_mut().add_to_wait_list();
  59. *working = false;
  60. };
  61. wait.await;
  62. *self.working.lock_irq() = true;
  63. self.disable_interrupts();
  64. }
  65. async fn worker(port: Arc<Self>) {
  66. let terminal = port.terminal.lock().clone();
  67. loop {
  68. while port.line_status().contains(LineStatus::RX_READY) {
  69. let ch = port.tx_rx.read();
  70. if let Some(terminal) = terminal.as_ref() {
  71. terminal.commit_char(ch).await;
  72. }
  73. }
  74. let should_wait = {
  75. let mut tx_buffer = port.tx_buffer.lock();
  76. let mut count = 0;
  77. // Give it a chance to receive data.
  78. for &ch in tx_buffer.iter().take(64) {
  79. if port.line_status().contains(LineStatus::TX_READY) {
  80. port.tx_rx.write(ch);
  81. } else {
  82. break;
  83. }
  84. count += 1;
  85. }
  86. tx_buffer.drain(..count);
  87. tx_buffer.is_empty()
  88. };
  89. if should_wait {
  90. port.wait_for_interrupt().await;
  91. }
  92. }
  93. }
  94. pub fn new(id: u32, base_port: u16) -> KResult<Self> {
  95. let port = Self {
  96. id,
  97. name: Arc::from(format!("ttyS{id}")),
  98. terminal: Spin::new(None),
  99. worker_wait: WaitList::new(),
  100. working: Spin::new(true),
  101. tx_buffer: Spin::new(VecDeque::new()),
  102. tx_rx: Port8::new(base_port),
  103. int_ena: Port8::new(base_port + 1),
  104. int_ident: Port8::new(base_port + 2),
  105. line_control: Port8::new(base_port + 3),
  106. modem_control: Port8::new(base_port + 4),
  107. line_status: Port8::new(base_port + 5),
  108. modem_status: Port8::new(base_port + 6),
  109. scratch: Port8::new(base_port + 7),
  110. };
  111. port.int_ena.write(0x00); // Disable all interrupts
  112. port.line_control.write(0x80); // Enable DLAB (set baud rate divisor)
  113. port.tx_rx.write(0x00); // Set divisor to 0 (lo byte) 115200 baud rate
  114. port.int_ena.write(0x00); // 0 (hi byte)
  115. port.line_control.write(0x03); // 8 bits, no parity, one stop bit
  116. port.int_ident.write(0xc7); // Enable FIFO, clear them, with 14-byte threshold
  117. port.modem_control.write(0x0b); // IRQs enabled, RTS/DSR set
  118. port.modem_control.write(0x1e); // Set in loopback mode, test the serial chip
  119. port.tx_rx.write(0x19); // Test serial chip (send byte 0x19 and check if serial returns
  120. // same byte)
  121. if port.tx_rx.read() != 0x19 {
  122. return Err(EIO);
  123. }
  124. port.modem_control.write(0x0f); // Return to normal operation mode
  125. Ok(port)
  126. }
  127. fn wakeup_worker(&self) {
  128. let working = self.working.lock_irq();
  129. if !*working {
  130. self.worker_wait.notify_one();
  131. }
  132. }
  133. fn irq_handler(&self) {
  134. // Read the interrupt ID register to clear the interrupt.
  135. self.int_ident.read();
  136. self.wakeup_worker();
  137. }
  138. fn register_char_device(port: Self) -> KResult<()> {
  139. let port = Arc::new(port);
  140. let terminal = Terminal::new(port.clone());
  141. port.terminal.lock().replace(terminal.clone());
  142. {
  143. let port = port.clone();
  144. let irq_no = match port.id {
  145. 0 => Serial::COM0_IRQ,
  146. 1 => Serial::COM1_IRQ,
  147. _ => unreachable!(),
  148. };
  149. register_irq_handler(irq_no as i32, move || {
  150. port.irq_handler();
  151. })?;
  152. }
  153. Scheduler::get().spawn::<KernelStack, _>(FutureRun::new(Self::worker(port.clone())));
  154. let _ = set_console(terminal.clone());
  155. eonix_log::set_console(terminal.clone());
  156. CharDevice::register(
  157. make_device(4, 64 + port.id),
  158. port.name.clone(),
  159. CharDeviceType::Terminal(terminal),
  160. )?;
  161. Ok(())
  162. }
  163. }
  164. impl TerminalDevice for Serial {
  165. fn putchar(&self, ch: u8) {
  166. let mut tx_buffer = self.tx_buffer.lock();
  167. tx_buffer.push_back(ch);
  168. self.wakeup_worker();
  169. }
  170. fn putchar_direct(&self, ch: u8) {
  171. self.tx_rx.write(ch);
  172. }
  173. }
  174. pub fn init() -> KResult<()> {
  175. if let Ok(port) = Serial::new(0, Serial::COM0_BASE) {
  176. Serial::register_char_device(port)?;
  177. }
  178. if let Ok(port) = Serial::new(1, Serial::COM1_BASE) {
  179. Serial::register_char_device(port)?;
  180. }
  181. Ok(())
  182. }