Skip to main content

esp_idf_svc/
io.rs

1pub use embedded_svc::utils::io as utils;
2pub use esp_idf_hal::io::*;
3
4#[cfg(esp_idf_comp_vfs_enabled)]
5pub mod vfs {
6    use core::borrow::BorrowMut;
7    use core::marker::PhantomData;
8
9    use crate::hal::uart::UartDriver;
10    #[cfg(esp_idf_soc_usb_serial_jtag_supported)]
11    use crate::hal::usb_serial::UsbSerialDriver;
12    use crate::sys::{self, EspError};
13    #[cfg(not(esp_idf_version_at_least_6_0_0))]
14    use crate::sys::{esp_vfs_dev_uart_use_driver, esp_vfs_dev_uart_use_nonblocking};
15    #[cfg(all(
16        not(esp_idf_version_at_least_6_0_0),
17        esp_idf_soc_usb_serial_jtag_supported
18    ))]
19    use crate::sys::{esp_vfs_usb_serial_jtag_use_driver, esp_vfs_usb_serial_jtag_use_nonblocking};
20    #[cfg(esp_idf_version_at_least_6_0_0)]
21    use crate::sys::{
22        uart_vfs_dev_use_driver as esp_vfs_dev_uart_use_driver,
23        uart_vfs_dev_use_nonblocking as esp_vfs_dev_uart_use_nonblocking,
24    };
25    #[cfg(all(esp_idf_version_at_least_6_0_0, esp_idf_soc_usb_serial_jtag_supported))]
26    use crate::sys::{
27        usb_serial_jtag_vfs_use_driver as esp_vfs_usb_serial_jtag_use_driver,
28        usb_serial_jtag_vfs_use_nonblocking as esp_vfs_usb_serial_jtag_use_nonblocking,
29    };
30
31    #[cfg(feature = "alloc")]
32    extern crate alloc;
33
34    /// Represents a mounted EventFD pseudo-filesystem.
35    ///
36    /// Operating on this filesystem is done only via the native, unsafe `sys::eventfd_*` function.
37    pub struct MountedEventfs(());
38
39    impl MountedEventfs {
40        /// Mount the EventFD pseudo-filesystem.
41        ///
42        /// # Arguments
43        /// - `max_fds`: The maximum number of file descriptors to allocate.
44        #[allow(clippy::needless_update)]
45        pub fn mount(max_fds: usize) -> Result<Self, sys::EspError> {
46            sys::esp!(unsafe {
47                sys::esp_vfs_eventfd_register(&sys::esp_vfs_eventfd_config_t {
48                    max_fds: max_fds as _,
49                    ..Default::default()
50                })
51            })?;
52
53            Ok(Self(()))
54        }
55    }
56
57    impl Drop for MountedEventfs {
58        fn drop(&mut self) {
59            sys::esp!(unsafe { sys::esp_vfs_eventfd_unregister() }).unwrap();
60        }
61    }
62
63    /// Represents a mounted SPIFFS filesystem.
64    #[cfg(feature = "alloc")]
65    pub struct MountedSpiffs<T> {
66        _spiffs: T,
67        path: alloc::ffi::CString,
68    }
69
70    #[cfg(feature = "alloc")]
71    impl<T> MountedSpiffs<T> {
72        /// Mount a SPIFFS filesystem.
73        ///
74        /// # Arguments
75        /// - `spiffs`: The SPIFFS filesystem instance to mount.
76        /// - `path`: The path to mount the filesystem at.
77        /// - `max_fds`: The maximum number of file descriptors to allocate.
78        pub fn mount(mut spiffs: T, path: &str, max_fds: usize) -> Result<Self, sys::EspError>
79        where
80            T: core::borrow::BorrowMut<crate::fs::spiffs::Spiffs>,
81        {
82            let path = crate::private::cstr::to_cstring_arg(path)?;
83
84            sys::esp!(unsafe {
85                sys::esp_vfs_spiffs_register(&sys::esp_vfs_spiffs_conf_t {
86                    base_path: path.as_ptr(),
87                    max_files: max_fds as _,
88                    partition_label: spiffs.borrow_mut().partition_label().as_ptr(),
89                    format_if_mount_failed: false,
90                })
91            })?;
92
93            Ok(Self {
94                _spiffs: spiffs,
95                path,
96            })
97        }
98    }
99
100    #[cfg(feature = "alloc")]
101    impl<T> Drop for MountedSpiffs<T> {
102        fn drop(&mut self) {
103            sys::esp!(unsafe { sys::esp_vfs_spiffs_unregister(self.path.as_ptr()) }).unwrap();
104        }
105    }
106
107    /// Represents a mounted FAT filesystem.
108    #[cfg(feature = "alloc")]
109    pub struct MountedFatfs<T> {
110        _handle: *mut sys::FATFS,
111        _fatfs: T,
112        path: alloc::ffi::CString,
113        drive: u8,
114    }
115
116    #[cfg(feature = "alloc")]
117    impl<T> MountedFatfs<T> {
118        /// Mount a FAT filesystem.
119        ///
120        /// # Arguments
121        /// - `fatfs`: The FAT filesystem instance to mount.
122        /// - `path`: The path to mount the filesystem at.
123        /// - `max_fds`: The maximum number of file descriptors to allocate.
124        pub fn mount<H>(mut fatfs: T, path: &str, max_fds: usize) -> Result<Self, sys::EspError>
125        where
126            T: core::borrow::BorrowMut<crate::fs::fatfs::Fatfs<H>>,
127        {
128            let path = crate::private::cstr::to_cstring_arg(path)?;
129            let drive_path = fatfs.borrow_mut().drive_path();
130
131            let mut handle = core::ptr::null_mut();
132
133            #[cfg(esp_idf_version_at_least_5_3_0)]
134            let conf = sys::esp_vfs_fat_conf_t {
135                base_path: path.as_ptr(),
136                fat_drive: drive_path.as_ptr(),
137                max_files: max_fds as _,
138            };
139
140            sys::esp!(unsafe {
141                #[cfg(not(esp_idf_version_at_least_5_3_0))]
142                {
143                    sys::esp_vfs_fat_register(
144                        path.as_ptr(),
145                        drive_path.as_ptr(),
146                        max_fds as _,
147                        &mut handle,
148                    )
149                }
150                // esp-idf >=5.3 switched to esp_vfs_fat_register_cfg with a config parameter.
151                #[cfg(all(esp_idf_version_at_least_5_3_0, not(esp_idf_version_at_least_6_1_0)))]
152                {
153                    sys::esp_vfs_fat_register_cfg(&conf, &mut handle)
154                }
155                // esp-idf >=6.1 reuses the old function name with a config parameter.
156                #[cfg(esp_idf_version_at_least_6_1_0)]
157                {
158                    sys::esp_vfs_fat_register(&conf, &mut handle)
159                }
160            })?;
161
162            unsafe {
163                sys::f_mount(handle, drive_path.as_ptr(), 0); // TODO
164            }
165
166            let drive = fatfs.borrow_mut().drive();
167
168            Ok(Self {
169                _handle: handle,
170                _fatfs: fatfs,
171                path,
172                drive,
173            })
174        }
175    }
176
177    #[cfg(feature = "alloc")]
178    impl<T> Drop for MountedFatfs<T> {
179        fn drop(&mut self) {
180            let drive_path = crate::fs::fatfs::Fatfs::<()>::drive_path_from(self.drive);
181
182            unsafe {
183                sys::f_mount(core::ptr::null_mut(), drive_path.as_ptr(), 0);
184            }
185
186            sys::esp!(unsafe { sys::esp_vfs_fat_unregister_path(self.path.as_ptr()) }).unwrap();
187        }
188    }
189
190    /// Represents a mounted Littlefs filesystem.
191    #[cfg(all(feature = "alloc", esp_idf_comp_joltwallet__littlefs_enabled))]
192    pub struct MountedLittlefs<T> {
193        _littlefs: T,
194        partition_raw_data: crate::fs::littlefs::PartitionRawData,
195    }
196
197    #[cfg(all(feature = "alloc", esp_idf_comp_joltwallet__littlefs_enabled))]
198    impl<T> MountedLittlefs<T> {
199        /// Mount a Littlefs filesystem.
200        ///
201        /// # Arguments
202        /// - `littlefs`: The Littlefs filesystem instance to mount.
203        /// - `path`: The path to mount the filesystem at.
204        pub fn mount<H>(mut littlefs: T, path: &str) -> Result<Self, sys::EspError>
205        where
206            T: core::borrow::BorrowMut<crate::fs::littlefs::Littlefs<H>>,
207        {
208            use crate::fs::littlefs::PartitionRawData;
209            use crate::private::cstr::to_cstring_arg;
210
211            let path = to_cstring_arg(path)?;
212
213            let partition_raw_data = littlefs.borrow_mut().partition_raw_data();
214
215            let conf = sys::esp_vfs_littlefs_conf_t {
216                base_path: path.as_ptr(),
217                partition_label: if let PartitionRawData::PartitionLabel(label) = partition_raw_data
218                {
219                    label
220                } else {
221                    core::ptr::null()
222                },
223                partition: if let PartitionRawData::RawPartition(partition) = partition_raw_data {
224                    partition
225                } else {
226                    core::ptr::null_mut()
227                },
228                #[cfg(esp_idf_littlefs_sdmmc_support)]
229                sdcard: if let PartitionRawData::SdCard(sdcard) = partition_raw_data {
230                    sdcard
231                } else {
232                    core::ptr::null_mut()
233                },
234                ..Default::default()
235            };
236
237            sys::esp!(unsafe { sys::esp_vfs_littlefs_register(&conf) })?;
238
239            Ok(Self {
240                _littlefs: littlefs,
241                partition_raw_data,
242            })
243        }
244
245        pub fn info(&self) -> Result<crate::fs::littlefs::LittleFsInfo, sys::EspError> {
246            use crate::fs::littlefs::PartitionRawData;
247
248            let mut info = crate::fs::littlefs::LittleFsInfo {
249                total_bytes: 0,
250                used_bytes: 0,
251            };
252
253            match self.partition_raw_data {
254                #[cfg(esp_idf_littlefs_sdmmc_support)]
255                PartitionRawData::SdCard(sd_card_ptr) => {
256                    sys::esp!(unsafe {
257                        sys::esp_littlefs_sdmmc_info(
258                            sd_card_ptr,
259                            &mut info.total_bytes,
260                            &mut info.used_bytes,
261                        )
262                    })?;
263                }
264                PartitionRawData::PartitionLabel(label) => {
265                    sys::esp!(unsafe {
266                        sys::esp_littlefs_info(label, &mut info.total_bytes, &mut info.used_bytes)
267                    })?;
268                }
269                PartitionRawData::RawPartition(partition) => {
270                    sys::esp!(unsafe {
271                        sys::esp_littlefs_partition_info(
272                            partition,
273                            &mut info.total_bytes,
274                            &mut info.used_bytes,
275                        )
276                    })?;
277                }
278            }
279
280            Ok(info)
281        }
282    }
283
284    #[cfg(all(feature = "alloc", esp_idf_comp_joltwallet__littlefs_enabled))]
285    impl<T> Drop for MountedLittlefs<T> {
286        fn drop(&mut self) {
287            use crate::fs::littlefs::PartitionRawData;
288
289            match self.partition_raw_data {
290                PartitionRawData::PartitionLabel(label) => {
291                    sys::esp!(unsafe { sys::esp_vfs_littlefs_unregister(label) }).unwrap();
292                }
293                PartitionRawData::RawPartition(partition) => {
294                    sys::esp!(unsafe { sys::esp_vfs_littlefs_unregister_partition(partition) })
295                        .unwrap();
296                }
297                #[cfg(esp_idf_littlefs_sdmmc_support)]
298                PartitionRawData::SdCard(sdcard) => {
299                    sys::esp!(unsafe { sys::esp_vfs_littlefs_unregister_sdmmc(sdcard) }).unwrap();
300                }
301            }
302        }
303    }
304
305    /// A utility for setting up a buffered and blocking communication for the Rust `stdio` subsystem.
306    ///
307    /// By default, all communication via `std::io:stdin` / `std::io::stdout` on the ESP-IDF is non-blocking.
308    /// One consequence of this, is that if the user wants to read from `std::io::stdin`, she has to constantly
309    /// poll the driver, since the respective hardware FIFO buffers are relatively small-ish.
310    /// Also the user would have to handle `WouldBlock` errors on every call, which is not very ergonomic.
311    ///
312    /// Instantiating the `BlockingStdIo` instructs the ESP-IDF VFS (Virtual File System) to use the
313    /// interrupt-driven drivers instead, as well as their blocking read / write functions.
314    pub struct BlockingStdIo<'d, T> {
315        uart_port: Option<crate::sys::uart_port_t>,
316        _driver: T,
317        _t: PhantomData<&'d mut ()>,
318    }
319
320    impl<'d, T> BlockingStdIo<'d, T>
321    where
322        T: BorrowMut<UartDriver<'d>>,
323    {
324        /// Create a `BlockingStdIo` instance for a UART driver
325        ///
326        /// Arguments:
327        /// - `driver`: The UART driver to use (i.e. a `UartDriver` instance that can be mutably borrowed)
328        pub fn uart(driver: T) -> Result<Self, EspError> {
329            unsafe { esp_vfs_dev_uart_use_driver(driver.borrow().port() as _) }
330
331            Ok(Self {
332                uart_port: Some(driver.borrow().port()),
333                _driver: driver,
334                _t: PhantomData,
335            })
336        }
337    }
338
339    #[cfg(esp_idf_soc_usb_serial_jtag_supported)]
340    impl<'d, T> BlockingStdIo<'d, T>
341    where
342        T: BorrowMut<UsbSerialDriver<'d>>,
343    {
344        /// Create a `BlockingStdIo` instance for a USB-SERIAL driver
345        ///
346        /// NOTE: By default, `println!` and `log!` output will be redirected to it in case
347        /// no UART connection is established to a Host PC. The peripheral is initialized at
348        /// startup and is using the ESP console slot 2 by default.
349        ///
350        /// NOTE: ESP console slot 2 cannot be used to read from the HOST, only writing is supported.
351        /// If reading from the HOST is necessary, reconfigure the ESP console by setting
352        /// the following into your projects sdkconfig.default file:
353        /// ```
354        /// CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
355        /// ```
356        ///
357        /// Arguments:
358        /// - `driver`: The USB-SERIAL driver to use (i.e. a `UsbSerialDriver` instance that can be mutably borrowed)
359        pub fn usb_serial(driver: T) -> Result<Self, EspError> {
360            unsafe { esp_vfs_usb_serial_jtag_use_driver() }
361
362            Ok(Self {
363                uart_port: None,
364                _driver: driver,
365                _t: PhantomData,
366            })
367        }
368    }
369
370    impl<T> Drop for BlockingStdIo<'_, T> {
371        fn drop(&mut self) {
372            if let Some(port) = self.uart_port {
373                unsafe { esp_vfs_dev_uart_use_nonblocking(port as _) }
374            } else {
375                #[cfg(esp_idf_soc_usb_serial_jtag_supported)]
376                {
377                    unsafe { esp_vfs_usb_serial_jtag_use_nonblocking() }
378                }
379            }
380        }
381    }
382}