//! Non-blocking, chip-select-scoped SPI transfer contract. //! //! A selected device owns electrical chip-select behavior and asynchronous //! transfer storage. Submission copies in; successful completion copies out. //! No caller borrow or caller address may survive either method call. #![no_std] pub mod dma; /// Terminal outcome reported by a selected SPI device. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SpiCompletion { /// The bound interrupt does not report a terminal event. NotPending, /// The submitted full-duplex transfer completed. Complete, /// The submitted transfer failed and its receive bytes are unusable. TransferFault, } /// Failure to accept and copy out a completed transfer. No receive bytes are /// changed on error. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CompletionError { /// No terminal event: leave the active transfer entirely untouched. NotPending, /// There is no submitted transfer to complete. NotActive, /// The destination length differs from the submitted length. InvalidLength, /// The transfer failed; no receive bytes are usable. TransferFault, /// Quiescence could not be proven. Storage and route are permanently held. Quarantined, } /// Owns one chip-select-scoped, non-blocking SPI route. /// /// Moving, dropping, or forgetting the device or caller buffers during a /// transfer must be memory-safe. Hardware accesses only implementation-owned /// storage whose allocation outlives hardware activity, including cancellation. pub trait SpiDevice { /// Failure to submit a transfer. type StartError; /// Identifies a temporary admission rejection. No transfer was accepted. /// Drivers may retry the same pending operation within their deadline. fn is_busy(_error: &Self::StartError) -> bool { false } /// Copies `transmit` into bounded route-owned storage and starts one /// full-duplex transfer. Empty/oversized submissions must fail safely. /// /// No pointer into `transmit` is retained. Chip select is asserted before /// submission; rejecting a concurrent submission must not disturb it. fn start_transfer(&mut self, transmit: &[u8]) -> Result<(), Self::StartError>; /// On `Complete`, proves hardware quiescence before copying exactly the /// submitted number of bytes to an equally sized `receive` and releasing /// chip select. An interrupt notification alone is not proof of quiescence. /// /// `NotPending` returns that error without aborting or changing anything. /// Faults cancel the transfer without copying out. Any bounded hardware /// shutdown that cannot prove quiescence must quarantine the route and its /// storage permanently, preventing reuse. No destination pointer is retained. /// Passing `TransferFault` also requests cancellation without an IRQ. It must /// perform the same bounded shutdown and return `TransferFault` only after /// quiescence, or `Quarantined` if that cannot be proved. No bytes are copied. /// /// Providers must validate current hardware progress, not trust a notification /// from a previous transfer. Before reuse, drain old hardware flags/data. A /// delayed notification may fault a new incomplete transfer but must never /// publish old receive storage as a new result. fn complete_transfer( &mut self, completion: SpiCompletion, receive: &mut [u8], ) -> Result<(), CompletionError>; }