xref: /linux-6.15/rust/kernel/firmware.rs (revision 53bd9780)
1de658283SDanilo Krummrich // SPDX-License-Identifier: GPL-2.0
2de658283SDanilo Krummrich 
3de658283SDanilo Krummrich //! Firmware abstraction
4de658283SDanilo Krummrich //!
5cd04d509SAndrew Ballance //! C header: [`include/linux/firmware.h`](srctree/include/linux/firmware.h)
6de658283SDanilo Krummrich 
7*53bd9780SChristian Schrefl use crate::{bindings, device::Device, error::Error, error::Result, ffi, str::CStr};
8de658283SDanilo Krummrich use core::ptr::NonNull;
9de658283SDanilo Krummrich 
10a23b018cSDanilo Krummrich /// # Invariants
11a23b018cSDanilo Krummrich ///
12a23b018cSDanilo Krummrich /// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
13a23b018cSDanilo Krummrich /// `bindings::firmware_request_platform`, `bindings::request_firmware_direct`.
14a23b018cSDanilo Krummrich struct FwFunc(
15*53bd9780SChristian Schrefl     unsafe extern "C" fn(
16*53bd9780SChristian Schrefl         *mut *const bindings::firmware,
17*53bd9780SChristian Schrefl         *const ffi::c_char,
18*53bd9780SChristian Schrefl         *mut bindings::device,
19*53bd9780SChristian Schrefl     ) -> i32,
20a23b018cSDanilo Krummrich );
21a23b018cSDanilo Krummrich 
22a23b018cSDanilo Krummrich impl FwFunc {
request() -> Self23a23b018cSDanilo Krummrich     fn request() -> Self {
24a23b018cSDanilo Krummrich         Self(bindings::request_firmware)
25a23b018cSDanilo Krummrich     }
26a23b018cSDanilo Krummrich 
request_nowarn() -> Self27a23b018cSDanilo Krummrich     fn request_nowarn() -> Self {
28a23b018cSDanilo Krummrich         Self(bindings::firmware_request_nowarn)
29a23b018cSDanilo Krummrich     }
30a23b018cSDanilo Krummrich }
31de658283SDanilo Krummrich 
32de658283SDanilo Krummrich /// Abstraction around a C `struct firmware`.
33de658283SDanilo Krummrich ///
34de658283SDanilo Krummrich /// This is a simple abstraction around the C firmware API. Just like with the C API, firmware can
35de658283SDanilo Krummrich /// be requested. Once requested the abstraction provides direct access to the firmware buffer as
36de658283SDanilo Krummrich /// `&[u8]`. The firmware is released once [`Firmware`] is dropped.
37de658283SDanilo Krummrich ///
38de658283SDanilo Krummrich /// # Invariants
39de658283SDanilo Krummrich ///
40de658283SDanilo Krummrich /// The pointer is valid, and has ownership over the instance of `struct firmware`.
41de658283SDanilo Krummrich ///
42bbe98f4fSDanilo Krummrich /// The `Firmware`'s backing buffer is not modified.
43de658283SDanilo Krummrich ///
44de658283SDanilo Krummrich /// # Examples
45de658283SDanilo Krummrich ///
462c61b8c5SDanilo Krummrich /// ```no_run
47de658283SDanilo Krummrich /// # use kernel::{c_str, device::Device, firmware::Firmware};
48de658283SDanilo Krummrich ///
492c61b8c5SDanilo Krummrich /// # fn no_run() -> Result<(), Error> {
50de658283SDanilo Krummrich /// # // SAFETY: *NOT* safe, just for the example to get an `ARef<Device>` instance
51cc4332afSGuilherme Giacomo Simoes /// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) };
52de658283SDanilo Krummrich ///
532c61b8c5SDanilo Krummrich /// let fw = Firmware::request(c_str!("path/to/firmware.bin"), &dev)?;
54de658283SDanilo Krummrich /// let blob = fw.data();
552c61b8c5SDanilo Krummrich ///
562c61b8c5SDanilo Krummrich /// # Ok(())
572c61b8c5SDanilo Krummrich /// # }
58de658283SDanilo Krummrich /// ```
59de658283SDanilo Krummrich pub struct Firmware(NonNull<bindings::firmware>);
60de658283SDanilo Krummrich 
61de658283SDanilo Krummrich impl Firmware {
request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result<Self>62de658283SDanilo Krummrich     fn request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result<Self> {
63de658283SDanilo Krummrich         let mut fw: *mut bindings::firmware = core::ptr::null_mut();
64de658283SDanilo Krummrich         let pfw: *mut *mut bindings::firmware = &mut fw;
65de658283SDanilo Krummrich 
66de658283SDanilo Krummrich         // SAFETY: `pfw` is a valid pointer to a NULL initialized `bindings::firmware` pointer.
67de658283SDanilo Krummrich         // `name` and `dev` are valid as by their type invariants.
68a23b018cSDanilo Krummrich         let ret = unsafe { func.0(pfw as _, name.as_char_ptr(), dev.as_raw()) };
69de658283SDanilo Krummrich         if ret != 0 {
70de658283SDanilo Krummrich             return Err(Error::from_errno(ret));
71de658283SDanilo Krummrich         }
72de658283SDanilo Krummrich 
73de658283SDanilo Krummrich         // SAFETY: `func` not bailing out with a non-zero error code, guarantees that `fw` is a
74de658283SDanilo Krummrich         // valid pointer to `bindings::firmware`.
75de658283SDanilo Krummrich         Ok(Firmware(unsafe { NonNull::new_unchecked(fw) }))
76de658283SDanilo Krummrich     }
77de658283SDanilo Krummrich 
78de658283SDanilo Krummrich     /// Send a firmware request and wait for it. See also `bindings::request_firmware`.
request(name: &CStr, dev: &Device) -> Result<Self>79de658283SDanilo Krummrich     pub fn request(name: &CStr, dev: &Device) -> Result<Self> {
80a23b018cSDanilo Krummrich         Self::request_internal(name, dev, FwFunc::request())
81de658283SDanilo Krummrich     }
82de658283SDanilo Krummrich 
83de658283SDanilo Krummrich     /// Send a request for an optional firmware module. See also
84de658283SDanilo Krummrich     /// `bindings::firmware_request_nowarn`.
request_nowarn(name: &CStr, dev: &Device) -> Result<Self>85de658283SDanilo Krummrich     pub fn request_nowarn(name: &CStr, dev: &Device) -> Result<Self> {
86a23b018cSDanilo Krummrich         Self::request_internal(name, dev, FwFunc::request_nowarn())
87de658283SDanilo Krummrich     }
88de658283SDanilo Krummrich 
as_raw(&self) -> *mut bindings::firmware89de658283SDanilo Krummrich     fn as_raw(&self) -> *mut bindings::firmware {
90de658283SDanilo Krummrich         self.0.as_ptr()
91de658283SDanilo Krummrich     }
92de658283SDanilo Krummrich 
93de658283SDanilo Krummrich     /// Returns the size of the requested firmware in bytes.
size(&self) -> usize94de658283SDanilo Krummrich     pub fn size(&self) -> usize {
95bbe98f4fSDanilo Krummrich         // SAFETY: `self.as_raw()` is valid by the type invariant.
96de658283SDanilo Krummrich         unsafe { (*self.as_raw()).size }
97de658283SDanilo Krummrich     }
98de658283SDanilo Krummrich 
99de658283SDanilo Krummrich     /// Returns the requested firmware as `&[u8]`.
data(&self) -> &[u8]100de658283SDanilo Krummrich     pub fn data(&self) -> &[u8] {
101bbe98f4fSDanilo Krummrich         // SAFETY: `self.as_raw()` is valid by the type invariant. Additionally,
102bbe98f4fSDanilo Krummrich         // `bindings::firmware` guarantees, if successfully requested, that
103bbe98f4fSDanilo Krummrich         // `bindings::firmware::data` has a size of `bindings::firmware::size` bytes.
104de658283SDanilo Krummrich         unsafe { core::slice::from_raw_parts((*self.as_raw()).data, self.size()) }
105de658283SDanilo Krummrich     }
106de658283SDanilo Krummrich }
107de658283SDanilo Krummrich 
108de658283SDanilo Krummrich impl Drop for Firmware {
drop(&mut self)109de658283SDanilo Krummrich     fn drop(&mut self) {
110bbe98f4fSDanilo Krummrich         // SAFETY: `self.as_raw()` is valid by the type invariant.
111de658283SDanilo Krummrich         unsafe { bindings::release_firmware(self.as_raw()) };
112de658283SDanilo Krummrich     }
113de658283SDanilo Krummrich }
114de658283SDanilo Krummrich 
115de658283SDanilo Krummrich // SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, which is safe to be used from
116de658283SDanilo Krummrich // any thread.
117de658283SDanilo Krummrich unsafe impl Send for Firmware {}
118de658283SDanilo Krummrich 
119de658283SDanilo Krummrich // SAFETY: `Firmware` only holds a pointer to a C `struct firmware`, references to which are safe to
120de658283SDanilo Krummrich // be used from any thread.
121de658283SDanilo Krummrich unsafe impl Sync for Firmware {}
122ef476b0dSDanilo Krummrich 
1231d121a33SDanilo Krummrich /// Create firmware .modinfo entries.
1241d121a33SDanilo Krummrich ///
1251d121a33SDanilo Krummrich /// This macro is the counterpart of the C macro `MODULE_FIRMWARE()`, but instead of taking a
1261d121a33SDanilo Krummrich /// simple string literals, which is already covered by the `firmware` field of
1271d121a33SDanilo Krummrich /// [`crate::prelude::module!`], it allows the caller to pass a builder type, based on the
1281d121a33SDanilo Krummrich /// [`ModInfoBuilder`], which can create the firmware modinfo strings in a more flexible way.
1291d121a33SDanilo Krummrich ///
1301d121a33SDanilo Krummrich /// Drivers should extend the [`ModInfoBuilder`] with their own driver specific builder type.
1311d121a33SDanilo Krummrich ///
1321d121a33SDanilo Krummrich /// The `builder` argument must be a type which implements the following function.
1331d121a33SDanilo Krummrich ///
1341d121a33SDanilo Krummrich /// `const fn create(module_name: &'static CStr) -> ModInfoBuilder`
1351d121a33SDanilo Krummrich ///
1361d121a33SDanilo Krummrich /// `create` should pass the `module_name` to the [`ModInfoBuilder`] and, with the help of
1371d121a33SDanilo Krummrich /// it construct the corresponding firmware modinfo.
1381d121a33SDanilo Krummrich ///
1391d121a33SDanilo Krummrich /// Typically, such contracts would be enforced by a trait, however traits do not (yet) support
1401d121a33SDanilo Krummrich /// const functions.
1411d121a33SDanilo Krummrich ///
1421d121a33SDanilo Krummrich /// # Example
1431d121a33SDanilo Krummrich ///
1441d121a33SDanilo Krummrich /// ```
1451d121a33SDanilo Krummrich /// # mod module_firmware_test {
1461d121a33SDanilo Krummrich /// # use kernel::firmware;
1471d121a33SDanilo Krummrich /// # use kernel::prelude::*;
1481d121a33SDanilo Krummrich /// #
1491d121a33SDanilo Krummrich /// # struct MyModule;
1501d121a33SDanilo Krummrich /// #
1511d121a33SDanilo Krummrich /// # impl kernel::Module for MyModule {
1521d121a33SDanilo Krummrich /// #     fn init(_module: &'static ThisModule) -> Result<Self> {
1531d121a33SDanilo Krummrich /// #         Ok(Self)
1541d121a33SDanilo Krummrich /// #     }
1551d121a33SDanilo Krummrich /// # }
1561d121a33SDanilo Krummrich /// #
1571d121a33SDanilo Krummrich /// #
1581d121a33SDanilo Krummrich /// struct Builder<const N: usize>;
1591d121a33SDanilo Krummrich ///
1601d121a33SDanilo Krummrich /// impl<const N: usize> Builder<N> {
1611d121a33SDanilo Krummrich ///     const DIR: &'static str = "vendor/chip/";
1621d121a33SDanilo Krummrich ///     const FILES: [&'static str; 3] = [ "foo", "bar", "baz" ];
1631d121a33SDanilo Krummrich ///
1641d121a33SDanilo Krummrich ///     const fn create(module_name: &'static kernel::str::CStr) -> firmware::ModInfoBuilder<N> {
1651d121a33SDanilo Krummrich ///         let mut builder = firmware::ModInfoBuilder::new(module_name);
1661d121a33SDanilo Krummrich ///
1671d121a33SDanilo Krummrich ///         let mut i = 0;
1681d121a33SDanilo Krummrich ///         while i < Self::FILES.len() {
1691d121a33SDanilo Krummrich ///             builder = builder.new_entry()
1701d121a33SDanilo Krummrich ///                 .push(Self::DIR)
1711d121a33SDanilo Krummrich ///                 .push(Self::FILES[i])
1721d121a33SDanilo Krummrich ///                 .push(".bin");
1731d121a33SDanilo Krummrich ///
1741d121a33SDanilo Krummrich ///                 i += 1;
1751d121a33SDanilo Krummrich ///         }
1761d121a33SDanilo Krummrich ///
1771d121a33SDanilo Krummrich ///         builder
1781d121a33SDanilo Krummrich ///      }
1791d121a33SDanilo Krummrich /// }
1801d121a33SDanilo Krummrich ///
1811d121a33SDanilo Krummrich /// module! {
1821d121a33SDanilo Krummrich ///    type: MyModule,
1831d121a33SDanilo Krummrich ///    name: "module_firmware_test",
1841d121a33SDanilo Krummrich ///    author: "Rust for Linux",
1851d121a33SDanilo Krummrich ///    description: "module_firmware! test module",
1861d121a33SDanilo Krummrich ///    license: "GPL",
1871d121a33SDanilo Krummrich /// }
1881d121a33SDanilo Krummrich ///
1891d121a33SDanilo Krummrich /// kernel::module_firmware!(Builder);
1901d121a33SDanilo Krummrich /// # }
1911d121a33SDanilo Krummrich /// ```
1921d121a33SDanilo Krummrich #[macro_export]
1931d121a33SDanilo Krummrich macro_rules! module_firmware {
1941d121a33SDanilo Krummrich     // The argument is the builder type without the const generic, since it's deferred from within
1951d121a33SDanilo Krummrich     // this macro. Hence, we can neither use `expr` nor `ty`.
1961d121a33SDanilo Krummrich     ($($builder:tt)*) => {
1971d121a33SDanilo Krummrich         const _: () = {
1981d121a33SDanilo Krummrich             const __MODULE_FIRMWARE_PREFIX: &'static $crate::str::CStr = if cfg!(MODULE) {
1991d121a33SDanilo Krummrich                 $crate::c_str!("")
2001d121a33SDanilo Krummrich             } else {
2011d121a33SDanilo Krummrich                 <LocalModule as $crate::ModuleMetadata>::NAME
2021d121a33SDanilo Krummrich             };
2031d121a33SDanilo Krummrich 
2041d121a33SDanilo Krummrich             #[link_section = ".modinfo"]
2051d121a33SDanilo Krummrich             #[used]
2061d121a33SDanilo Krummrich             static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX)
2071d121a33SDanilo Krummrich                 .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build();
2081d121a33SDanilo Krummrich         };
2091d121a33SDanilo Krummrich     };
2101d121a33SDanilo Krummrich }
2111d121a33SDanilo Krummrich 
212ef476b0dSDanilo Krummrich /// Builder for firmware module info.
213ef476b0dSDanilo Krummrich ///
214ef476b0dSDanilo Krummrich /// [`ModInfoBuilder`] is a helper component to flexibly compose firmware paths strings for the
215ef476b0dSDanilo Krummrich /// .modinfo section in const context.
216ef476b0dSDanilo Krummrich ///
217ef476b0dSDanilo Krummrich /// Therefore the [`ModInfoBuilder`] provides the methods [`ModInfoBuilder::new_entry`] and
218ef476b0dSDanilo Krummrich /// [`ModInfoBuilder::push`], where the latter is used to push path components and the former to
219ef476b0dSDanilo Krummrich /// mark the beginning of a new path string.
220ef476b0dSDanilo Krummrich ///
2211d121a33SDanilo Krummrich /// [`ModInfoBuilder`] is meant to be used in combination with [`kernel::module_firmware!`].
222ef476b0dSDanilo Krummrich ///
223ef476b0dSDanilo Krummrich /// The const generic `N` as well as the `module_name` parameter of [`ModInfoBuilder::new`] is an
224ef476b0dSDanilo Krummrich /// internal implementation detail and supplied through the above macro.
225ef476b0dSDanilo Krummrich pub struct ModInfoBuilder<const N: usize> {
226ef476b0dSDanilo Krummrich     buf: [u8; N],
227ef476b0dSDanilo Krummrich     n: usize,
228ef476b0dSDanilo Krummrich     module_name: &'static CStr,
229ef476b0dSDanilo Krummrich }
230ef476b0dSDanilo Krummrich 
231ef476b0dSDanilo Krummrich impl<const N: usize> ModInfoBuilder<N> {
232ef476b0dSDanilo Krummrich     /// Create an empty builder instance.
new(module_name: &'static CStr) -> Self233ef476b0dSDanilo Krummrich     pub const fn new(module_name: &'static CStr) -> Self {
234ef476b0dSDanilo Krummrich         Self {
235ef476b0dSDanilo Krummrich             buf: [0; N],
236ef476b0dSDanilo Krummrich             n: 0,
237ef476b0dSDanilo Krummrich             module_name,
238ef476b0dSDanilo Krummrich         }
239ef476b0dSDanilo Krummrich     }
240ef476b0dSDanilo Krummrich 
push_internal(mut self, bytes: &[u8]) -> Self241ef476b0dSDanilo Krummrich     const fn push_internal(mut self, bytes: &[u8]) -> Self {
242ef476b0dSDanilo Krummrich         let mut j = 0;
243ef476b0dSDanilo Krummrich 
244ef476b0dSDanilo Krummrich         if N == 0 {
245ef476b0dSDanilo Krummrich             self.n += bytes.len();
246ef476b0dSDanilo Krummrich             return self;
247ef476b0dSDanilo Krummrich         }
248ef476b0dSDanilo Krummrich 
249ef476b0dSDanilo Krummrich         while j < bytes.len() {
250ef476b0dSDanilo Krummrich             if self.n < N {
251ef476b0dSDanilo Krummrich                 self.buf[self.n] = bytes[j];
252ef476b0dSDanilo Krummrich             }
253ef476b0dSDanilo Krummrich             self.n += 1;
254ef476b0dSDanilo Krummrich             j += 1;
255ef476b0dSDanilo Krummrich         }
256ef476b0dSDanilo Krummrich         self
257ef476b0dSDanilo Krummrich     }
258ef476b0dSDanilo Krummrich 
259ef476b0dSDanilo Krummrich     /// Push an additional path component.
260ef476b0dSDanilo Krummrich     ///
261ef476b0dSDanilo Krummrich     /// Append path components to the [`ModInfoBuilder`] instance. Paths need to be separated
262ef476b0dSDanilo Krummrich     /// with [`ModInfoBuilder::new_entry`].
263ef476b0dSDanilo Krummrich     ///
264ef476b0dSDanilo Krummrich     /// # Example
265ef476b0dSDanilo Krummrich     ///
266ef476b0dSDanilo Krummrich     /// ```
267ef476b0dSDanilo Krummrich     /// use kernel::firmware::ModInfoBuilder;
268ef476b0dSDanilo Krummrich     ///
269ef476b0dSDanilo Krummrich     /// # const DIR: &str = "vendor/chip/";
270ef476b0dSDanilo Krummrich     /// # const fn no_run<const N: usize>(builder: ModInfoBuilder<N>) {
271ef476b0dSDanilo Krummrich     /// let builder = builder.new_entry()
272ef476b0dSDanilo Krummrich     ///     .push(DIR)
273ef476b0dSDanilo Krummrich     ///     .push("foo.bin")
274ef476b0dSDanilo Krummrich     ///     .new_entry()
275ef476b0dSDanilo Krummrich     ///     .push(DIR)
276ef476b0dSDanilo Krummrich     ///     .push("bar.bin");
277ef476b0dSDanilo Krummrich     /// # }
278ef476b0dSDanilo Krummrich     /// ```
push(self, s: &str) -> Self279ef476b0dSDanilo Krummrich     pub const fn push(self, s: &str) -> Self {
280ef476b0dSDanilo Krummrich         // Check whether there has been an initial call to `next_entry()`.
281ef476b0dSDanilo Krummrich         if N != 0 && self.n == 0 {
282ef476b0dSDanilo Krummrich             crate::build_error!("Must call next_entry() before push().");
283ef476b0dSDanilo Krummrich         }
284ef476b0dSDanilo Krummrich 
285ef476b0dSDanilo Krummrich         self.push_internal(s.as_bytes())
286ef476b0dSDanilo Krummrich     }
287ef476b0dSDanilo Krummrich 
push_module_name(self) -> Self288ef476b0dSDanilo Krummrich     const fn push_module_name(self) -> Self {
289ef476b0dSDanilo Krummrich         let mut this = self;
290ef476b0dSDanilo Krummrich         let module_name = this.module_name;
291ef476b0dSDanilo Krummrich 
292ef476b0dSDanilo Krummrich         if !this.module_name.is_empty() {
293ef476b0dSDanilo Krummrich             this = this.push_internal(module_name.as_bytes_with_nul());
294ef476b0dSDanilo Krummrich 
295ef476b0dSDanilo Krummrich             if N != 0 {
296ef476b0dSDanilo Krummrich                 // Re-use the space taken by the NULL terminator and swap it with the '.' separator.
297ef476b0dSDanilo Krummrich                 this.buf[this.n - 1] = b'.';
298ef476b0dSDanilo Krummrich             }
299ef476b0dSDanilo Krummrich         }
300ef476b0dSDanilo Krummrich 
301ef476b0dSDanilo Krummrich         this
302ef476b0dSDanilo Krummrich     }
303ef476b0dSDanilo Krummrich 
304ef476b0dSDanilo Krummrich     /// Prepare the [`ModInfoBuilder`] for the next entry.
305ef476b0dSDanilo Krummrich     ///
306ef476b0dSDanilo Krummrich     /// This method acts as a separator between module firmware path entries.
307ef476b0dSDanilo Krummrich     ///
308ef476b0dSDanilo Krummrich     /// Must be called before constructing a new entry with subsequent calls to
309ef476b0dSDanilo Krummrich     /// [`ModInfoBuilder::push`].
310ef476b0dSDanilo Krummrich     ///
311ef476b0dSDanilo Krummrich     /// See [`ModInfoBuilder::push`] for an example.
new_entry(self) -> Self312ef476b0dSDanilo Krummrich     pub const fn new_entry(self) -> Self {
313ef476b0dSDanilo Krummrich         self.push_internal(b"\0")
314ef476b0dSDanilo Krummrich             .push_module_name()
315ef476b0dSDanilo Krummrich             .push_internal(b"firmware=")
316ef476b0dSDanilo Krummrich     }
317ef476b0dSDanilo Krummrich 
318ef476b0dSDanilo Krummrich     /// Build the byte array.
build(self) -> [u8; N]319ef476b0dSDanilo Krummrich     pub const fn build(self) -> [u8; N] {
320ef476b0dSDanilo Krummrich         // Add the final NULL terminator.
321ef476b0dSDanilo Krummrich         let this = self.push_internal(b"\0");
322ef476b0dSDanilo Krummrich 
323ef476b0dSDanilo Krummrich         if this.n == N {
324ef476b0dSDanilo Krummrich             this.buf
325ef476b0dSDanilo Krummrich         } else {
326ef476b0dSDanilo Krummrich             crate::build_error!("Length mismatch.");
327ef476b0dSDanilo Krummrich         }
328ef476b0dSDanilo Krummrich     }
329ef476b0dSDanilo Krummrich }
330ef476b0dSDanilo Krummrich 
331ef476b0dSDanilo Krummrich impl ModInfoBuilder<0> {
332ef476b0dSDanilo Krummrich     /// Return the length of the byte array to build.
build_length(self) -> usize333ef476b0dSDanilo Krummrich     pub const fn build_length(self) -> usize {
334ef476b0dSDanilo Krummrich         // Compensate for the NULL terminator added by `build`.
335ef476b0dSDanilo Krummrich         self.n + 1
336ef476b0dSDanilo Krummrich     }
337ef476b0dSDanilo Krummrich }
338