use core::{mem::ManuallyDrop, num::NonZeroUsize, ptr::NonNull};
use crate::{ContiguousArray, DeviceDma, DmaAddr, DmaDirection, DmaDomainId, DmaError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DmaSegment {
pub addr: DmaAddr,
pub len: NonZeroUsize,
}
impl DmaSegment {
pub const fn new(addr: DmaAddr, len: NonZeroUsize) -> Self {
Self { addr, len }
}
}
pub struct CpuDmaBuffer {
backing: ContiguousArray<u8>,
direction: DmaDirection,
domain: DmaDomainId,
}
impl CpuDmaBuffer {
pub fn new_zero(
device: &DeviceDma,
len: NonZeroUsize,
align: usize,
direction: DmaDirection,
) -> Result<Self, DmaError> {
let backing =
device.contiguous_array_zero_with_align(len.get(), align.max(1), direction)?;
Ok(Self::from_contiguous(backing))
}
pub fn from_contiguous(backing: ContiguousArray<u8>) -> Self {
assert!(
!backing.is_empty(),
"CpuDmaBuffer backing must be non-empty"
);
let direction = backing.direction();
let domain = backing.domain_id();
Self {
backing,
direction,
domain,
}
}
pub fn len(&self) -> NonZeroUsize {
NonZeroUsize::new(self.backing.bytes_len())
.expect("CpuDmaBuffer never owns zero-sized backing")
}
pub fn is_empty(&self) -> bool {
false
}
pub const fn direction(&self) -> DmaDirection {
self.direction
}
pub const fn domain_id(&self) -> DmaDomainId {
self.domain
}
pub fn cpu_ptr(&self) -> NonNull<u8> {
self.backing.as_ptr()
}
pub fn dma_addr(&self) -> DmaAddr {
self.backing.dma_addr()
}
pub fn segment(&self) -> DmaSegment {
DmaSegment::new(self.dma_addr(), self.len())
}
pub fn as_slice_cpu(&self) -> &[u8] {
self.backing.as_slice_cpu()
}
pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [u8] {
unsafe { self.backing.as_mut_slice_cpu() }
}
pub fn copy_to_device_from_slice(&mut self, src: &[u8]) {
self.backing.copy_to_device_from_slice(src);
}
pub fn copy_from_device_to_slice(&self, dst: &mut [u8]) {
self.backing.copy_from_device_to_slice(dst);
}
pub fn prepare_for_device_all(&self) {
self.backing.prepare_for_device_all();
}
pub fn complete_for_cpu_all(&self) {
self.backing.complete_for_cpu_all();
}
pub fn prepare_for_device(self) -> PreparedDma {
self.prepare_for_device_all();
PreparedDma { buffer: self }
}
}
pub struct PreparedDma {
buffer: CpuDmaBuffer,
}
impl PreparedDma {
pub fn len(&self) -> NonZeroUsize {
self.buffer.len()
}
pub const fn direction(&self) -> DmaDirection {
self.buffer.direction()
}
pub const fn domain_id(&self) -> DmaDomainId {
self.buffer.domain_id()
}
pub fn cpu_ptr(&self) -> NonNull<u8> {
self.buffer.cpu_ptr()
}
pub fn dma_addr(&self) -> DmaAddr {
self.buffer.dma_addr()
}
pub fn segment(&self) -> DmaSegment {
self.buffer.segment()
}
pub fn segments(&self) -> [DmaSegment; 1] {
[self.segment()]
}
pub fn into_cpu_buffer(self) -> CpuDmaBuffer {
self.buffer
}
pub unsafe fn into_in_flight(self) -> InFlightDma {
InFlightDma {
prepared: ManuallyDrop::new(self),
}
}
}
pub struct InFlightDma {
prepared: ManuallyDrop<PreparedDma>,
}
impl InFlightDma {
pub fn len(&self) -> NonZeroUsize {
self.prepared.len()
}
pub fn direction(&self) -> DmaDirection {
self.prepared.direction()
}
pub fn domain_id(&self) -> DmaDomainId {
self.prepared.domain_id()
}
pub fn cpu_ptr(&self) -> NonNull<u8> {
self.prepared.cpu_ptr()
}
pub fn dma_addr(&self) -> DmaAddr {
self.prepared.dma_addr()
}
pub fn segment(&self) -> DmaSegment {
self.prepared.segment()
}
pub unsafe fn complete_after_quiesce(mut self) -> CompletedDma {
let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
if matches!(
prepared.buffer.direction(),
DmaDirection::FromDevice | DmaDirection::Bidirectional
) {
prepared.buffer.complete_for_cpu_all();
}
CompletedDma {
buffer: prepared.buffer,
}
}
pub fn quarantine(mut self) -> QuarantinedDma {
let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
QuarantinedDma {
prepared: ManuallyDrop::new(prepared),
}
}
}
pub struct CompletedDma {
buffer: CpuDmaBuffer,
}
impl CompletedDma {
pub fn len(&self) -> NonZeroUsize {
self.buffer.len()
}
pub const fn direction(&self) -> DmaDirection {
self.buffer.direction()
}
pub fn copy_from_device_to_slice(&self, dst: &mut [u8]) {
self.buffer.copy_from_device_to_slice(dst);
}
pub fn into_cpu_buffer(self) -> CpuDmaBuffer {
self.buffer
}
}
pub struct QuarantinedDma {
prepared: ManuallyDrop<PreparedDma>,
}
impl QuarantinedDma {
pub fn len(&self) -> NonZeroUsize {
self.prepared.len()
}
pub fn domain_id(&self) -> DmaDomainId {
self.prepared.domain_id()
}
}