1use core::{mem::ManuallyDrop, num::NonZeroUsize, ptr::NonNull};
2
3use crate::{ContiguousArray, DeviceDma, DmaAddr, DmaDirection, DmaDomainId, DmaError};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct DmaSegment {
8 pub addr: DmaAddr,
9 pub len: NonZeroUsize,
10}
11
12impl DmaSegment {
13 pub const fn new(addr: DmaAddr, len: NonZeroUsize) -> Self {
14 Self { addr, len }
15 }
16}
17
18pub struct CpuDmaBuffer {
20 backing: ContiguousArray<u8>,
21 direction: DmaDirection,
22 domain: DmaDomainId,
23}
24
25impl CpuDmaBuffer {
26 pub fn new_zero(
27 device: &DeviceDma,
28 len: NonZeroUsize,
29 align: usize,
30 direction: DmaDirection,
31 ) -> Result<Self, DmaError> {
32 let backing =
33 device.contiguous_array_zero_with_align(len.get(), align.max(1), direction)?;
34 Ok(Self::from_contiguous(backing))
35 }
36
37 pub fn from_contiguous(backing: ContiguousArray<u8>) -> Self {
38 assert!(
39 !backing.is_empty(),
40 "CpuDmaBuffer backing must be non-empty"
41 );
42 let direction = backing.direction();
43 let domain = backing.domain_id();
44 Self {
45 backing,
46 direction,
47 domain,
48 }
49 }
50
51 pub fn len(&self) -> NonZeroUsize {
52 NonZeroUsize::new(self.backing.bytes_len())
53 .expect("CpuDmaBuffer never owns zero-sized backing")
54 }
55
56 pub fn is_empty(&self) -> bool {
57 false
58 }
59
60 pub const fn direction(&self) -> DmaDirection {
61 self.direction
62 }
63
64 pub const fn domain_id(&self) -> DmaDomainId {
65 self.domain
66 }
67
68 pub fn cpu_ptr(&self) -> NonNull<u8> {
69 self.backing.as_ptr()
70 }
71
72 pub fn dma_addr(&self) -> DmaAddr {
73 self.backing.dma_addr()
74 }
75
76 pub fn segment(&self) -> DmaSegment {
77 DmaSegment::new(self.dma_addr(), self.len())
78 }
79
80 pub fn as_slice_cpu(&self) -> &[u8] {
81 self.backing.as_slice_cpu()
82 }
83
84 pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [u8] {
89 unsafe { self.backing.as_mut_slice_cpu() }
90 }
91
92 pub fn copy_to_device_from_slice(&mut self, src: &[u8]) {
93 self.backing.copy_to_device_from_slice(src);
94 }
95
96 pub fn copy_from_device_to_slice(&self, dst: &mut [u8]) {
97 self.backing.copy_from_device_to_slice(dst);
98 }
99
100 pub fn prepare_for_device_all(&self) {
101 self.backing.prepare_for_device_all();
102 }
103
104 pub fn complete_for_cpu_all(&self) {
105 self.backing.complete_for_cpu_all();
106 }
107
108 pub fn prepare_for_device(self) -> PreparedDma {
109 self.prepare_for_device_all();
110 PreparedDma { buffer: self }
111 }
112}
113
114pub struct PreparedDma {
116 buffer: CpuDmaBuffer,
117}
118
119impl PreparedDma {
120 pub fn len(&self) -> NonZeroUsize {
121 self.buffer.len()
122 }
123
124 pub const fn direction(&self) -> DmaDirection {
125 self.buffer.direction()
126 }
127
128 pub const fn domain_id(&self) -> DmaDomainId {
129 self.buffer.domain_id()
130 }
131
132 pub fn cpu_ptr(&self) -> NonNull<u8> {
133 self.buffer.cpu_ptr()
134 }
135
136 pub fn dma_addr(&self) -> DmaAddr {
137 self.buffer.dma_addr()
138 }
139
140 pub fn segment(&self) -> DmaSegment {
141 self.buffer.segment()
142 }
143
144 pub fn segments(&self) -> [DmaSegment; 1] {
145 [self.segment()]
146 }
147
148 pub fn into_cpu_buffer(self) -> CpuDmaBuffer {
149 self.buffer
150 }
151
152 pub unsafe fn into_in_flight(self) -> InFlightDma {
157 InFlightDma {
158 prepared: ManuallyDrop::new(self),
159 }
160 }
161}
162
163pub struct InFlightDma {
169 prepared: ManuallyDrop<PreparedDma>,
170}
171
172impl InFlightDma {
173 pub fn len(&self) -> NonZeroUsize {
174 self.prepared.len()
175 }
176
177 pub fn direction(&self) -> DmaDirection {
178 self.prepared.direction()
179 }
180
181 pub fn domain_id(&self) -> DmaDomainId {
182 self.prepared.domain_id()
183 }
184
185 pub fn cpu_ptr(&self) -> NonNull<u8> {
186 self.prepared.cpu_ptr()
187 }
188
189 pub fn dma_addr(&self) -> DmaAddr {
190 self.prepared.dma_addr()
191 }
192
193 pub fn segment(&self) -> DmaSegment {
194 self.prepared.segment()
195 }
196
197 pub unsafe fn complete_after_quiesce(mut self) -> CompletedDma {
202 let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
203 if matches!(
204 prepared.buffer.direction(),
205 DmaDirection::FromDevice | DmaDirection::Bidirectional
206 ) {
207 prepared.buffer.complete_for_cpu_all();
208 }
209 CompletedDma {
210 buffer: prepared.buffer,
211 }
212 }
213
214 pub fn quarantine(mut self) -> QuarantinedDma {
215 let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
216 QuarantinedDma {
217 prepared: ManuallyDrop::new(prepared),
218 }
219 }
220}
221
222pub struct CompletedDma {
224 buffer: CpuDmaBuffer,
225}
226
227impl CompletedDma {
228 pub fn len(&self) -> NonZeroUsize {
229 self.buffer.len()
230 }
231
232 pub const fn direction(&self) -> DmaDirection {
233 self.buffer.direction()
234 }
235
236 pub fn copy_from_device_to_slice(&self, dst: &mut [u8]) {
237 self.buffer.copy_from_device_to_slice(dst);
238 }
239
240 pub fn into_cpu_buffer(self) -> CpuDmaBuffer {
241 self.buffer
242 }
243}
244
245pub struct QuarantinedDma {
250 prepared: ManuallyDrop<PreparedDma>,
251}
252
253impl QuarantinedDma {
254 pub fn len(&self) -> NonZeroUsize {
255 self.prepared.len()
256 }
257
258 pub fn domain_id(&self) -> DmaDomainId {
259 self.prepared.domain_id()
260 }
261}