zephyr/
printk.rs

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