use crate::{IoCtl, Simulator}; use std::{cell::UnsafeCell, ptr::NonNull}; impl Simulator { pub fn console(&mut self, id: char) -> Console { let mut flags: u32 = 0; // SAFETY: `IoCtl::UartGetFlags` requires parameter of type `u32`, which is the case // here. let status = unsafe { self.ioctl(IoCtl::UartGetFlags { uart: id }, &mut flags) }; assert!(status == 0); // We detach the UART from the standard output. flags &= !simavr_ffi::AVR_UART_FLAG_STDIO; // SAFETY: `IoCtl::UartSetFlags` requires parameter of type `u32`, which is the case // here. unsafe { self.ioctl(IoCtl::UartSetFlags { uart: id }, &mut flags); } let irq_output = self.io_getirq( IoCtl::UartGetIrq { uart: id }, simavr_ffi::UART_IRQ_OUTPUT, ); let state = State::default().leak(); // SAFETY: Callback matches the expected IRQ. unsafe { Self::irq_register_notify( irq_output, Some(Console::uart_output_irq_hook), state.as_ptr(), ); } Console { state } } } pub struct Console { state: NonNull, } #[derive(Default)] struct State { rx: UnsafeCell>, } impl State { fn leak(self) -> NonNull { NonNull::new(Box::into_raw(Box::new(self))).unwrap() } } impl State { const RX_BUFFER_MAX_BYTES: usize = 128 * 1024; /// # Safety /// /// Cannot be called simultaneously with [`Self::flush_rx`]. unsafe fn push_rx(&self, value: u8) { let rx = &mut *self.rx.get(); if rx.len() < Self::RX_BUFFER_MAX_BYTES { rx.push(value); } } /// # Safety /// /// Cannot be called simultaneously with [`Self::push_rx`]. unsafe fn flush_rx(&self, f: F) -> O where F: FnOnce(&[u8]) -> O, { let rx = &mut *self.rx.get(); let output = f(&rx); rx.clear(); output } } impl Console { pub fn flush(&mut self, f: F) -> O where F: FnOnce(&[u8]) -> O, { // SAFETY: `self.state` is alive as long as `self`. let state = unsafe { self.state.as_ref() }; // SAFETY: `&mut self` ensures that while we are working, simavr won't interrupt us. unsafe { state.flush_rx(f) } } } impl Console { unsafe extern "C" fn uart_output_irq_hook( _: NonNull, value: u32, state: *mut State, ) { let state = state.as_mut().unwrap(); state.push_rx(value as u8); } }