use core::ops::{Deref, DerefMut};
use procmacros::doc_replace;
#[cfg(dma_can_access_psram)]
use crate::soc::is_valid_psram_address;
use crate::{
dma::{DmaAlignmentError, DmaBufError},
soc::is_valid_ram_address,
};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(soc_internal_memory_cached, repr(C, align(64)))] #[cfg_attr(not(soc_internal_memory_cached), repr(C, align(4)))] #[instability::unstable]
pub struct InternalMemory<T>(T);
impl<T> InternalMemory<T> {
#[instability::unstable]
pub const fn new(init: T) -> Self {
Self(init)
}
#[instability::unstable]
pub const fn as_ptr(&self) -> *const T {
&raw const self.0
}
#[instability::unstable]
pub fn get_mut(&mut self) -> DmaAlignedMut<'_, T> {
assert!(is_valid_ram_address(&raw const self.0 as usize));
DmaAlignedMut(&mut self.0)
}
#[instability::unstable]
pub fn get_ref(&self) -> DmaAlignedRef<'_, T> {
assert!(is_valid_ram_address(&raw const self.0 as usize));
DmaAlignedRef(&self.0)
}
}
pub(crate) fn region_dma_alignment(addr: usize) -> Option<usize> {
if is_valid_ram_address(addr) {
return Some(core::mem::align_of::<InternalMemory<()>>());
}
#[cfg(dma_can_access_psram)]
if is_valid_psram_address(addr) {
return Some(cfg_select! {
soc_internal_memory_cached => 128,
esp32s31 => 64,
any(esp32, esp32c5, esp32c61) => 32,
_ => crate::soc::CONFIG_DATA_CACHE_LINE_SIZE,
});
}
None
}
fn validate_dma_alignment(addr: usize, size: usize) -> Result<(), DmaBufError> {
if size == 0 {
return Ok(());
}
let Some(alignment) = region_dma_alignment(addr) else {
return Err(DmaBufError::UnsupportedMemoryRegion);
};
if !size.is_multiple_of(alignment) {
return Err(DmaBufError::InvalidAlignment(DmaAlignmentError::Size));
}
if !addr.is_multiple_of(alignment) {
return Err(DmaBufError::InvalidAlignment(DmaAlignmentError::Address));
}
Ok(())
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
fn region_needs_cache_op(addr: usize, size: usize) -> bool {
if size == 0 {
return false;
}
let mut in_cached_region = false;
#[cfg(soc_internal_memory_cached)]
{
in_cached_region |= is_valid_ram_address(addr);
}
#[cfg(dma_can_access_psram)]
{
in_cached_region |= is_valid_psram_address(addr);
}
in_cached_region
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct DmaAlignedMut<'a, T>(&'a mut T)
where
T: ?Sized;
impl<'a, T: ?Sized> DmaAlignedMut<'a, T> {
#[doc_replace("align_req" => {
cfg(soc_internal_memory_cached) => "64",
_ => "4"
})]
#[instability::unstable]
pub fn new(ptr: &'a mut T) -> Result<Self, DmaBufError> {
let addr = ptr as *mut T as *mut () as usize;
validate_dma_alignment(addr, core::mem::size_of_val(ptr))?;
Ok(Self(ptr))
}
#[instability::unstable]
pub const unsafe fn new_unchecked(ptr: &'a mut T) -> Self {
Self(ptr)
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
fn do_cache_op(&self) -> bool {
region_needs_cache_op(
self.0 as *const T as *const () as usize,
core::mem::size_of_val(self.0),
)
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
#[instability::unstable]
pub fn writeback(&mut self) {
if !self.do_cache_op() {
return;
}
unsafe {
crate::soc::cache_writeback_addr(
self.0 as *const T as *const () as u32,
core::mem::size_of_val(self.0) as u32,
);
}
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
#[instability::unstable]
pub fn invalidate(&self) {
if !self.do_cache_op() {
return;
}
unsafe {
crate::soc::cache_invalidate_addr(
self.0 as *const T as *const () as u32,
core::mem::size_of_val(self.0) as u32,
);
}
}
pub fn into_inner(self) -> &'a mut T {
self.0
}
pub fn reborrow<'b>(&'b mut self) -> DmaAlignedMut<'b, T> {
DmaAlignedMut(self.0)
}
}
impl<'a, T, const N: usize> DmaAlignedMut<'a, [T; N]> {
pub fn unsize(self) -> DmaAlignedMut<'a, [T]> {
DmaAlignedMut(self.0)
}
}
impl<'a, T: ?Sized> Deref for DmaAlignedMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, T: ?Sized> DerefMut for DmaAlignedMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct DmaAlignedRef<'a, T>(&'a T)
where
T: ?Sized;
impl<'a, T: ?Sized> DmaAlignedRef<'a, T> {
#[doc_replace("align_req" => {
cfg(soc_internal_memory_cached) => "64",
_ => "4"
})]
#[instability::unstable]
pub fn new(ptr: &'a T) -> Result<Self, DmaBufError> {
let addr = ptr as *const T as *const () as usize;
validate_dma_alignment(addr, core::mem::size_of_val(ptr))?;
Ok(Self(ptr))
}
#[instability::unstable]
pub const unsafe fn new_unchecked(ptr: &'a T) -> Self {
Self(ptr)
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
fn do_cache_op(&self) -> bool {
region_needs_cache_op(
self.0 as *const T as *const () as usize,
core::mem::size_of_val(self.0),
)
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
#[instability::unstable]
pub fn invalidate(&self) {
if !self.do_cache_op() {
return;
}
unsafe {
crate::soc::cache_invalidate_addr(
self.0 as *const T as *const () as u32,
core::mem::size_of_val(self.0) as u32,
);
}
}
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
#[instability::unstable]
pub fn writeback(&self) {
if !self.do_cache_op() {
return;
}
unsafe {
crate::soc::cache_writeback_addr(
self.0 as *const T as *const () as u32,
core::mem::size_of_val(self.0) as u32,
);
}
}
pub fn into_inner(self) -> &'a T {
self.0
}
}
impl<'a, T: ?Sized> Deref for DmaAlignedRef<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0
}
}