Skip to content

Commit

Permalink
Blocking and buffered StdIo (#541)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov authored Jan 1, 2025
1 parent 953bde3 commit 2968fa1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- OTA - Implements a new type `EspFirmwareInfoLoad` that has a reduced memory consumption (#531)
- Added set_promiscuous function in EthDriver (#246)
- Added set_promiscuous, is_promiscuous functions in WifiDriver (#246)
- Blocking and buffered StdIo (#541) - i.e. easy reading/input from `std::io::stdin`

### Fixed
- The alloc::Vec version stomps the scan state from Done to Idle. (#459)
Expand Down
90 changes: 89 additions & 1 deletion src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ pub use esp_idf_hal::io::*;

#[cfg(esp_idf_comp_vfs_enabled)]
pub mod vfs {
use crate::sys;
use core::borrow::BorrowMut;
use core::marker::PhantomData;

use crate::hal::uart::UartDriver;
#[cfg(esp_idf_soc_usb_serial_jtag_supported)]
use crate::hal::usb_serial::UsbSerialDriver;
use crate::sys::{
self, esp_vfs_dev_uart_use_driver, esp_vfs_dev_uart_use_nonblocking, EspError,
};
#[cfg(esp_idf_soc_usb_serial_jtag_supported)]
use crate::sys::{esp_vfs_usb_serial_jtag_use_driver, esp_vfs_usb_serial_jtag_use_nonblocking};

#[cfg(all(feature = "experimental", feature = "alloc"))]
extern crate alloc;
Expand Down Expand Up @@ -258,4 +268,82 @@ pub mod vfs {
}
}
}

/// A utility for setting up a buffered and blocking communication for the Rust `stdio` subsystem.
///
/// By default, all communication via `std::io:stdin` / `std::io::stdout` on the ESP-IDF is non-blocking.
/// One consequence of this, is that if the user wants to read from `std::io::stdin`, she has to constantly
/// poll the driver, since the respective hardware FIFO buffers are relatively small-ish.
/// Also the user would have to handle `WouldBlock` errors on every call, which is not very ergonomic.
///
/// Instantiating the `BlockingStdIo` instructs the ESP-IDF VFS (Virtual File System) to use the
/// interrupt-driven drivers instead, as well as their blocking read / write functions.
pub struct BlockingStdIo<'d, T> {
uart_port: Option<crate::sys::uart_port_t>,
_driver: T,
_t: PhantomData<&'d mut ()>,
}

impl<'d, T> BlockingStdIo<'d, T>
where
T: BorrowMut<UartDriver<'d>>,
{
/// Create a `BlockingStdIo` instance for a UART driver
///
/// Arguments:
/// - `driver`: The UART driver to use (i.e. a `UartDriver` instance that can be mutably borrowed)
pub fn uart(driver: T) -> Result<Self, EspError> {
unsafe { esp_vfs_dev_uart_use_driver(driver.borrow().port() as _) }

Ok(Self {
uart_port: Some(driver.borrow().port()),
_driver: driver,
_t: PhantomData,
})
}
}

#[cfg(esp_idf_soc_usb_serial_jtag_supported)]
impl<'d, T> BlockingStdIo<'d, T>
where
T: BorrowMut<UsbSerialDriver<'d>>,
{
/// Create a `BlockingStdIo` instance for a USB-SERIAL driver
///
/// NOTE: By default, `println!` and `log!` output will be redirected to it in case
/// no UART connection is established to a Host PC. The peripheral is initialized at
/// startup and is using the ESP console slot 2 by default.
///
/// NOTE: ESP console slot 2 cannot be used to read from the HOST, only writing is supported.
/// If reading from the HOST is necessary, reconfigure the ESP console by setting
/// the following into your projects sdkconfig.default file:
/// ```
/// CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
/// ```
///
/// Arguments:
/// - `driver`: The USB-SERIAL driver to use (i.e. a `UsbSerialDriver` instance that can be mutably borrowed)
pub fn usb_serial(driver: T) -> Result<Self, EspError> {
unsafe { esp_vfs_usb_serial_jtag_use_driver() }

Ok(Self {
uart_port: None,
_driver: driver,
_t: PhantomData,
})
}
}

impl<T> Drop for BlockingStdIo<'_, T> {
fn drop(&mut self) {
if let Some(port) = self.uart_port {
unsafe { esp_vfs_dev_uart_use_nonblocking(port as _) }
} else {
#[cfg(esp_idf_soc_usb_serial_jtag_supported)]
{
unsafe { esp_vfs_usb_serial_jtag_use_nonblocking() }
}
}
}
}
}

0 comments on commit 2968fa1

Please sign in to comment.