embedded_dma/
lib.rs

1//! Traits to aid the correct use of buffers in DMA abstractions.
2//!
3//! This library provides the [`ReadBuffer`] and [`WriteBuffer`] unsafe traits to be used as bounds to
4//! buffers types used in DMA operations.
5//!
6//! There are some subtleties to the extent of the guarantees provided by these traits, all of these
7//! subtleties are properly documented in the safety requirements in this crate. However, as a
8//! measure of redundancy, some are listed below:
9//!
10//! * The traits only guarantee a stable location while no `&mut self` methods are called upon
11//! `Self` (with the exception of [`write_buffer`](trait.WriteBuffer.html#tymethod.write_buffer) in
12//! our case). This is to allow types like `Vec`, this restriction doesn't apply to `Self::Target`.
13//!
14//! * [`ReadBuffer`] and [`WriteBuffer`] guarantee a stable location for as long as the DMA transfer
15//! occurs. Given the intrinsics of `mem::forget` and the Rust language itself, a
16//! 'static lifetime is usually required.
17//!
18//! The above list is not exhaustive, for a complete set of requirements and guarantees, the
19//! documentation of each trait and method should be analyzed.
20#![no_std]
21
22use core::{
23    mem::{self, MaybeUninit},
24    ops::{Deref, DerefMut},
25};
26use stable_deref_trait::StableDeref;
27
28/// Trait for buffers that can be given to DMA for reading.
29///
30/// # Safety
31///
32/// The implementing type must be safe to use for DMA reads. This means:
33///
34/// - It must be a pointer that references the actual buffer.
35/// - As long as no `&mut self` method is called on the implementing object:
36///   - `read_buffer` must always return the same value, if called multiple
37///     times.
38///   - The memory specified by the pointer and size returned by `read_buffer`
39///     must not be freed during the transfer it is used in as long as `self` is not dropped.
40pub unsafe trait ReadBuffer {
41    type Word;
42
43    /// Provide a buffer usable for DMA reads.
44    ///
45    /// The return value is:
46    ///
47    /// - pointer to the start of the buffer
48    /// - buffer size in words
49    ///
50    /// # Safety
51    ///
52    /// Once this method has been called, it is unsafe to call any `&mut self`
53    /// methods on this object as long as the returned value is in use (by DMA).
54    unsafe fn read_buffer(&self) -> (*const Self::Word, usize);
55}
56
57/// Trait for buffers that can be given to DMA for writing.
58///
59/// # Safety
60///
61/// The implementing type must be safe to use for DMA writes. This means:
62///
63/// - It must be a pointer that references the actual buffer.
64/// - `Target` must be a type that is valid for any possible byte pattern.
65/// - As long as no `&mut self` method, except for `write_buffer`, is called on
66///   the implementing object:
67///   - `write_buffer` must always return the same value, if called multiple
68///     times.
69///   - The memory specified by the pointer and size returned by `write_buffer`
70///     must not be freed during the transfer as long as `self` is not dropped.
71pub unsafe trait WriteBuffer {
72    type Word;
73
74    /// Provide a buffer usable for DMA writes.
75    ///
76    /// The return value is:
77    ///
78    /// - pointer to the start of the buffer
79    /// - buffer size in words
80    ///
81    /// # Safety
82    ///
83    /// Once this method has been called, it is unsafe to call any `&mut self`
84    /// methods, except for `write_buffer`, on this object as long as the
85    /// returned value is in use (by DMA).
86    unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize);
87}
88
89// Blanket implementations for common DMA buffer types.
90
91unsafe impl<B, T> ReadBuffer for B
92where
93    B: Deref<Target = T> + StableDeref + 'static,
94    T: ReadTarget + ?Sized,
95{
96    type Word = T::Word;
97
98    unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
99        self.as_read_buffer()
100    }
101}
102
103unsafe impl<B, T> WriteBuffer for B
104where
105    B: DerefMut<Target = T> + StableDeref + 'static,
106    T: WriteTarget + ?Sized,
107{
108    type Word = T::Word;
109
110    unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
111        self.as_write_buffer()
112    }
113}
114
115/// Trait for DMA word types used by the blanket DMA buffer impls.
116///
117/// # Safety
118///
119/// Types that implement this trait must be valid for every possible byte
120/// pattern. This is to ensure that, whatever DMA writes into the buffer,
121/// we won't get UB due to invalid values.
122pub unsafe trait Word {}
123
124unsafe impl Word for u8 {}
125unsafe impl Word for i8 {}
126unsafe impl Word for u16 {}
127unsafe impl Word for i16 {}
128unsafe impl Word for u32 {}
129unsafe impl Word for i32 {}
130unsafe impl Word for u64 {}
131unsafe impl Word for i64 {}
132
133/// Trait for `Deref` targets used by the blanket `DmaReadBuffer` impl.
134///
135/// This trait exists solely to work around
136/// https://github.com/rust-lang/rust/issues/20400.
137///
138/// # Safety
139///
140/// - `as_read_buffer` must adhere to the safety requirements
141///   documented for [`ReadBuffer::read_buffer`].
142pub unsafe trait ReadTarget {
143    type Word: Word;
144
145    fn as_read_buffer(&self) -> (*const Self::Word, usize) {
146        let len = mem::size_of_val(self) / mem::size_of::<Self::Word>();
147        let ptr = self as *const _ as *const Self::Word;
148        (ptr, len)
149    }
150}
151
152/// Trait for `DerefMut` targets used by the blanket `DmaWriteBuffer` impl.
153///
154/// This trait exists solely to work around
155/// https://github.com/rust-lang/rust/issues/20400.
156///
157/// # Safety
158///
159/// - `as_write_buffer` must adhere to the safety requirements
160///   documented for [`WriteBuffer::write_buffer`].
161pub unsafe trait WriteTarget {
162    type Word: Word;
163
164    fn as_write_buffer(&mut self) -> (*mut Self::Word, usize) {
165        let len = mem::size_of_val(self) / mem::size_of::<Self::Word>();
166        let ptr = self as *mut _ as *mut Self::Word;
167        (ptr, len)
168    }
169}
170
171unsafe impl<W: Word> ReadTarget for W {
172    type Word = W;
173}
174
175unsafe impl<W: Word> WriteTarget for W {
176    type Word = W;
177}
178
179unsafe impl<T: ReadTarget> ReadTarget for [T] {
180    type Word = T::Word;
181}
182
183unsafe impl<T: WriteTarget> WriteTarget for [T] {
184    type Word = T::Word;
185}
186
187unsafe impl<T: ReadTarget, const N: usize> ReadTarget for [T; N] {
188    type Word = T::Word;
189}
190
191unsafe impl<T: WriteTarget, const N: usize> WriteTarget for [T; N] {
192    type Word = T::Word;
193}
194
195unsafe impl<T: WriteTarget> WriteTarget for MaybeUninit<T> {
196    type Word = T::Word;
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use core::any::Any;
203
204    fn api_read<W, B>(buffer: B) -> (*const W, usize)
205    where
206        B: ReadBuffer<Word = W>,
207    {
208        unsafe { buffer.read_buffer() }
209    }
210
211    fn api_write<W, B>(mut buffer: B) -> (*mut W, usize)
212    where
213        B: WriteBuffer<Word = W>,
214    {
215        unsafe { buffer.write_buffer() }
216    }
217
218    #[test]
219    fn read_api() {
220        const SIZE: usize = 128;
221        static BUF: [u8; SIZE] = [0u8; SIZE];
222
223        let (ptr, size_local) = api_read(&BUF);
224        assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
225        assert_eq!(size_local, SIZE);
226    }
227
228    #[test]
229    fn write_api() {
230        const SIZE: usize = 128;
231        static mut BUF: [u8; SIZE] = [0u8; SIZE];
232
233        let (ptr, size_local) = api_write(unsafe { &mut BUF });
234        assert!(unsafe { (&*ptr as &dyn Any).is::<u8>() });
235        assert_eq!(size_local, SIZE);
236    }
237}