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();
}