Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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