@@ -115,3 +115,130 @@ unsafe impl Send for Firmware {}
115115// SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to
116116// be used from any thread.
117117unsafe impl Sync for Firmware { }
118+
119+ /// Builder for firmware module info.
120+ ///
121+ /// [`ModInfoBuilder`] is a helper component to flexibly compose firmware paths strings for the
122+ /// .modinfo section in const context.
123+ ///
124+ /// Therefore the [`ModInfoBuilder`] provides the methods [`ModInfoBuilder::new_entry`] and
125+ /// [`ModInfoBuilder::push`], where the latter is used to push path components and the former to
126+ /// mark the beginning of a new path string.
127+ ///
128+ /// [`ModInfoBuilder`] is meant to be used in combination with `kernel::module_firmware!`.
129+ ///
130+ /// The const generic `N` as well as the `module_name` parameter of [`ModInfoBuilder::new`] is an
131+ /// internal implementation detail and supplied through the above macro.
132+ pub struct ModInfoBuilder < const N : usize > {
133+ buf : [ u8 ; N ] ,
134+ n : usize ,
135+ module_name : & ' static CStr ,
136+ }
137+
138+ impl < const N : usize > ModInfoBuilder < N > {
139+ /// Create an empty builder instance.
140+ pub const fn new ( module_name : & ' static CStr ) -> Self {
141+ Self {
142+ buf : [ 0 ; N ] ,
143+ n : 0 ,
144+ module_name,
145+ }
146+ }
147+
148+ const fn push_internal ( mut self , bytes : & [ u8 ] ) -> Self {
149+ let mut j = 0 ;
150+
151+ if N == 0 {
152+ self . n += bytes. len ( ) ;
153+ return self ;
154+ }
155+
156+ while j < bytes. len ( ) {
157+ if self . n < N {
158+ self . buf [ self . n ] = bytes[ j] ;
159+ }
160+ self . n += 1 ;
161+ j += 1 ;
162+ }
163+ self
164+ }
165+
166+ /// Push an additional path component.
167+ ///
168+ /// Append path components to the [`ModInfoBuilder`] instance. Paths need to be separated
169+ /// with [`ModInfoBuilder::new_entry`].
170+ ///
171+ /// # Example
172+ ///
173+ /// ```
174+ /// use kernel::firmware::ModInfoBuilder;
175+ ///
176+ /// # const DIR: &str = "vendor/chip/";
177+ /// # const fn no_run<const N: usize>(builder: ModInfoBuilder<N>) {
178+ /// let builder = builder.new_entry()
179+ /// .push(DIR)
180+ /// .push("foo.bin")
181+ /// .new_entry()
182+ /// .push(DIR)
183+ /// .push("bar.bin");
184+ /// # }
185+ /// ```
186+ pub const fn push ( self , s : & str ) -> Self {
187+ // Check whether there has been an initial call to `next_entry()`.
188+ if N != 0 && self . n == 0 {
189+ crate :: build_error!( "Must call next_entry() before push()." ) ;
190+ }
191+
192+ self . push_internal ( s. as_bytes ( ) )
193+ }
194+
195+ const fn push_module_name ( self ) -> Self {
196+ let mut this = self ;
197+ let module_name = this. module_name ;
198+
199+ if !this. module_name . is_empty ( ) {
200+ this = this. push_internal ( module_name. as_bytes_with_nul ( ) ) ;
201+
202+ if N != 0 {
203+ // Re-use the space taken by the NULL terminator and swap it with the '.' separator.
204+ this. buf [ this. n - 1 ] = b'.' ;
205+ }
206+ }
207+
208+ this
209+ }
210+
211+ /// Prepare the [`ModInfoBuilder`] for the next entry.
212+ ///
213+ /// This method acts as a separator between module firmware path entries.
214+ ///
215+ /// Must be called before constructing a new entry with subsequent calls to
216+ /// [`ModInfoBuilder::push`].
217+ ///
218+ /// See [`ModInfoBuilder::push`] for an example.
219+ pub const fn new_entry ( self ) -> Self {
220+ self . push_internal ( b"\0 " )
221+ . push_module_name ( )
222+ . push_internal ( b"firmware=" )
223+ }
224+
225+ /// Build the byte array.
226+ pub const fn build ( self ) -> [ u8 ; N ] {
227+ // Add the final NULL terminator.
228+ let this = self . push_internal ( b"\0 " ) ;
229+
230+ if this. n == N {
231+ this. buf
232+ } else {
233+ crate :: build_error!( "Length mismatch." ) ;
234+ }
235+ }
236+ }
237+
238+ impl ModInfoBuilder < 0 > {
239+ /// Return the length of the byte array to build.
240+ pub const fn build_length ( self ) -> usize {
241+ // Compensate for the NULL terminator added by `build`.
242+ self . n + 1
243+ }
244+ }
0 commit comments