1010//
1111// You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
1212
13+ //! Universal device tree overlay handler implementation.
14+ //!
15+ //! This module provides the [`UniversalOverlayHandler`] struct, which implements the
16+ //! [`OverlayHandler`] trait for generic device tree overlay management using the Linux
17+ //! configfs mechanism. It handles overlay application, removal, and status checking
18+ //! without vendor-specific logic.
19+ //!
20+ //! # Configfs Interface
21+ //!
22+ //! ## Overlay Directory Structure
23+ //!
24+ //! Overlays are managed in `/sys/kernel/config/device-tree/overlays/<handle>/`:
25+ //! ```text
26+ //! /sys/kernel/config/device-tree/overlays/my_overlay/
27+ //! ├── dtbo # Device tree blob (appears unused in current implementation)
28+ //! ├── path # Write overlay source path here to apply; read to verify application
29+ //! └── status # Read to check if overlay was applied successfully
30+ //!```
31+ //!
32+ //! # Overlay Application Flow
33+ //!
34+ //! 1. Create overlay directory in configfs
35+ //! 2. Write overlay source path to the `path` file
36+ //! 3. Verify overlay applied by checking both `path` and `status` files
37+ //!
38+ //! # Examples
39+ //!
40+ //! ```rust,no_run
41+ //! use daemon::platforms::universal_components::universal_overlay_handler::UniversalOverlayHandler;
42+ //! use daemon::platforms::platform::OverlayHandler;
43+ //! use std::path::Path;
44+ //!
45+ //! # fn example() -> Result<(), daemon::error::FpgadError> {
46+ //! let handler = platform_for_known_platform("universal").overlay_handler("my_overlay")?;
47+ //!
48+ //! // Apply overlay
49+ //! handler.apply_overlay(Path::new("/lib/firmware/design.dtbo"))?;
50+ //!
51+ //! // Check status
52+ //! let status = handler.status()?;
53+ //! println!("Overlay status: {}", status);
54+ //!
55+ //! // Remove overlay
56+ //! handler.remove_overlay()?;
57+ //! # Ok(())
58+ //! # }
59+ //! ```
60+
1361use crate :: config;
1462use crate :: error:: FpgadError ;
1563use crate :: platforms:: platform:: OverlayHandler ;
1664use crate :: system_io:: { fs_create_dir, fs_read, fs_remove_dir, fs_write} ;
1765use log:: { info, trace} ;
1866use std:: path:: { Path , PathBuf } ;
1967
20- /// Takes a handle and creates and stores an appropriate overlay_fs_path in this object.
21- /// The overlay_fs_path is static apart from the handle associated with each
22- /// device, overlay or bitstream, and so the handle is specified by the user here and the rest
23- /// is fixed.
68+ /// Construct the configfs path for an overlay directory.
69+ ///
70+ /// Creates the full path to an overlay's configfs directory by combining the
71+ /// base overlay control directory with the provided handle.
72+ /// See [config::OVERLAY_CONTROL_DIR] for information about the base path.
73+ ///
74+ /// # Arguments
75+ ///
76+ /// * `overlay_handle` - The overlay handle (directory name)
77+ ///
78+ /// # Returns: `PathBuf`
79+ /// * Full path to overlay directory in configfs
80+ ///
81+ /// # Examples
82+ ///
83+ /// ```rust,no_run
84+ /// # use std::path::PathBuf;
85+ /// # fn construct_overlay_fs_path(overlay_handle: &str) -> PathBuf {
86+ /// # PathBuf::from("/sys/kernel/config/device-tree/overlays").join(overlay_handle)
87+ /// # }
88+ /// let path = construct_overlay_fs_path("my_overlay");
89+ /// assert!(path.to_string_lossy().contains("my_overlay"));
90+ /// ```
2491fn construct_overlay_fs_path ( overlay_handle : & str ) -> PathBuf {
2592 let overlay_fs_path = PathBuf :: from ( config:: OVERLAY_CONTROL_DIR ) . join ( overlay_handle) ;
2693 trace ! ( "overlay_fs_path will be {overlay_fs_path:?}" ) ;
2794 overlay_fs_path
2895}
2996
30- /// Stores the three relevant paths: source files for dtbo/bitstream and the overlayfs dir to which
31- /// the dtbo path was written.
97+ /// Universal device tree overlay handler using Linux configfs.
98+ ///
99+ /// This struct manages a single device tree overlay through the Linux configfs
100+ /// mechanism. It stores the overlay's configfs directory path and provides methods
101+ /// to apply, remove, and check the status of the overlay.
102+ ///
103+ /// # Fields
104+ ///
105+ /// * `overlay_fs_path` - Path to the overlay's directory in configfs
106+ /// (e.g., `/sys/kernel/config/device-tree/overlays/my_overlay`)
107+ ///
108+ /// see [config::OVERLAY_CONTROL_DIR] for the base overlay control directory.
32109#[ derive( Debug ) ]
33110pub struct UniversalOverlayHandler {
34111 /// The path which points to the overlay virtual filesystem's dir which contains
35112 /// `path`, `status` and `dtbo` virtual files for overlay control. `dtbo` appears unused?
36113 overlay_fs_path : PathBuf ,
37114}
38115
116+ /// Implementation of helper methods for UniversalOverlayHandler.
39117impl UniversalOverlayHandler {
118+ /// Read the overlay status from the configfs `status` file.
119+ ///
120+ /// Reads from the `<overlay_fs_path>/status` file and returns
121+ /// the status string with trailing newlines removed.
122+ ///
123+ /// # Returns: `Result<String, FpgadError>`
124+ /// * `Ok(String)` - Status string (typically "applied" or empty)
125+ /// * `Err(FpgadError::IORead)` - Failed to read status file
40126 fn get_vfs_status ( & self ) -> Result < String , FpgadError > {
41127 let status_path = self . overlay_fs_path ( ) ?. join ( "status" ) ;
42128 trace ! ( "Reading from {status_path:?}" ) ;
43129 fs_read ( & status_path) . map ( |s| s. trim_end_matches ( '\n' ) . to_string ( ) )
44130 }
45- /// Read path from <overlay_fs_path>/path file and verify that what was meant to be applied
46- /// was applied.
131+
132+ /// Read the overlay path from the configfs `path` file.
133+ ///
134+ /// Reads from the `<overlay_fs_path>/path` file which contains
135+ /// the device tree path where the overlay was applied.
136+ ///
137+ /// # Returns: `Result<PathBuf, FpgadError>`
138+ /// * `Ok(PathBuf)` - Device tree path from the path file
139+ /// * `Err(FpgadError::IORead)` - Failed to read path file
47140 fn get_vfs_path ( & self ) -> Result < PathBuf , FpgadError > {
48141 let path_path = self . overlay_fs_path ( ) ?. join ( "path" ) ;
49142 trace ! ( "Reading from {path_path:?}" ) ;
50143 let path_string = fs_read ( & path_path) . map ( |s| s. trim_end_matches ( '\n' ) . to_string ( ) ) ?;
51144 Ok ( PathBuf :: from ( path_string) )
52145 }
53146
54- /// When an overlay fails to be applied, it may show as "applied" status but the path will
55- /// be empty. Therefore, this checks both match what is expected.
147+ /// Verify that an overlay was successfully applied.
148+ ///
149+ /// Checks both the `path` and `status` files within `overlay_fs_path` to ensure that
150+ /// the overlay was correctly applied. Sometimes an overlay may show "applied" status
151+ /// but have an empty path, indicating a failure.
152+ ///
153+ /// # Arguments
154+ ///
155+ /// * `source_path_rel` - The source path that was written to apply the overlay
156+ ///
157+ /// # Returns: `Result<(), FpgadError>`
158+ /// * `Ok(())` - Overlay successfully applied (path matches and status is "applied")
159+ /// * `Err(FpgadError::OverlayStatus)` - Path doesn't match or status not "applied"
160+ /// * `Err(FpgadError::IORead)` - Failed to read path or status file
56161 fn vfs_check_applied ( & self , source_path_rel : & Path ) -> Result < ( ) , FpgadError > {
57162 let path_file_contents = & self . get_vfs_path ( ) ?;
58163 if path_file_contents. ends_with ( source_path_rel) {
@@ -79,11 +184,32 @@ impl UniversalOverlayHandler {
79184 }
80185}
81186
187+ /// Implementation of the OverlayHandler trait for UniversalOverlayHandler.
82188impl OverlayHandler for UniversalOverlayHandler {
83- /// Attempts to apply a device tree overlay which should trigger a firmware load.
84- /// There are multiple ways to trigger a firmware load so this is not valid if the
85- /// dtbo doesn't contain a firmware to load.
86- /// Calls prepare_for_load to ensure paths are valid etc. beforehand.
189+ /// Apply a device tree overlay through configfs.
190+ ///
191+ /// Creates the overlay directory in configfs, writes the overlay source path to the
192+ /// `path` file, and verifies successful application. This may trigger automatic
193+ /// firmware loading if the overlay specifies a bitstream.
194+ ///
195+ /// # Arguments
196+ ///
197+ /// * `source_path_rel` - Path to the overlay file (can be absolute or relative to firmware path)
198+ ///
199+ /// # Returns: `Result<(), FpgadError>`
200+ /// * `Ok(())` - Overlay applied and verified successfully
201+ /// * `Err(FpgadError::Argument)` - Overlay with this handle already exists
202+ /// * `Err(FpgadError::IOCreate)` - Failed to create overlay directory
203+ /// * `Err(FpgadError::Internal)` - configfs didn't create `path` file (not mounted?)
204+ /// * `Err(FpgadError::IOWrite)` - Failed to write overlay path
205+ /// * `Err(FpgadError::OverlayStatus)` - Overlay didn't apply correctly
206+ ///
207+ /// # Notes
208+ ///
209+ /// - There are multiple ways device tree overlays can trigger firmware loading
210+ /// - This method is not valid if the dtbo doesn't contain firmware to load
211+ /// - The overlay directory must not already exist. [`OverlayHandler::remove_overlay`] can be called
212+ /// to remove it first.
87213 fn apply_overlay ( & self , source_path_rel : & Path ) -> Result < ( ) , FpgadError > {
88214 let overlay_fs_path = self . overlay_fs_path ( ) ?;
89215 if overlay_fs_path. exists ( ) {
@@ -115,20 +241,42 @@ impl OverlayHandler for UniversalOverlayHandler {
115241 self . vfs_check_applied ( source_path_rel)
116242 }
117243
118- /// Attempts to delete overlay_fs_path
244+ /// Remove a device tree overlay from configfs.
245+ ///
246+ /// Removes the overlay directory from configfs, which deactivates the overlay and
247+ /// restores the original device tree state.
248+ ///
249+ /// # Returns: `Result<(), FpgadError>`
250+ /// * `Ok(())` - Overlay directory removed successfully
251+ /// * `Err(FpgadError::IODelete)` - Failed to remove directory (not empty, doesn't exist, etc.)
119252 fn remove_overlay ( & self ) -> Result < ( ) , FpgadError > {
120253 let overlay_fs_path = self . overlay_fs_path ( ) ?;
121254 fs_remove_dir ( overlay_fs_path)
122255 }
123256
124- /// WARNING NOT IMPLEMENTED:
125- /// This is where the required fpga flags will be determined from the dtbo,
126- /// such as compressed or encrypted.
257+ /// Get the required FPGA programming flags from the overlay.
258+ ///
259+ /// # Warning: Not Implemented
260+ ///
261+ /// This method is intentionally unimplemented for the universal overlay handler.
262+ /// In a platform specific implementation (i.e. a softener) this would implement
263+ /// logic to parse the overlay/relevant package and determine any required FPGA
264+ /// programming flags.
265+ ///
266+ /// # Returns: `Result<isize, FpgadError>`
267+ /// * `Ok(0)` - Always returns 0 (no flags)
127268 fn required_flags ( & self ) -> Result < isize , FpgadError > {
128269 Ok ( 0 )
129270 }
130271
131- /// Read status from <overlay_fs_path>/status file and verify that it is "applied"
272+ /// Get the current status of the overlay.
273+ ///
274+ /// Reads both the `path` and `status` files from configfs and returns a combined
275+ /// status string. If the overlay directory doesn't exist, returns "not present".
276+ ///
277+ /// # Returns: `Result<String, FpgadError>`
278+ /// * `Ok(String)` - Status string (e.g., "/path/in/tree applied" or "not present")
279+ /// * `Err(FpgadError::IORead)` - Failed to read status or path file
132280 fn status ( & self ) -> Result < String , FpgadError > {
133281 if !self . overlay_fs_path ( ) ?. exists ( ) {
134282 return Ok ( "not present" . into ( ) ) ;
@@ -138,14 +286,37 @@ impl OverlayHandler for UniversalOverlayHandler {
138286 Ok ( format ! ( "{path:?} {status}" ) )
139287 }
140288
141- /// Checks that the overlay_fs_path is stored at time of call and returns it if so (unwraps Option into Result)
289+ /// Get the filesystem path to the overlay directory.
290+ ///
291+ /// Returns the stored overlay configfs path.
292+ ///
293+ /// # Returns: `Result<&Path, FpgadError>`
294+ /// * `Ok(&Path)` - Path to overlay directory in configfs
142295 fn overlay_fs_path ( & self ) -> Result < & Path , FpgadError > {
143296 Ok ( self . overlay_fs_path . as_path ( ) )
144297 }
145298}
146299
147300impl UniversalOverlayHandler {
148- /// Scans the package dir for required files
301+ /// Create a new UniversalOverlayHandler for the specified overlay.
302+ ///
303+ /// Constructs the configfs path for the overlay but doesn't create the directory
304+ /// or validate its existence. Directory creation happens in [`apply_overlay`](OverlayHandler::apply_overlay).
305+ ///
306+ /// # Arguments
307+ ///
308+ /// * `overlay_handle` - The overlay handle (directory name in configfs)
309+ ///
310+ /// # Returns: `Self`
311+ /// * New UniversalOverlayHandler instance
312+ ///
313+ /// # Examples
314+ ///
315+ /// ```rust,no_run
316+ /// # use daemon::platforms::platform::platform_for_known_platform;
317+ ///
318+ /// let handler = platform_for_known_platform("universal").overlay_handler("my_overlay")?;
319+ /// ```
149320 pub ( crate ) fn new ( overlay_handle : & str ) -> Self {
150321 UniversalOverlayHandler {
151322 overlay_fs_path : construct_overlay_fs_path ( overlay_handle) ,
0 commit comments