Skip to content

Commit addfa44

Browse files
committed
feat(test_runner): pause the RP2040 test driver's output until the serial port is open
1 parent 24b8169 commit addfa44

File tree

3 files changed

+70
-29
lines changed

3 files changed

+70
-29
lines changed

src/r3_port_arm_m_test_driver/src/board_rp2040.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use core::panic::PanicInfo;
1+
use core::{
2+
panic::PanicInfo,
3+
sync::atomic::{AtomicBool, Ordering},
4+
};
25
use r3::kernel::{cfg::CfgBuilder, Kernel, StartupHook};
36
use r3_support_rp2040::usbstdio;
47

@@ -81,11 +84,14 @@ pub const fn configure<System: Kernel>(b: &mut CfgBuilder<System>) {
8184
usbstdio::configure::<_, Options>(b);
8285
}
8386

87+
static SHOULD_PAUSE_OUTPUT: AtomicBool = AtomicBool::new(true);
88+
8489
struct Options;
8590

8691
impl usbstdio::Options for Options {
8792
/// Handle USB serial input data.
8893
fn handle_input(s: &[u8]) {
94+
let mut should_unpause_output = false;
8995
for &b in s.iter() {
9096
match b {
9197
b'r' => {
@@ -96,17 +102,27 @@ impl usbstdio::Options for Options {
96102
.reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask);
97103
}
98104
b'g' => {
99-
// TODO: unblock the output? check if this is really necessary.
100-
// maybe we can get away with DTR
105+
should_unpause_output = true;
101106
}
102107
_ => {}
103108
}
104109
}
110+
111+
if should_unpause_output && SHOULD_PAUSE_OUTPUT.load(Ordering::Relaxed) {
112+
SHOULD_PAUSE_OUTPUT.store(false, Ordering::Relaxed);
113+
114+
// Flush the transmission buffer.
115+
usbstdio::poll::<Options>();
116+
}
105117
}
106118

107119
fn product_name() -> &'static str {
108120
"R3 Test Driver Port"
109121
}
122+
123+
fn should_pause_output() -> bool {
124+
SHOULD_PAUSE_OUTPUT.load(Ordering::Relaxed)
125+
}
110126
}
111127

112128
#[repr(C)]

src/r3_support_rp2040/src/usbstdio.rs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fn with_usb_stdio_global<T>(f: impl FnOnce(&mut UsbStdioGlobal, &mut WriteBufDeq
4949
}
5050

5151
/// The options for the USB serial device configured by [`configure`].
52-
pub trait Options {
52+
pub trait Options: 'static + Send + Sync {
5353
/// Handle incoming data.
5454
///
5555
/// This method may be called with interrupts disabled. It's safe to write
@@ -60,6 +60,15 @@ pub trait Options {
6060
fn product_name() -> &'static str {
6161
"R3 Example Application Port"
6262
}
63+
64+
/// Return a flag indicating whether the output data should be withheld
65+
/// from transmission.
66+
///
67+
/// If this flag is changed to `false`, [`poll`] must be called to flush
68+
/// the data in the transmission buffer.
69+
fn should_pause_output() -> bool {
70+
false
71+
}
6372
}
6473

6574
/// Add a USB serial device to the system and register it as the destination of
@@ -98,7 +107,7 @@ pub const fn configure<System: Kernel, TOptions: Options>(b: &mut CfgBuilder<Sys
98107
});
99108

100109
// Register the standard output
101-
crate::stdout::set_stdout(NbWriter);
110+
crate::stdout::set_stdout(NbWriter::<TOptions>(core::marker::PhantomData));
102111
})
103112
.finish(b);
104113

@@ -114,29 +123,33 @@ pub const fn configure<System: Kernel, TOptions: Options>(b: &mut CfgBuilder<Sys
114123
InterruptHandler::build()
115124
.line(int_num)
116125
.start(|_| {
117-
let mut buf = [0; 64];
118-
let mut read_len = 0;
126+
poll::<TOptions>();
127+
})
128+
.finish(b);
129+
}
119130

