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}