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 fn complete_without_device(self) -> CompletedDma {
157 if matches!(
158 self.buffer.direction(),
159 DmaDirection::FromDevice | DmaDirection::Bidirectional
160 ) {
161 self.buffer.complete_for_cpu_all();
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 cpu_ptr(&self) -> NonNull<u8> {
202 self.prepared.cpu_ptr()
203 }
204
205 pub fn dma_addr(&self) -> DmaAddr {
206 self.prepared.dma_addr()
207 }
208
209 pub fn segment(&self) -> DmaSegment {
210 self.prepared.segment()
211 }
212
213 pub unsafe fn complete_after_quiesce(mut self) -> CompletedDma {
218 let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
219 if matches!(
220 prepared.buffer.direction(),
221 DmaDirection::FromDevice | DmaDirection::Bidirectional
222 ) {
223 prepared.buffer.complete_for_cpu_all();
224 }
225 CompletedDma {
226 buffer: prepared.buffer,
227 }
228 }
229
230 pub fn quarantine(mut self) -> QuarantinedDma {
231 let prepared = unsafe { ManuallyDrop::take(&mut self.prepared) };
232 QuarantinedDma {
233 prepared: ManuallyDrop::new(prepared),
234 }
235 }
236}
237
238pub struct CompletedDma {
240 buffer: CpuDmaBuffer,
241}
242
243impl CompletedDma {
244 pub fn len(&self) -> NonZeroUsize {
245 self.buffer.len()
246 }
247
248 pub const fn direction(&self) -> DmaDirection {
249 self.buffer.direction()
250 }
251
252 pub fn copy_from_device_to_slice(&self, dst: &mut [u8]) {
253 self.buffer.copy_from_device_to_slice(dst);
254 }
255
256 pub fn into_cpu_buffer(self) -> CpuDmaBuffer {
257 self.buffer
258 }
259}
260
261pub struct QuarantinedDma {
266 prepared: ManuallyDrop<PreparedDma>,
267}
268
269impl QuarantinedDma {
270 pub fn len(&self) -> NonZeroUsize {
271 self.prepared.len()
272 }
273
274 pub fn domain_id(&self) -> DmaDomainId {
275 self.prepared.domain_id()
276 }
277}