sdmmc-protocol 0.4.1

no_std SD/MMC protocol building blocks for embedded systems
Documentation
use rdif_block::{
    BlkError,
    dma_api::{self, DeviceDma},
};

use crate::{BlockTransferMode, Error};

pub const BLOCK_SIZE: usize = 512;
pub const DEFAULT_DMA_MASK: u64 = u32::MAX as u64;
pub const DEFAULT_DMA_MAX_BLOCKS_PER_REQUEST: u32 = u16::MAX as u32 + 1;

#[derive(Clone)]
pub struct BlockConfig {
    pub name: &'static str,
    pub capacity_blocks: u64,
    pub dma_mask: u64,
    pub dma_domain: dma_api::DmaDomainId,
    pub max_blocks_per_request: u32,
    pub max_segment_size: usize,
    pub irq_driven: bool,
    pub dma: Option<DeviceDma>,
}

impl BlockConfig {
    pub fn dma(name: &'static str, capacity_blocks: u64, irq_driven: bool, dma: DeviceDma) -> Self {
        let dma_mask = dma.dma_mask();
        Self {
            name,
            capacity_blocks,
            dma_mask,
            dma_domain: dma.domain_id(),
            max_blocks_per_request: DEFAULT_DMA_MAX_BLOCKS_PER_REQUEST,
            max_segment_size: usize::MAX,
            irq_driven,
            dma: Some(dma),
        }
    }

    pub const fn fifo(name: &'static str, capacity_blocks: u64, irq_driven: bool) -> Self {
        Self {
            name,
            capacity_blocks,
            dma_mask: DEFAULT_DMA_MASK,
            dma_domain: dma_api::DmaDomainId::legacy_global(),
            max_blocks_per_request: 1,
            max_segment_size: BLOCK_SIZE,
            irq_driven,
            dma: None,
        }
    }

    pub fn with_dma_mask(mut self, dma_mask: u64) -> Self {
        self.dma_mask = dma_mask;
        self
    }

    pub fn with_max_blocks_per_request(mut self, max_blocks_per_request: u32) -> Self {
        self.max_blocks_per_request = max_blocks_per_request;
        self
    }

    pub fn with_max_segment_size(mut self, max_segment_size: usize) -> Self {
        self.max_segment_size = max_segment_size;
        self
    }

    pub fn with_irq_driven(mut self, irq_driven: bool) -> Self {
        self.irq_driven = irq_driven;
        self
    }

    pub fn with_dma(mut self, dma: DeviceDma) -> Self {
        self.dma_mask = dma.dma_mask();
        self.dma_domain = dma.domain_id();
        self.dma = Some(dma);
        self
    }

    pub const fn uses_dma(&self) -> bool {
        self.dma.is_some()
    }
}

pub fn queue_limits(config: &BlockConfig, dma_mask: u64) -> rdif_block::QueueLimits {
    rdif_block::QueueLimits {
        dma_mask,
        dma_domain: config.dma_domain,
        dma_alignment: BLOCK_SIZE,
        max_inflight: 1,
        max_blocks_per_request: config.max_blocks_per_request,
        max_segments: 1,
        max_segment_size: config.max_segment_size,
        supported_flags: rdif_block::RequestFlags::NONE,
        supports_flush: false,
        supports_discard: false,
        supports_write_zeroes: false,
    }
}

pub fn device_info(config: &BlockConfig) -> rdif_block::DeviceInfo {
    rdif_block::DeviceInfo {
        name: Some(config.name),
        ..rdif_block::DeviceInfo::new(config.capacity_blocks, BLOCK_SIZE)
    }
}

pub fn block_addr_for_card(block_id: u64, high_capacity: bool) -> Result<u32, BlkError> {
    let block_id = u32::try_from(block_id).map_err(|_| BlkError::InvalidBlockIndex(block_id))?;
    if high_capacity {
        Ok(block_id)
    } else {
        block_id
            .checked_mul(BLOCK_SIZE as u32)
            .ok_or(BlkError::InvalidBlockIndex(block_id as u64))
    }
}

pub fn map_dev_err_to_blk_err(err: Error) -> BlkError {
    match err {
        Error::Busy => BlkError::Retry,
        Error::NoCard | Error::UnsupportedCommand | Error::CardLocked => BlkError::NotSupported,
        Error::Misaligned | Error::InvalidArgument => {
            BlkError::Other("SD/MMC request is not block aligned")
        }
        _ => BlkError::Io,
    }
}

pub fn transfer_mode_for_dma(dma: Option<&DeviceDma>) -> BlockTransferMode {
    match dma {
        Some(_) => BlockTransferMode::Dma,
        None => BlockTransferMode::Fifo,
    }
}

pub(super) fn should_split_fifo_request(dma: Option<&DeviceDma>, block_count: u32) -> bool {
    dma.is_none() && block_count > 1
}

pub fn can_fallback_to_fifo(err: Error) -> bool {
    matches!(
        err,
        Error::UnsupportedCommand | Error::InvalidArgument | Error::Misaligned
    )
}