use crate::{
bindings,
device::Device,
error::Error,
error::Result,
ffi,
str::{CStr, CStrExt as _},
};
use core::ptr::NonNull;
struct FwFunc(
unsafe extern "C" fn(
*mut *const bindings::firmware,
*const ffi::c_char,
*mut bindings::device,
) -> i32,
);
impl FwFunc {
fn request() -> Self {
Self(bindings::request_firmware)
}
fn request_nowarn() -> Self {
Self(bindings::firmware_request_nowarn)
}
}
pub struct Firmware(NonNull<bindings::firmware>);
impl Firmware {
fn request_internal(name: &CStr, dev: &Device, func: FwFunc) -> Result<Self> {
let mut fw: *mut bindings::firmware = core::ptr::null_mut();
let pfw: *mut *mut bindings::firmware = &mut fw;
let pfw: *mut *const bindings::firmware = pfw.cast();
let ret = unsafe { func.0(pfw, name.as_char_ptr(), dev.as_raw()) };
if ret != 0 {
return Err(Error::from_errno(ret));
}
Ok(Firmware(unsafe { NonNull::new_unchecked(fw) }))
}
pub fn request(name: &CStr, dev: &Device) -> Result<Self> {
Self::request_internal(name, dev, FwFunc::request())
}
pub fn request_nowarn(name: &CStr, dev: &Device) -> Result<Self> {
Self::request_internal(name, dev, FwFunc::request_nowarn())
}
fn as_raw(&self) -> *mut bindings::firmware {
self.0.as_ptr()
}
pub fn size(&self) -> usize {
unsafe { (*self.as_raw()).size }
}
pub fn data(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts((*self.as_raw()).data, self.size()) }
}
}
impl Drop for Firmware {
fn drop(&mut self) {
unsafe { bindings::release_firmware(self.as_raw()) };
}
}
unsafe impl Send for Firmware {}
unsafe impl Sync for Firmware {}
#[macro_export]
macro_rules! module_firmware {
($($builder:tt)*) => {
const _: () = {
const __MODULE_FIRMWARE_PREFIX: &'static $crate::str::CStr = if cfg!(MODULE) {
c""
} else {
<LocalModule as $crate::ModuleMetadata>::NAME
};
#[link_section = ".modinfo"]
#[used(compiler)]
static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX)
.build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build();
};
};
}
pub struct ModInfoBuilder<const N: usize> {
buf: [u8; N],
n: usize,
module_name: &'static CStr,
}
impl<const N: usize> ModInfoBuilder<N> {
pub const fn new(module_name: &'static CStr) -> Self {
Self {
buf: [0; N],
n: 0,
module_name,
}
}
const fn push_internal(mut self, bytes: &[u8]) -> Self {
let mut j = 0;
if N == 0 {
self.n += bytes.len();
return self;
}
while j < bytes.len() {
if self.n < N {
self.buf[self.n] = bytes[j];
}
self.n += 1;
j += 1;
}
self
}
pub const fn push(self, s: &str) -> Self {
if N != 0 && self.n == 0 {
crate::build_error!("Must call next_entry() before push().");
}
self.push_internal(s.as_bytes())
}
const fn push_module_name(self) -> Self {
let mut this = self;
let module_name = this.module_name;
if !this.module_name.is_empty() {
this = this.push_internal(module_name.to_bytes_with_nul());
if N != 0 {
this.buf[this.n - 1] = b'.';
}
}
this
}
pub const fn new_entry(self) -> Self {
self.push_internal(b"\0")
.push_module_name()
.push_internal(b"firmware=")
}
pub const fn build(self) -> [u8; N] {
let this = self.push_internal(b"\0");
if this.n == N {
this.buf
} else {
crate::build_error!("Length mismatch.");
}
}
}
impl ModInfoBuilder<0> {
pub const fn build_length(self) -> usize {
self.n + 1
}
}