blob: d1356d6feaa3eebd15772d13cfc4f9d9a625c5ab [file]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
//! UART blocking and non-blocking transmit console test command.
use core::ffi::c_int;
use kprint::{kformat, kprintln};
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use crate::console::CmdArgs;
use crate::debuglog_rs::{dlog_serial_write, serial_write};
unsafe extern "C" {
fn cpp_global_prng_draw(buffer: *mut u8, len: usize);
}
/// Shared scratch buffer for formatting UART test lines.
///
/// Guarded by a `ksync::KMutex` so the 1,048-byte scratch buffer can remain in static storage off
/// the 8 KB kernel stack without `static mut` aliasing undefined behavior if `k uart_tests` is
/// invoked concurrently.
#[ksync::guarded]
struct UartTestBuffer {
/// Mutex serializing access to `buf`.
#[mutex]
lock: ksync::KMutex<crate::ksync_kernel::TrackedMutex>,
/// Scratch line buffer.
#[guarded_by(lock)]
buf: [u8; 1048],
}
/// Static UART test line buffer, guarded by `UartTestBuffer::lock` to keep the 1,048-byte buffer
/// off the kernel stack while preventing concurrent `static mut` aliasing.
static BUF: UartTestBuffer = UartTestBuffer {
lock: ksync::KMutex::new(crate::ksync_kernel::TrackedMutex::const_init(
<UartTestBufferLockClass as ksync::LockClass>::ID,
)),
buf: ksync::KCell::new([0u8; 1048]),
};
fn uart_blocking_print_test() {
ksync::lock!(let mut guard = BUF.lock_lock());
let buf = guard.as_mut().buf_mut();
for count in 0..5 {
let line = kformat!(
buf,
"Blocking Test Count {} (FIRST LINE)\n\
AND THIS SHOULD BE THE SECOND LINE Count {}\n",
count,
count
);
dlog_serial_write(line);
}
}
fn uart_nonblocking_print_test() {
ksync::lock!(let mut guard = BUF.lock_lock());
let buf = guard.as_mut().buf_mut();
for count in 0..5 {
let line = kformat!(
buf,
"NON-Blocking Test Count {} (FIRST LINE)\n\
AND THIS SHOULD BE THE SECOND LINE Count {}\n",
count,
count
);
serial_write(line);
}
}
fn uart_print_lots_of_lines(block: bool) {
const TOTAL_LINES: usize = 1024;
const MAX_SINGLE_LINE_LEN: i32 = 128;
const MIN_SINGLE_LINE_LEN: i32 = 80;
let mut seed = [0u8; 32];
// SAFETY: `cpp_global_prng_draw` writes `seed.len()` bytes into `seed`.
unsafe {
cpp_global_prng_draw(seed.as_mut_ptr(), seed.len());
}
let mut rng = SmallRng::from_seed(seed);
ksync::lock!(let mut guard = BUF.lock_lock());
let buf = guard.as_mut().buf_mut();
for count in 0..TOTAL_LINES {
let len = if block {
kformat!(&mut buf[..1024], "BLOCK LINE: {} ", count).len()
} else {
kformat!(&mut buf[..1024], "NON-BLOCK LINE: {} ", count).len()
};
// Pick a random line length between 80 and 128
let mut i = rng.random_range(MIN_SINGLE_LINE_LEN..MAX_SINGLE_LINE_LEN);
i -= len as i32; // Less what we've already snprintf'ed above
let mut s = len;
while i > 0 {
i -= 1;
if rng.random_ratio(1, 2) {
buf[s] = b'a' + rng.random_range(0u8..26);
} else {
buf[s] = b'A' + rng.random_range(0u8..26);
}
s += 1;
}
buf[s] = b'\n';
s += 1;
if block {
dlog_serial_write(&buf[..s]);
} else {
serial_write(&buf[..s]);
}
}
}
/// # Safety
///
/// `_argv` is not dereferenced; `unsafe` only to match the console command callback signature.
pub(super) unsafe extern "C" fn uart_tests(
_argc: c_int,
_argv: *const CmdArgs,
_flags: u32,
) -> c_int {
// Print out a few short lines, to test '\n' behavior
uart_blocking_print_test();
uart_nonblocking_print_test();
// Print a few long lines to test bulk output,
// both block and non-blocking
let mut i = 100;
while i > 0 {
i -= 1;
uart_print_lots_of_lines(true);
uart_print_lots_of_lines(false);
kprintln!("Printed Count = {}", 100 - i);
}
0
}