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