#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use core::{
fmt,
num::{NonZeroU16, NonZeroU32},
};
use dma_api::{CompletedDma, DmaDirection, PreparedDma};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Command {
pub index: u8,
pub argument: u32,
pub response: ResponseType,
}
impl Command {
pub const fn new(index: u8, argument: u32, response: ResponseType) -> Self {
Self {
index,
argument,
response,
}
}
pub const fn index(self) -> u8 {
self.index
}
pub const fn argument(self) -> u32 {
self.argument
}
pub const fn with_response(self, response: ResponseType) -> Self {
Self { response, ..self }
}
pub const fn with_resp_type(self, response: ResponseType) -> Self {
self.with_response(response)
}
pub const fn cmd(self) -> u8 {
self.index
}
pub const fn arg(self) -> u32 {
self.argument
}
pub const fn data_direction(&self) -> Option<DataDirection> {
match self.index {
17 | 18 => Some(DataDirection::Read),
24 | 25 => Some(DataDirection::Write),
_ => None,
}
}
pub const fn data_block_size(&self) -> Option<u32> {
match self.index {
17 | 18 | 24 | 25 => Some(512),
_ => None,
}
}
pub fn crc7(&self) -> u8 {
let mut crc: u8 = 0;
let token: u8 = 0x40 | (self.index & 0x3F);
crc = crc7_update(crc, token);
for byte in self.argument.to_be_bytes() {
crc = crc7_update(crc, byte);
}
(crc << 1) | 1
}
pub fn to_spi_bytes(&self) -> [u8; 6] {
let crc = self.crc7();
let token = 0x40 | (self.index & 0x3F);
let arg = self.argument.to_be_bytes();
[token, arg[0], arg[1], arg[2], arg[3], crc]
}
}
fn crc7_update(crc: u8, byte: u8) -> u8 {
let mut crc = crc;
let mut data = byte;
for _ in 0..8 {
crc <<= 1;
if (crc ^ data) & 0x80 != 0 {
crc ^= 0x89;
}
data <<= 1;
}
crc
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ResponseType {
None,
R1,
R1b,
R2,
R3,
R4,
R5,
R6,
R7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RawResponse {
pub ty: ResponseType,
pub words: [u32; 4],
}
impl RawResponse {
pub const fn new(ty: ResponseType, words: [u32; 4]) -> Self {
Self { ty, words }
}
pub const fn empty() -> Self {
Self {
ty: ResponseType::None,
words: [0; 4],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataDirection {
Read,
Write,
}
pub enum DataBuffer<'a> {
Read(&'a mut [u8]),
Write(&'a [u8]),
Dma(PreparedDma),
}
impl DataBuffer<'_> {
pub fn len(&self) -> usize {
match self {
Self::Read(buf) => buf.len(),
Self::Write(buf) => buf.len(),
Self::Dma(buffer) => buffer.len().get(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn matches_direction(&self, direction: DataDirection) -> bool {
match self {
Self::Read(_) => direction == DataDirection::Read,
Self::Write(_) => direction == DataDirection::Write,
Self::Dma(buffer) => matches!(
(buffer.direction(), direction),
(DmaDirection::FromDevice, DataDirection::Read)
| (DmaDirection::ToDevice, DataDirection::Write)
| (DmaDirection::Bidirectional, _)
),
}
}
}
pub type DataTransfer<'a> = DataBuffer<'a>;
pub struct DmaPhaseError {
error: Error,
buffer: Box<PreparedDma>,
}
impl DmaPhaseError {
fn new(error: Error, buffer: PreparedDma) -> Self {
Self {
error,
buffer: Box::new(buffer),
}
}
pub const fn error(&self) -> Error {
self.error
}
pub fn into_buffer(self) -> PreparedDma {
*self.buffer
}
pub fn into_parts(self) -> (Error, PreparedDma) {
(self.error, *self.buffer)
}
}
impl fmt::Debug for DmaPhaseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DmaPhaseError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl fmt::Display for DmaPhaseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.error.fmt(f)
}
}
impl core::error::Error for DmaPhaseError {}
pub struct DataPhase<'a> {
pub direction: DataDirection,
pub block_size: NonZeroU16,
pub block_count: NonZeroU32,
pub buffer: DataBuffer<'a>,
}
impl<'a> DataPhase<'a> {
pub fn read(
block_size: NonZeroU16,
block_count: NonZeroU32,
buffer: &'a mut [u8],
) -> Result<Self, Error> {
let phase = Self {
direction: DataDirection::Read,
block_size,
block_count,
buffer: DataBuffer::Read(buffer),
};
phase.validate()?;
Ok(phase)
}
pub fn write(
block_size: NonZeroU16,
block_count: NonZeroU32,
buffer: &'a [u8],
) -> Result<Self, Error> {
let phase = Self {
direction: DataDirection::Write,
block_size,
block_count,
buffer: DataBuffer::Write(buffer),
};
phase.validate()?;
Ok(phase)
}
pub fn dma(
direction: DataDirection,
block_size: NonZeroU16,
block_count: NonZeroU32,
buffer: PreparedDma,
) -> Result<Self, DmaPhaseError> {
let phase = Self {
direction,
block_size,
block_count,
buffer: DataBuffer::Dma(buffer),
};
match phase.validate() {
Ok(()) => Ok(phase),
Err(err) => {
let DataBuffer::Dma(buffer) = phase.buffer else {
unreachable!("DataPhase::dma always stores a DMA buffer")
};
Err(DmaPhaseError::new(err, buffer))
}
}
}
pub fn validate(&self) -> Result<(), Error> {
let expected = usize::from(self.block_size.get())
.checked_mul(
usize::try_from(self.block_count.get()).map_err(|_| Error::InvalidArgument)?,
)
.ok_or(Error::InvalidArgument)?;
if self.buffer.len() != expected {
return Err(Error::InvalidArgument);
}
if !self.buffer.matches_direction(self.direction) {
return Err(Error::InvalidArgument);
}
Ok(())
}
}
pub struct Transaction<'a> {
pub command: Command,
pub data: Option<DataPhase<'a>>,
}
impl<'a> Transaction<'a> {
pub const fn command(command: Command) -> Self {
Self {
command,
data: None,
}
}
pub const fn with_data(command: Command, data: DataPhase<'a>) -> Self {
Self {
command,
data: Some(data),
}
}
}
pub struct SubmitTransactionError<'a> {
pub error: Error,
transaction: Option<Box<Transaction<'a>>>,
}
impl<'a> SubmitTransactionError<'a> {
pub fn new(error: Error, transaction: Transaction<'a>) -> Self {
Self {
error,
transaction: Some(Box::new(transaction)),
}
}
pub const fn consumed(error: Error) -> Self {
Self {
error,
transaction: None,
}
}
pub fn into_transaction(self) -> Option<Transaction<'a>> {
self.transaction.map(|transaction| *transaction)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestPoll<T> {
Pending,
Ready(Result<T, Error>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PollRequestError {
WrongOwner,
WrongKind,
AlreadyCompleted,
StaleGeneration,
RecoveryFailed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum BusWidth {
Bit1,
Bit4,
Bit8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ClockSpeed {
Identification,
Default,
HighSpeed,
Sdr12,
Sdr25,
Sdr50,
Sdr104,
Ddr50,
Hs200,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ClockHz(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SignalVoltage {
V330,
V180,
V120,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum BusOp {
ResetAll,
ResetCommandLine,
ResetDataLine,
PowerOn,
PowerOff,
SetClock(ClockSpeed),
SetClockHz(ClockHz),
SetBusWidth(BusWidth),
SetSignalVoltage(SignalVoltage),
ExecuteTuning {
command: Command,
block_size: NonZeroU16,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Busy,
Timeout,
Crc,
NoCard,
Unsupported,
InvalidArgument,
Misaligned,
Bus,
Controller,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Busy => "host bus is busy",
Self::Timeout => "host bus timeout",
Self::Crc => "host bus CRC error",
Self::NoCard => "no card present",
Self::Unsupported => "operation is not supported",
Self::InvalidArgument => "invalid host bus argument",
Self::Misaligned => "misaligned host bus buffer",
Self::Bus => "host bus error",
Self::Controller => "host controller error",
};
f.write_str(s)
}
}
impl core::error::Error for Error {}
impl fmt::Display for PollRequestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::WrongOwner => "request belongs to a different host",
Self::WrongKind => "request was polled through the wrong operation kind",
Self::AlreadyCompleted => "request has already completed",
Self::StaleGeneration => "request generation is no longer active",
Self::RecoveryFailed => "request recovery failed",
};
f.write_str(s)
}
}
impl core::error::Error for PollRequestError {}
pub trait SdioHost {
type TransactionRequest<'a>
where
Self: 'a;
type BusRequest;
unsafe fn submit_transaction<'a>(
&mut self,
transaction: Transaction<'a>,
) -> Result<Self::TransactionRequest<'a>, Error>
where
Self: 'a;
unsafe fn submit_transaction_owned<'a>(
&mut self,
transaction: Transaction<'a>,
) -> Result<Self::TransactionRequest<'a>, SubmitTransactionError<'a>>
where
Self: 'a,
{
match unsafe { self.submit_transaction(transaction) } {
Ok(request) => Ok(request),
Err(error) => Err(SubmitTransactionError::consumed(error)),
}
}
fn poll_transaction<'a>(
&mut self,
request: &mut Self::TransactionRequest<'a>,
) -> Result<RequestPoll<RawResponse>, PollRequestError>
where
Self: 'a;
fn abort_transaction<'a>(
&mut self,
request: &mut Self::TransactionRequest<'a>,
) -> Result<(), Error>
where
Self: 'a;
fn take_completed_dma<'a>(
&mut self,
_request: &mut Self::TransactionRequest<'a>,
) -> Option<CompletedDma>
where
Self: 'a,
{
None
}
unsafe fn submit_bus_op(&mut self, op: BusOp) -> Result<Self::BusRequest, Error>;
fn poll_bus_op(
&mut self,
request: &mut Self::BusRequest,
) -> Result<RequestPoll<()>, PollRequestError>;
fn abort_bus_op(&mut self, request: &mut Self::BusRequest) -> Result<(), Error>;
fn now_ms(&self) -> Option<u64> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockHost {
busy: bool,
}
#[derive(Debug)]
struct MockTransactionRequest {
response: RawResponse,
pending_once: bool,
done: bool,
}
#[derive(Debug)]
struct MockBusRequest {
pending_once: bool,
done: bool,
}
impl SdioHost for MockHost {
type TransactionRequest<'a>
= MockTransactionRequest
where
Self: 'a;
type BusRequest = MockBusRequest;
unsafe fn submit_transaction<'a>(
&mut self,
transaction: Transaction<'a>,
) -> Result<Self::TransactionRequest<'a>, Error>
where
Self: 'a,
{
if self.busy {
return Err(Error::Busy);
}
self.busy = true;
Ok(MockTransactionRequest {
response: RawResponse::new(transaction.command.response, [0x1234, 0, 0, 0]),
pending_once: true,
done: false,
})
}
fn poll_transaction<'a>(
&mut self,
request: &mut Self::TransactionRequest<'a>,
) -> Result<RequestPoll<RawResponse>, PollRequestError>
where
Self: 'a,
{
if request.done {
return Err(PollRequestError::AlreadyCompleted);
}
if request.pending_once {
request.pending_once = false;
return Ok(RequestPoll::Pending);
}
self.busy = false;
request.done = true;
Ok(RequestPoll::Ready(Ok(request.response)))
}
fn abort_transaction<'a>(
&mut self,
request: &mut Self::TransactionRequest<'a>,
) -> Result<(), Error>
where
Self: 'a,
{
request.done = true;
self.busy = false;
Ok(())
}
unsafe fn submit_bus_op(&mut self, _op: BusOp) -> Result<Self::BusRequest, Error> {
if self.busy {
return Err(Error::Busy);
}
self.busy = true;
Ok(MockBusRequest {
pending_once: false,
done: false,
})
}
fn poll_bus_op(
&mut self,
request: &mut Self::BusRequest,
) -> Result<RequestPoll<()>, PollRequestError> {
if request.done {
return Err(PollRequestError::AlreadyCompleted);
}
if request.pending_once {
request.pending_once = false;
return Ok(RequestPoll::Pending);
}
self.busy = false;
request.done = true;
Ok(RequestPoll::Ready(Ok(())))
}
fn abort_bus_op(&mut self, request: &mut Self::BusRequest) -> Result<(), Error> {
request.done = true;
self.busy = false;
Ok(())
}
}
#[test]
fn data_phase_validates_buffer_shape() {
let mut read = [0u8; 1024];
let block = NonZeroU16::new(512).unwrap();
let phase = DataPhase::read(block, NonZeroU32::new(2).unwrap(), &mut read).unwrap();
assert_eq!(phase.direction, DataDirection::Read);
assert_eq!(phase.buffer.len(), 1024);
}
#[test]
fn host_reports_busy_for_second_active_transaction() {
let mut host = MockHost { busy: false };
let cmd = Command::new(17, 0, ResponseType::R1);
let mut request = unsafe { host.submit_transaction(Transaction::command(cmd)) }.unwrap();
assert_eq!(
unsafe { host.submit_transaction(Transaction::command(cmd)) }.unwrap_err(),
Error::Busy
);
assert_eq!(
host.poll_transaction(&mut request),
Ok(RequestPoll::Pending)
);
assert!(matches!(
host.poll_transaction(&mut request),
Ok(RequestPoll::Ready(Ok(_)))
));
assert_eq!(
host.poll_transaction(&mut request),
Err(PollRequestError::AlreadyCompleted)
);
assert!(unsafe { host.submit_transaction(Transaction::command(cmd)) }.is_ok());
}
#[test]
fn bus_op_uses_same_single_active_contract() {
let mut host = MockHost { busy: false };
let _request = unsafe { host.submit_bus_op(BusOp::SetClock(ClockSpeed::Default)) }.unwrap();
assert_eq!(
unsafe { host.submit_bus_op(BusOp::SetBusWidth(BusWidth::Bit4)) }.unwrap_err(),
Error::Busy
);
}
#[test]
fn abort_releases_single_active_contract() {
let mut host = MockHost { busy: false };
let cmd = Command::new(17, 0, ResponseType::R1);
let mut request = unsafe { host.submit_transaction(Transaction::command(cmd)) }.unwrap();
host.abort_transaction(&mut request).unwrap();
assert!(unsafe { host.submit_transaction(Transaction::command(cmd)) }.is_ok());
assert_eq!(
host.poll_transaction(&mut request),
Err(PollRequestError::AlreadyCompleted)
);
}
}