Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions machine/cortex-m/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,14 @@ fn main() {
return;
}

println!("cargo::rerun-if-env-changed=OSIRIS_DEBUG_UART");
if env::var_os("OSIRIS_DEBUG_UART").is_some() {
println!(
"cargo::error=OSIRIS_DEBUG_UART is removed; the console UART is now driven by `chosen.osiris,console` in the device tree. Drop the env var from your preset / .cargo/config.toml and pick the console in DTS."
);
std::process::exit(1);
}

let dts = hal_builder::dt::check_dts()
.expect("No DeviceTree specified. Set OSIRIS_DTS_PATH to specify.");
let out = hal_builder::read_path_env("OUT_DIR");
Expand Down
15 changes: 7 additions & 8 deletions machine/cortex-m/src/native.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::ffi::c_char;

pub use hal_api::*;

pub mod asm;
Expand All @@ -9,6 +7,7 @@ pub mod i2c;
pub mod panic;
pub mod sched;
pub mod spi;
pub mod uart;

mod crit;
mod print;
Expand Down Expand Up @@ -39,20 +38,20 @@ impl hal_api::Machinelike for ArmMachine {
fn init() {
unsafe {
bindings::init_hal();
bindings::init_debug_uart();
}
let _ = uart::init_console_from_dt();
unsafe {
bindings::dwt_init();
}
}

fn print(s: &str) -> Result<()> {
let state = asm::disable_irq_save();

if (unsafe { bindings::write_debug_uart(s.as_ptr() as *const c_char, s.len() as i32) } != 0)
{
asm::enable_irq_restr(state);
let ok = uart::console_write(s.as_bytes()).is_ok();
asm::enable_irq_restr(state);
if ok {
Ok(())
} else {
asm::enable_irq_restr(state);
Err(hal_api::PosixError::EIO)
}
}
Expand Down
234 changes: 234 additions & 0 deletions machine/cortex-m/src/native/uart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use super::bindings;
use super::device_tree;

pub(crate) fn console_entry() -> Option<&'static device_tree::UartRegistryEntry> {
device_tree::CONSOLE_UART.map(|i| &device_tree::UART_REGISTRY[i])
}

pub(crate) fn init_console_from_dt() -> Result<()> {
let Some(entry) = console_entry() else {
return Ok(());
};
let cfg = cfg_from_entry(entry, &Overrides::default());
let rc = unsafe { bindings::uart_init_console(&cfg as *const _) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

pub(crate) fn console_write(buf: &[u8]) -> Result<()> {
let Some(entry) = console_entry() else {
return Ok(());
};
if buf.is_empty() {
return Ok(());
}
// HAL_MAX_DELAY: console writes never give up.
let rc = unsafe {
bindings::uart_transmit_blocking(entry.instance, buf.as_ptr(), buf.len() as i32, u32::MAX)
};
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
InvalidArgument,
NoSuchDevice,
NotInitialized,
OutOfMemory,
Busy,
WouldBlock,
TimedOut,
Io,
}

pub type Result<T> = core::result::Result<T, Error>;

fn from_c_rc(rc: i32) -> Error {
match rc {
-1 => Error::InvalidArgument,
-2 => Error::OutOfMemory,
-3 => Error::Busy,
-4 => Error::WouldBlock,
-5 => Error::Io,
_ => Error::Io,
}
}

#[derive(Clone, Copy)]
pub struct Device(&'static device_tree::UartRegistryEntry);

impl Device {
pub fn entry(&self) -> &'static device_tree::UartRegistryEntry {
self.0
}

pub fn index(&self) -> u8 {
self.0.index
}

pub fn instance(&self) -> usize {
self.0.instance
}

pub fn irqn(&self) -> u8 {
self.0.irqn
}
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Irq {
Rx = 0,
TxDone = 1,
}

pub type IrqHandler = extern "C" fn(Irq, *mut ());

pub fn get_by_index(idx: u8) -> Result<Device> {
device_tree::uart_by_index(idx)
.map(Device)
.ok_or(Error::NoSuchDevice)
}

pub fn get(compatible: &str, ordinal: usize) -> Result<Device> {
device_tree::uart_by_compatible(compatible, ordinal)
.map(Device)
.ok_or(Error::NoSuchDevice)
}

fn cfg_from_entry(
entry: &device_tree::UartRegistryEntry,
overrides: &Overrides,
) -> bindings::uart_bus_cfg_t {
let mk_pin = |port: usize, line: u8, af: u8| bindings::uart_pin_cfg_t {
port,
pin: line,
af,
reserved: 0,
};
let zero_pin = bindings::uart_pin_cfg_t {
port: 0,
pin: 0,
af: 0,
reserved: 0,
};
let rts = entry
.rts
.map(|p| mk_pin(p.port, p.line, p.af))
.unwrap_or(zero_pin);
let cts = entry
.cts
.map(|p| mk_pin(p.port, p.line, p.af))
.unwrap_or(zero_pin);

bindings::uart_bus_cfg_t {
instance: entry.instance,
tx: mk_pin(entry.tx.port, entry.tx.line, entry.tx.af),
rx: mk_pin(entry.rx.port, entry.rx.line, entry.rx.af),
rts,
cts,
baud: overrides.baud.unwrap_or(entry.baud),
data_bits: overrides.data_bits.unwrap_or(entry.data_bits),
stop_bits: overrides.stop_bits.unwrap_or(entry.stop_bits),
parity: overrides.parity.unwrap_or(entry.parity),
flow_control: overrides.flow_control.unwrap_or(entry.flow_control),
irqn: entry.irqn,
priority: entry.priority,
}
}

#[derive(Clone, Copy, Default)]
pub struct Overrides {
pub baud: Option<u32>,
pub data_bits: Option<u8>,
pub stop_bits: Option<u8>,
pub parity: Option<u8>,
pub flow_control: Option<u8>,
}

pub fn init(dev: &Device, overrides: &Overrides) -> Result<()> {
let cfg = cfg_from_entry(dev.0, overrides);
let rc = unsafe { bindings::uart_init(&cfg as *const _) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

pub fn deinit(dev: &Device) -> Result<()> {
let rc = unsafe { bindings::uart_deinit(dev.0.instance) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

pub fn transmit_blocking(dev: &Device, buf: &[u8], timeout_ms: u32) -> Result<()> {
if buf.is_empty() {
return Ok(());
}
let rc = unsafe {
bindings::uart_transmit_blocking(
dev.0.instance,
buf.as_ptr(),
buf.len() as i32,
timeout_ms,
)
};
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

pub fn transmit_nb(dev: &Device, buf: &[u8]) -> Result<usize> {
if buf.is_empty() {
return Ok(0);
}
let rc =
unsafe { bindings::uart_transmit_nb(dev.0.instance, buf.as_ptr(), buf.len() as i32) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(rc as usize)
}

pub fn receive_nb(dev: &Device, buf: &mut [u8]) -> Result<usize> {
if buf.is_empty() {
return Ok(0);
}
let rc =
unsafe { bindings::uart_receive_nb(dev.0.instance, buf.as_mut_ptr(), buf.len() as i32) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(rc as usize)
}

pub fn register_irq_handler(
dev: &Device,
handler: Option<IrqHandler>,
ctx: *mut (),
) -> Result<()> {
// SAFETY: `IrqHandler = extern "C" fn(Irq, *mut ())` is ABI-compatible
// with `uart_irq_handler_fn` because `Irq` is `#[repr(u32)]` and `ctx`
// is `*mut () ↔ void *`.
let raw: bindings::uart_irq_handler_fn = unsafe { core::mem::transmute(handler) };
let rc =
unsafe { bindings::uart_set_irq_handler(dev.0.instance, raw, ctx as *mut core::ffi::c_void) };
if rc < 0 {
return Err(from_c_rc(rc));
}
Ok(())
}

pub fn dispatch_by_slot(slot: u8) {
unsafe {
bindings::uart_dispatch_by_slot(slot);
}
}
1 change: 1 addition & 0 deletions machine/cortex-m/src/stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod device_tree;
pub mod i2c;
pub mod sched;
pub mod spi;
pub mod uart;

pub type Machine = StubMachine;
pub type Stack = sched::StubStack;
Expand Down
98 changes: 98 additions & 0 deletions machine/cortex-m/src/stub/uart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
InvalidArgument,
NoSuchDevice,
NotInitialized,
OutOfMemory,
Busy,
WouldBlock,
TimedOut,
Io,
}

pub type Result<T> = core::result::Result<T, Error>;

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Irq {
Rx = 0,
TxDone = 1,
}

pub type IrqHandler = extern "C" fn(Irq, *mut ());

#[derive(Clone, Copy, Default)]
pub struct Overrides {
pub baud: Option<u32>,
pub data_bits: Option<u8>,
pub stop_bits: Option<u8>,
pub parity: Option<u8>,
pub flow_control: Option<u8>,
}

#[derive(Clone, Copy)]
pub struct Device;

impl Device {
pub fn index(&self) -> u8 {
0
}
pub fn instance(&self) -> usize {
0
}
pub fn irqn(&self) -> u8 {
0
}
}

pub fn get_by_index(_idx: u8) -> Result<Device> {
Err(Error::NoSuchDevice)
}

pub fn get(_compatible: &str, _ordinal: usize) -> Result<Device> {
Err(Error::NoSuchDevice)
}

pub fn init(_dev: &Device, _overrides: &Overrides) -> Result<()> {
Err(Error::NotInitialized)
}

pub fn deinit(_dev: &Device) -> Result<()> {
Err(Error::NotInitialized)
}

pub fn transmit_blocking(_dev: &Device, _buf: &[u8], _timeout_ms: u32) -> Result<()> {
Err(Error::NotInitialized)
}

pub fn transmit_nb(_dev: &Device, _buf: &[u8]) -> Result<usize> {
Err(Error::NotInitialized)
}

pub fn receive_nb(_dev: &Device, _buf: &mut [u8]) -> Result<usize> {
Err(Error::NotInitialized)
}

pub fn register_irq_handler(
_dev: &Device,
_handler: Option<IrqHandler>,
_ctx: *mut (),
) -> Result<()> {
Err(Error::NotInitialized)
}

pub fn dispatch_by_slot(_slot: u8) {}

/// Mirror of the `hal_arm::uart::console_entry` API; testing has no
/// device-tree-driven console, so always returns `None`.
pub fn console_entry() -> Option<&'static ()> {
None
}

pub fn init_console_from_dt() -> Result<()> {
Ok(())
}

pub fn console_write(_buf: &[u8]) -> Result<()> {
Ok(())
}
Loading
Loading