Skip to content

Commit ced0f0e

Browse files
committed
uefi: serial: add read_exact() and write_exact()
1 parent 3a1f35c commit ced0f0e

File tree

2 files changed

+126
-1
lines changed

2 files changed

+126
-1
lines changed

uefi/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
implements `Display`.
1919
- Added `Handle::component_name()` and `Handle::device_path()` to simplify the
2020
common use-case of querying more information about a handle.
21+
- Added `Serial::read_exact()` and `Serial::write_exact()`
22+
- Added `Serial::read_to_vec()`
2123

2224
## Changed
2325
- export all `text::{input, output}::*` types

uefi/src/proto/console/serial.rs

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
//! Abstraction over byte stream devices, also known as serial I/O devices.
44
55
use crate::proto::unsafe_protocol;
6-
use crate::{Error, Result, Status, StatusExt};
6+
use crate::{Error, Result, ResultExt, Status, StatusExt};
77
use core::fmt;
88
use core::fmt::Write;
99
use uefi_raw::protocol::console::serial::{
1010
SerialIoProtocol, SerialIoProtocol_1_1, SerialIoProtocolRevision,
1111
};
1212
use uguid::Guid;
1313

14+
use core::time::Duration;
15+
use uefi::boot;
1416
pub use uefi_raw::protocol::console::serial::{
1517
ControlBits, Parity, SerialIoMode as IoMode, StopBits,
1618
};
19+
#[cfg(feature = "alloc")]
20+
use {alloc::vec::Vec, core::slice};
1721

1822
/// Serial IO [`Protocol`]. Provides access to a serial I/O device.
1923
///
@@ -164,6 +168,86 @@ impl Serial {
164168
)
165169
}
166170

171+
/// Variant of [`Self::read`] that blocks until the exact amount of
172+
/// requested bytes were read.
173+
///
174+
/// This loops over potential timeouts and just tries again.
175+
///
176+
/// # Arguments
177+
///
178+
/// - `buffer`: buffer to fill
179+
///
180+
/// # Tips
181+
///
182+
/// Consider setting non-default properties via [`Self::set_attributes`]
183+
/// and [`Self::set_control_bits`] matching your use-case. For more info,
184+
/// please read the general [documentation](Self) of the protocol.
185+
///
186+
/// # Errors
187+
///
188+
/// - [`Status::DEVICE_ERROR`]: serial device reported an error
189+
pub fn read_exact(&mut self, buffer: &mut [u8]) -> Result<()> {
190+
let mut remaining_buffer = buffer;
191+
// We retry on timeout and only return other errors
192+
while !remaining_buffer.is_empty() {
193+
match self.read(remaining_buffer) {
194+
Ok(_) => {
195+
break;
196+
}
197+
Err(err) if err.status() == Status::TIMEOUT => {
198+
let n = *err.data();
199+
remaining_buffer = &mut remaining_buffer[n..];
200+
boot::stall(Duration::from_millis(1));
201+
}
202+
err => {
203+
return Err(Error::from(err.status()));
204+
}
205+
}
206+
}
207+
Ok(())
208+
}
209+
210+
/// Reads all data from the device until the first timeout occurs.
211+
///
212+
/// # Tips
213+
///
214+
/// Consider setting non-default properties via [`Self::set_attributes`]
215+
/// and [`Self::set_control_bits`] matching your use-case. For more info,
216+
/// please read the general [documentation](Self) of the protocol.
217+
///
218+
/// # Errors
219+
///
220+
/// - [`Status::DEVICE_ERROR`]: serial device reported an error
221+
#[cfg(feature = "alloc")]
222+
pub fn read_to_vec(&mut self) -> Result<Vec<u8>> {
223+
let mut vec = Vec::new();
224+
loop {
225+
vec.reserve(256);
226+
227+
let spare = vec.spare_capacity_mut();
228+
229+
// SAFETY: `read` promises to write `n` bytes starting at
230+
// `spare.as_mut_ptr()` and `n <= buf.len()`.
231+
let spare_slice =
232+
unsafe { slice::from_raw_parts_mut(spare.as_mut_ptr().cast::<u8>(), spare.len()) };
233+
234+
let n = match self.read(spare_slice) {
235+
Ok(_) => spare_slice.len(),
236+
Err(e) if e.status() == Status::TIMEOUT => *e.data(),
237+
Err(e) => return Err(Error::from(e.status())),
238+
};
239+
240+
// SAFETY: We know how many bytes have just been written.
241+
unsafe {
242+
vec.set_len(vec.len() + n);
243+
}
244+
if n == 0 {
245+
break;
246+
}
247+
}
248+
Ok(vec)
249+
}
250+
167251
/// Writes data to this device. This function has the raw semantics of the
168252
/// underlying UEFI protocol.
169253
///
@@ -235,6 +319,45 @@ impl Serial {
235319
};
236320
Ok(protocol)
237321
}
322+
323+
/// Variant of [`Self::write`] that blocks until the exact number of
324+
/// provided bytes were written.
325+
///
326+
/// This loops over potential timeouts and just tries again.
327+
///
328+
/// # Arguments
329+
///
330+
/// - `data`: bytes to write
331+
///
332+
/// # Tips
333+
///
334+
/// Consider setting non-default properties via [`Self::set_attributes`]
335+
/// and [`Self::set_control_bits`] matching your use-case. For more info,
336+
/// please read the general [documentation](Self) of the protocol.
337+
///
338+
/// # Errors
339+
///
340+
/// - [`Status::DEVICE_ERROR`]: serial device reported an error
341+
pub fn write_exact(&mut self, data: &[u8]) -> Result<()> {
342+
let mut remaining_bytes = data;
343+
// We retry on timeout and only return other errors
344+
while !remaining_bytes.is_empty() {
345+
match self.write(remaining_bytes) {
346+
Ok(_) => {
347+
break;
348+
}
349+
Err(err) if err.status() == Status::TIMEOUT => {
350+
let n = *err.data();
351+
remaining_bytes = &remaining_bytes[n..];
352+
boot::stall(Duration::from_millis(1));
353+
}
354+
err => {
355+
return Err(Error::from(err.status()));
356+
}
357+
}
358+
}
359+
Ok(())
360+
}
238361
}
239362

240363
impl Write for Serial {

0 commit comments

Comments
 (0)