|
3 | 3 | //! Abstraction over byte stream devices, also known as serial I/O devices. |
4 | 4 |
|
5 | 5 | use crate::proto::unsafe_protocol; |
6 | | -use crate::{Error, Result, Status, StatusExt}; |
| 6 | +use crate::{Error, Result, ResultExt, Status, StatusExt}; |
7 | 7 | use core::fmt; |
8 | 8 | use core::fmt::Write; |
9 | 9 | use uefi_raw::protocol::console::serial::{ |
10 | 10 | SerialIoProtocol, SerialIoProtocol_1_1, SerialIoProtocolRevision, |
11 | 11 | }; |
12 | 12 | use uguid::Guid; |
13 | 13 |
|
| 14 | +use core::time::Duration; |
| 15 | +use uefi::boot; |
14 | 16 | pub use uefi_raw::protocol::console::serial::{ |
15 | 17 | ControlBits, Parity, SerialIoMode as IoMode, StopBits, |
16 | 18 | }; |
| 19 | +#[cfg(feature = "alloc")] |
| 20 | +use {alloc::vec::Vec, core::slice}; |
17 | 21 |
|
18 | 22 | /// Serial IO [`Protocol`]. Provides access to a serial I/O device. |
19 | 23 | /// |
@@ -164,6 +168,86 @@ impl Serial { |
164 | 168 | ) |
165 | 169 | } |
166 | 170 |
|
| 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 | + |
167 | 251 | /// Writes data to this device. This function has the raw semantics of the |
168 | 252 | /// underlying UEFI protocol. |
169 | 253 | /// |
@@ -235,6 +319,45 @@ impl Serial { |
235 | 319 | }; |
236 | 320 | Ok(protocol) |
237 | 321 | } |
| 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 | + } |
238 | 361 | } |
239 | 362 |
|
240 | 363 | impl Write for Serial { |
|
0 commit comments