| // Licensed under the Apache-2.0 license |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #![no_std] |
| |
| use ast10x0_peripherals::uart::Usart; |
| use embedded_io::{Read, Write}; |
| use usart_api::backend::{BackendError, IrqMask, LineStatus, Parity, UsartBackend, UsartConfig}; |
| |
| pub struct Ast10x0UsartBackend { |
| uart: Usart, |
| } |
| |
| impl Ast10x0UsartBackend { |
| /// Create a backend using UART5 (the standard debug UART for AST10x0). |
| /// |
| /// # Safety |
| /// |
| /// This function is safe because it uses the well-known UART5 base address |
| /// and will only be called once per process by the server loop. |
| pub fn new() -> Self { |
| // SAFETY: 0x7e78_4000 is UART5 MMIO base on AST10x0 (IRQ 8); this |
| // process is the sole owner of the mapping per |
| // target/ast10x0/usart/system.json5. |
| let uart = unsafe { Usart::new(0x7e78_4000 as *const _) }; |
| |
| // ELSI and EDSSI were also left enabled. EDSSI fires immediately if |
| // the MSR delta bits (DCTS/DDSR/TERI/DDCD) are set at init — QEMU |
| // sets them — causing a continuous IRQ that the server acks and |
| // unmasks in a tight loop, starving the IPC dispatch path. |
| // Read MSR to clear the delta bits, then disable all four IER sources. |
| uart.drain_modem_status(); |
| uart.disable_all_interrupts(); |
| |
| Self { uart } |
| } |
| } |
| |
| impl Default for Ast10x0UsartBackend { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
| |
| /// Stable type alias used by the server binary for compile-time backend selection. |
| pub type Backend = Ast10x0UsartBackend; |
| |
| impl UsartBackend for Ast10x0UsartBackend { |
| fn configure(&mut self, config: UsartConfig) -> Result<(), BackendError> { |
| // Current low-level driver supports a fixed 8N1 configuration and limited rates. |
| // Keep behavior explicit until richer runtime configuration is plumbed through. |
| if config.stop_bits != 1 { |
| return Err(BackendError::InvalidConfiguration); |
| } |
| if config.parity != Parity::None { |
| return Err(BackendError::InvalidConfiguration); |
| } |
| match config.baud_rate { |
| 9_600 | 19_200 | 1_500_000 => Ok(()), |
| _ => Err(BackendError::InvalidConfiguration), |
| } |
| } |
| |
| fn write(&mut self, data: &[u8]) -> Result<usize, BackendError> { |
| // Use embedded_io::Write to send bytes |
| match self.uart.write_all(data) { |
| Ok(()) => Ok(data.len()), |
| Err(_) => Err(BackendError::Timeout), |
| } |
| } |
| |
| fn read(&mut self, out: &mut [u8]) -> Result<usize, BackendError> { |
| match self.uart.read(out) { |
| Ok(n) => Ok(n), |
| Err(_) => Err(BackendError::InternalError), |
| } |
| } |
| |
| fn try_read(&mut self, out: &mut [u8]) -> Result<usize, BackendError> { |
| if out.is_empty() { |
| return Ok(0); |
| } |
| let count = self.uart.try_read_available(out); |
| if count == 0 { |
| Err(BackendError::WouldBlock) |
| } else { |
| Ok(count) |
| } |
| } |
| |
| fn line_status(&self) -> Result<LineStatus, BackendError> { |
| let status = self.uart.read_line_status(); |
| Ok(LineStatus(status.bits())) |
| } |
| |
| fn enable_interrupts(&mut self, mask: IrqMask) -> Result<(), BackendError> { |
| if mask.contains(IrqMask::RX_DATA_AVAILABLE) { |
| self.uart.set_rx_data_available_interrupt(); |
| } |
| if mask.contains(IrqMask::TX_IDLE) { |
| self.uart.set_tx_idle_interrupt(); |
| } |
| Ok(()) |
| } |
| |
| fn disable_interrupts(&mut self, mask: IrqMask) -> Result<(), BackendError> { |
| if mask.contains(IrqMask::RX_DATA_AVAILABLE) { |
| self.uart.clear_rx_data_available_interrupt(); |
| } |
| if mask.contains(IrqMask::TX_IDLE) { |
| self.uart.clear_tx_idle_interrupt(); |
| } |
| Ok(()) |
| } |
| } |