120-
// Get the global `UsbStdioGlobal` instance, which should
121-
// have been created by the startup hook above
122-
with_usb_stdio_global(|g, write_buf| {
123-
g.usb_device.poll(&mut [&mut g.serial]);
131+
pub fn poll<TOptions: Options>() {
132+
let mut buf = [0; 64];
133+
let mut read_len = 0;
124134

125-
if let Ok(len) = g.serial.read(&mut buf) {
126-
read_len = len;
127-
}
135+
// Get the global `UsbStdioGlobal` instance, which should
136+
// have been created by the startup hook above
137+
with_usb_stdio_global(|g, write_buf| {
138+
g.usb_device.poll(&mut [&mut g.serial]);
128139

129-
g.try_flush(write_buf);
130-
});
140+
if let Ok(len) = g.serial.read(&mut buf) {
141+
read_len = len;
142+
}
131143

132-
if read_len > 0 {
133-
TOptions::handle_input(&buf[..read_len]);
134-
}
135-
})
136-
.finish(b);
144+
g.try_flush::<TOptions>(write_buf);
145+
});
146+
147+
if read_len > 0 {
148+
TOptions::handle_input(&buf[..read_len]);
149+
}
137150
}
138151

139-
struct NbWriter;
152+
struct NbWriter<TOptions>(core::marker::PhantomData<fn() -> TOptions>);
140153

141154
fn map_usb_error_to_nb_error(e: usb_device::UsbError) -> nb::Error<core::convert::Infallible> {
142155
match e {
@@ -158,22 +171,22 @@ fn map_usb_error_to_nb_error(e: usb_device::UsbError) -> nb::Error<core::convert
158171
}
159172
}
160173

161-
impl embedded_hal::serial::Write<u8> for NbWriter {
174+
impl<TOptions: Options> embedded_hal::serial::Write<u8> for NbWriter<TOptions> {
162175
type Error = core::convert::Infallible;
163176

164177
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
165178
with_usb_stdio_global(|g, write_buf| {
166179
// Push the given byte to the write buffer. Return `WouldBlock` if
167180
// the buffer is full.
168181
write_buf.push(word).map_err(|_| nb::Error::WouldBlock)?;
169-
g.try_flush(write_buf);
182+
g.try_flush::<TOptions>(write_buf);
170183
Ok(())
171184
})
172185
}
173186

174187
fn flush(&mut self) -> nb::Result<(), Self::Error> {
175188
with_usb_stdio_global(|g, write_buf| {
176-
g.try_flush(write_buf);
189+
g.try_flush::<TOptions>(write_buf);
177190
g.serial.flush().map_err(map_usb_error_to_nb_error)?;
178191
if !write_buf.is_empty() {
179192
return Err(nb::Error::WouldBlock);
@@ -184,9 +197,9 @@ impl embedded_hal::serial::Write<u8> for NbWriter {
184197
}
185198

186199
impl UsbStdioGlobal {
187-
fn try_flush(&mut self, write_buf: &mut WriteBufDeque) {
188-
// Withhold the data until DTR is asserted and RTS is cleared
189-
if !self.serial.dtr() {
200+
fn try_flush<TOptions: Options>(&mut self, write_buf: &mut WriteBufDeque) {
201+
// Withhold the data until DTR is asserted
202+
if !self.serial.dtr() || TOptions::should_pause_output() {
190203
return;
191204
}
192205

src/r3_test_runner/src/targets/rp_pico.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,20 @@ impl DebugProbe for RaspberryPiPicoUsbDebugProbe {
160160

161161
self.serial = Some(BufStream::new(serial));
162162

163+
// Tell the test driver that we are now listening for the output
164+
log::debug!("Unblocking the test driver's output");
165+
let serial = self.serial.as_mut().unwrap();
166+
serial
167+
.write_all(b"g")
168+
.await
169+
.with_context(|| "Failed to write to the test driver serial interface.")?;
170+
serial
171+
.flush()
172+
.await
173+
.with_context(|| "Failed to write to the test driver serial interface.")?;
174+
163175
// Now, pass the channel to the caller
164-
Ok(Box::pin(Demux::new(self.serial.as_mut().unwrap())) as _)
176+
Ok(Box::pin(Demux::new(serial)) as _)
165177
})
166178
}
167179
}

0 commit comments

Comments
 (0)