-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add BlockingIoDriver to make stdin/out/err calls blocking #471
base: master
Are you sure you want to change the base?
Conversation
This PR is not implementing the usb_serial driver for raw use. That can be done separately. It just adds some skeleton peripheral and only is concerned about the stdio stuff. |
I've avoided the VFS stdout/in/err redirection topic for a long time, because it is complex. However, if we are to introduce some control w.r.t. blocking vs non-blocking IO, I think we need to summarize - in English: I've tried to do (a) below and after that, validate your approach (b) against it. Please read on and if you spot anything incorrect, comment so that we can arrive at the status quo we both agree on. (a) How VFS stdout/in/err worksInvariant 1 - configuration of stdin/out/err initialization (very important)Whether UART0/1, or JTAG or USB-CDC will be used for the primary console output (and ditto for the secondary / "dev" console output) is controlled at compile-time ONLY using
Invariant 2 - VFS stdin/out/err initializationBecause the stdin/out/err should operate from very early in the program startup, the VFS stdin/out/err initialization is done implicitly, before the execution hits Invariant 3 - VFS stdin/out/err deinitializationDe-initialization of stdin/out/err VFS redirection never happens. I.e. the peripheral to which the standard streams are directed is grabbed inside VFS from before program startup, and remains grabbed forever throughout the lifecycle of the program. Invariant 4 - Blocking vs nonblocking IO
ConclusionsWhat follows from the first three invariants, is that a certain peripheral - or even two peripherals, if the secondary "dev" console is enabled - (be it UART0, or UART1 or JTAG or USB-CDC) is simply not available during the lifetime of the program, because it is "grabbed" in the VFS layer and stays there forever - possibly with some pins as well - at least for the UART case? Not sure how JTAG and CDC work w.r.t. grabbing of specific pins... This is to our advantage though, as what we can do is conditionalize the availability of the UART0, UART1, JTAG and (in future) USBCDC peripherals in the (b) How exactly we'll model it in RustPeripherals availability
Switching between blocking and nonblocking IO for the configured stdin/out/err peripheralFrom Invariant 4 it follows, that modeling the blocking mode can indeed be done with a "driver" or "state" or something - a token the user initializes and by the very initialization of the token, the mode of operation switches from nonblocking to blocking. Once the token is dropped, the operation switched back from blocking to nonblocking. The advantage of this approach is that it is symmetric with how "mounting" is currently modeled in The disadvantage is that there is nothing stopping users from calling this token twice. Your idea of passing the jtag peripheral (or UART0/1 peripheral) to the constructor of the token (= While the above is not the end of the world (trying to instantiate another =========== I'll now follow up with some inline comments in the PR, along the spirit of the above ^^^. |
@@ -1,10 +1,12 @@ | |||
//! Error types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move all of this code to esp_idf_svc::io::vfs
.
@ivmarkov Thanks for the feedback! Making it all compile time might be a good idea. A couple of things that are still open questions on that specific point. If we introduce this we should probably also gate the default gpio pins to not be available for whatever combination currently is selected. I will start here with an overview of how different configs will effect the availability of peripherals / gpios, and what it means for a "BlockingIoDriver" availability. Configuration SpaceDemonstrated on esp32c3 for simplicity.
Now additional cases for usb-otg devices like a esp32s2:
Difference between usb_cdc in otg devices vs usb_cdc in usb_serial_jtag devices.First of all it would be factual wrong to rename a usb_serial into jtag. The usb_serial_jtag controller is a USB Composite device witch means it consists of independent hardware that consist of a cdc_acm hardware interface and a jtag_command_processor. When we are reading and writing over usb_cdc we are not using the jtag_command_processor in any form physically. There are independent registers internally and writing and reading from them are independent. This opens the possibility of two different drivers
Because of this i want to make it clear that the BlockingIoDriver is using the usb_cdc hardware interface here. If you don't like usb_serial as a name i am ok with renaming it into into usb_cdc or something but i am not ok naming it JTAG as its misleading. Now when we are talking about usb_cdc on esp's usb-otg devices. Its basically a software solution ontop of the usb_transceiver. In other words its not a zero setup hardware solution but needs a complex usb setup chain. It also is mutual exusive with other usb scenarios on the device. E.g as soon as tinyusb stack is used this option is unavailable. What does this mean for a potential usb_serial / usb_cdc peripheral in our device tree?
I would for now go with option a) since we don't have any wrappers for tinyusb / usb-otg and at this point we have enough of addressing other open points from feedback
TLDR:
|
@Vollbrecht Let's not rush the idea of marking e.g. UART0 and its pins as "grabbed" in VFS if the console is configured for UART. This might have benefits but also disadvantages. I need more time to comprehend the C code I'm seeing. |
It seems the way how UART, JTAG and USB-CDC operate as "files" in the VFS layer is fundamentally incompatible with our notion of This is not even related to the Here's an example to make it clearer:
Does that mean that ALL uarts are now "grabbed" by the VFS layer?
Where it becomes harmful, and where the peripheral becomes "grabbed" (but just for the duration of the operation) is if you call VFS So in a way, doing VFS I no longer believe we can express this pattern in a safe way in Rust. So what to do?
Users just have to be careful about the fact that "owning" a UARTX peripheral somewhere still leaves the
It becomes too complex and next to impossible to track I think. And after all, even if the user does not call And again please note that all of the above has NOTHING to do with stdin/out/err in fact! This is just how VFS works w.r.t. uart/jtag/usb-cdc!
Let me think about this.... |
The primary two goal's of the API should be the following:
I think handling the availability of gpio's or peripherals is more of a secondary goal overall, and as you also pointed out its quite a complex undertaking. So i would suggest to go with just a light wrapper around the different unsafe calls, and provide the necessary docu ontop of that wrapper, mention the correct sdkconfigs a user needs to set, while still making it a compile time solution like discussed ( to guarantee exclusiveness), but doesn't bother to take arguments with any peripherals or gpio's, and only internally make sure that it doesn't cant get called multiple times by an internal static? |
I agree.
How about....: In fact, your original approach might then be just good enough!
Let me re-review the comments to your changes in light of this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed all my previous feedback and re-added a new one in light of everything discussed.
Other than moving the VFS portion to esp_idf_svc::io::vfs
, simplifying BlockingUartIo
and BlockingSerialIo
, and - most importantly - fixing one lifetime-related bug I don't have anything else to add ATM.
/// | ||
/// A BlockingIoDriver will instruct the VFS(Virtual File System) to use the drivers interrupt driven, | ||
/// blocking read and write functions instead. | ||
pub struct BlockingIoDriver<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need BlockingIoDriver
. See below.
phantom: PhantomData<T>, | ||
} | ||
|
||
pub struct BlockingUartIo<T: Uart> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just
struct BlockingUartIo<'d> {
uart: u8,
_d: PhantomData<&'d mut ()>, // You do need this, or else user would be able to call `BlockingUartIo::new` multiple times on the _same_ `&mut UART` without dropping first the previous `BlockingUartIo`s, which is not ideal
}
impl<'d> BlockingUartIo<';d> {
pub fn new<P: Uart>(uart: impl Peripheral<P = P> + 'd) -> Result<Self, EspError> {
// TODO: Call driver initialize etc. etc.
Ok(Self {
uart: P::uart(),
_d: PhantomData,
})
}
}
impl<'d> Drop for BlockingUartIo<'d> {
fn drop(&mut self) {
// TODO: Deinstall the driver and switch to nonblocking mode using `self.uart`
}
}
... and ditto for BlockingUsbSerialIo
? No extra BlockingIoDriver
, no generics. Just one lifetime 'd
which we do need if we want to prevent the user from calling BlockingUartIo
twice for the same UART peripheral.
#[cfg(esp_idf_soc_usb_serial_jtag_supported)] | ||
use crate::usb_serial::UsbSerial; | ||
#[cfg(esp_idf_soc_usb_serial_jtag_supported)] | ||
pub struct BlockingSerialIo<T: UsbSerial> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above how we can simplify by removing the T
generics and removing BlockingIoDriver
.
Also - even if it is a bit mouthful, how about BlockingUsbSerialIo
?
No description provided.