Merge pull request #2414 from JomerDev/usb-logger-without-device

Adds function and macro to create usb logger without device (also fixes a logger issue)
This commit is contained in:
Ulf Lilleengen
2024-02-12 20:04:57 +00:00
committed by GitHub
2 changed files with 184 additions and 17 deletions

View File

@@ -6,7 +6,7 @@ use core::fmt::Write as _;
use embassy_futures::join::join;
use embassy_sync::pipe::Pipe;
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
use embassy_usb::driver::Driver;
use embassy_usb::{Builder, Config};
use log::{Metadata, Record};
@@ -37,6 +37,9 @@ impl<'d> LoggerState<'d> {
}
}
/// The packet size used in the usb logger, to be used with `create_future_from_class`
pub const MAX_PACKET_SIZE: u8 = 64;
/// The logger handle, which contains a pipe with configurable size for buffering log messages.
pub struct UsbLogger<const N: usize> {
buffer: Pipe<CS, N>,
@@ -54,7 +57,6 @@ impl<const N: usize> UsbLogger<N> {
D: Driver<'d>,
Self: 'd,
{
const MAX_PACKET_SIZE: u8 = 64;
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-serial logger");
@@ -87,22 +89,46 @@ impl<const N: usize> UsbLogger<N> {
let mut device = builder.build();
loop {
let run_fut = device.run();
let log_fut = async {
let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
sender.wait_connection().await;
loop {
let len = self.buffer.read(&mut rx[..]).await;
let _ = sender.write_packet(&rx[..len]).await;
let class_fut = self.run_logger_class(&mut sender, &mut receiver);
join(run_fut, class_fut).await;
}
}
async fn run_logger_class<'d, D>(&self, sender: &mut Sender<'d, D>, receiver: &mut Receiver<'d, D>)
where
D: Driver<'d>,
{
let log_fut = async {
let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
sender.wait_connection().await;
loop {
let len = self.buffer.read(&mut rx[..]).await;
let _ = sender.write_packet(&rx[..len]).await;
if len as u8 == MAX_PACKET_SIZE {
let _ = sender.write_packet(&[]).await;
}
};
let discard_fut = async {
let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
receiver.wait_connection().await;
loop {
let _ = receiver.read_packet(&mut discard_buf).await;
}
};
join(run_fut, join(log_fut, discard_fut)).await;
}
};
let discard_fut = async {
let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
receiver.wait_connection().await;
loop {
let _ = receiver.read_packet(&mut discard_buf).await;
}
};
join(log_fut, discard_fut).await;
}
/// Creates the futures needed for the logger from a given class
/// This can be used in cases where the usb device is already in use for another connection
pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>)
where
D: Driver<'d>,
{
let (mut sender, mut receiver) = class.split();
loop {
self.run_logger_class(&mut sender, &mut receiver).await;
}
}
}
@@ -153,3 +179,27 @@ macro_rules! run {
let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
};
}
/// Initialize the USB serial logger from a serial class and return the future to run it.
///
/// Arguments specify the buffer size, log level and the serial class, respectively.
///
/// # Usage
///
/// ```
/// embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, class);
/// ```
///
/// # Safety
///
/// This macro should only be invoked only once since it is setting the global logging state of the application.
#[macro_export]
macro_rules! with_class {
( $x:expr, $l:expr, $p:ident ) => {{
static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
unsafe {
let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
}
LOGGER.create_future_from_class($p)
}};
}