zephyr/printk.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
// Copyright (c) 2024 Linaro LTD
// SPDX-License-Identifier: Apache-2.0
//! Printk implementation for Rust.
//!
//! This uses the `k_str_out` syscall, which is part of printk to output to the console.
use core::fmt::{
Arguments,
Result,
Write,
write,
};
/// Print to Zephyr's console, without a newline.
///
/// This macro uses the same syntax as std's
/// [`format!`](https://doc.rust-lang.org/stable/std/fmt/index.html), but writes to the Zephyr
/// console instead.
///
/// if `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation,
/// and due to private accessors in the Zephyr printk implementation, the lock is only over groups
/// of a small buffer size. This buffer must be kept fairly small, as it resides on the stack.
#[macro_export]
macro_rules! printk {
($($arg:tt)*) => {{
$crate::printk::printk(format_args!($($arg)*));
}};
}
/// Print to Zephyr's console, with a newline.
///
/// This macro uses the same syntax as std's
/// [`format!`](https://doc.rust-lang.org/stable/std/fmt/index.html), but writes to the Zephyr
/// console instead.
///
/// If `CONFIG_PRINTK_SYNC` is enabled, this locks during printing. However, to avoid allocation,
/// and due to private accessors in the Zephyr printk implementation, the lock is only over groups
/// of a small buffer size. This buffer must be kept fairly small, as it resides on the stack.
///
/// [`format!`]: alloc::format
#[macro_export]
macro_rules! printkln {
($($arg:tt)*) => {{
$crate::printk::printkln(format_args!($($arg)*));
}};
}
// This could readily be optimized for the configuration where we don't have userspace, as well as
// when we do, and are not running in userspace. This initial implementation will always use a
// string buffer, as it doesn't depend on static symbols in print.c.
//
// A consequence is that the CONFIG_PRINTK_SYNC is enabled, the synchonization will only occur at
// the granularity of these buffers rather than at the print message level.
/// The buffer size for syscall. This is a tradeoff between efficiency (large buffers need fewer
/// syscalls) and needing more stack space.
const BUF_SIZE: usize = 32;
struct Context {
// How many characters are used in the buffer.
count: usize,
// Bytes written.
buf: [u8; BUF_SIZE],
}
fn utf8_byte_length(byte: u8) -> usize {
if byte & 0b1000_0000 == 0 {
// Single byte (0xxxxxxx)
1
} else if byte & 0b1110_0000 == 0b1100_0000 {
// Two-byte sequence (110xxxxx)
2
} else if byte & 0b1111_0000 == 0b1110_0000 {
// Three-byte sequence (1110xxxx)
3
} else if byte & 0b1111_1000 == 0b1111_0000 {
// Four-byte sequence (11110xxx)
4
} else {
// Continuation byte or invalid (10xxxxxx)
1
}
}
impl Context {
fn add_byte(&mut self, b: u8) {
// Ensure we have room for an entire UTF-8 sequence.
if self.count + utf8_byte_length(b) > self.buf.len() {
self.flush();
}
self.buf[self.count] = b;
self.count += 1;
}
fn flush(&mut self) {
if self.count > 0 {
unsafe {
zephyr_sys::k_str_out(self.buf.as_mut_ptr() as *mut i8, self.count);
}
self.count = 0;
}
}
}
impl Write for Context {
fn write_str(&mut self, s: &str) -> Result {
for b in s.bytes() {
self.add_byte(b);
}
Ok(())
}
}
#[doc(hidden)]
pub fn printk(args: Arguments<'_>) {
let mut context = Context {
count: 0,
buf: [0; BUF_SIZE],
};
write(&mut context, args).unwrap();
context.flush();
}
#[doc(hidden)]
pub fn printkln(args: Arguments<'_>) {
let mut context = Context {
count: 0,
buf: [0; BUF_SIZE],
};
write(&mut context, args).unwrap();
context.add_byte(b'\n');
context.flush();
}