Skip to content

Commit d48a7b0

Browse files
committed
Use safe DMA buffer traits
This commit migrates the current DMA implementation to the safe DMA buffer traits proposed by the "DMA API Documentation" focus project. See: https://github.com/ra-kete/dma-poc/blob/master/README.md
1 parent ae2c57c commit d48a7b0

File tree

3 files changed

+263
-62
lines changed

3 files changed

+263
-62
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ targets = ["thumbv7em-none-eabihf"]
1919
travis-ci = { repository = "stm32-rs/stm32f3xx-hal" }
2020

2121
[dependencies]
22-
as-slice = "0.1"
2322
cortex-m = "0.6"
2423
cortex-m-rt = "0.6"
2524
embedded-hal = "0.2"

src/dma.rs

Lines changed: 257 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ use crate::{
1010
stm32::{self, dma1::ch::cr},
1111
};
1212
use cast::u16;
13-
use core::sync::atomic::{self, Ordering};
13+
use core::{
14+
mem::{self, MaybeUninit},
15+
ops::{Deref, DerefMut},
16+
sync::atomic::{self, Ordering},
17+
};
1418
use stable_deref_trait::StableDeref;
1519

1620
/// Extension trait to split a DMA peripheral into independent channels
@@ -29,15 +33,46 @@ pub struct Transfer<B, C: Channel, T> {
2933
}
3034

3135
impl<B, C: Channel, T> Transfer<B, C, T> {
32-
/// Start a DMA transfer.
33-
///
36+
/// Start a DMA write transfer.
37+
pub fn start_write(mut buffer: B, mut channel: C, target: T) -> Self
38+
where
39+
B: WriteBuffer + 'static,
40+
T: Target<C>,
41+
{
42+
let (ptr, len) = buffer.write_buffer();
43+
44+
channel.set_memory_address(ptr as u32, Increment::Enable);
45+
channel.set_transfer_length(len);
46+
channel.set_word_size::<B::Word>();
47+
channel.set_direction(Direction::FromPeripheral);
48+
49+
unsafe { Self::start(buffer, channel, target) }
50+
}
51+
52+
/// Start a DMA read transfer.
53+
pub fn start_read(buffer: B, mut channel: C, target: T) -> Self
54+
where
55+
B: ReadBuffer + 'static,
56+
T: Target<C>,
57+
{
58+
let (ptr, len) = buffer.read_buffer();
59+
60+
channel.set_memory_address(ptr as u32, Increment::Enable);
61+
channel.set_transfer_length(len);
62+
channel.set_word_size::<B::Word>();
63+
channel.set_direction(Direction::FromMemory);
64+
65+
unsafe { Self::start(buffer, channel, target) }
66+
}
67+
3468
/// # Safety
3569
///
36-
/// Callers must ensure that the DMA channel is configured correctly for
37-
/// the given target and buffer.
38-
pub unsafe fn start(buffer: B, mut channel: C, target: T) -> Self
70+
/// Callers must ensure that:
71+
///
72+
/// - the given buffer will be valid for the duration of the transfer
73+
/// - the DMA channel is configured correctly for the given target and buffer
74+
unsafe fn start(buffer: B, mut channel: C, target: T) -> Self
3975
where
40-
B: StableDeref + 'static,
4176
T: Target<C>,
4277
{
4378
assert!(!channel.is_enabled());
@@ -100,6 +135,211 @@ impl<B, C: Channel, T> TransferInner<B, C, T> {
100135
}
101136
}
102137

138+
/// Trait for buffers that can be given to DMA for reading.
139+
///
140+
/// # Safety
141+
///
142+
/// The implementing type must be safe to use for DMA reads. This means:
143+
///
144+
/// - It must be a pointer that references the actual buffer.
145+
/// - The requirements documented on `read_buffer` must be fulfilled.
146+
pub unsafe trait ReadBuffer {
147+
type Word;
148+
149+
/// Provide a buffer usable for DMA reads.
150+
///
151+
/// The return value is:
152+
///
153+
/// - pointer to the start of the buffer
154+
/// - buffer size in words
155+
///
156+
/// # Safety
157+
///
158+
/// - This function must always return the same values, if called multiple
159+
/// times.
160+
/// - The memory specified by the returned pointer and size must not be
161+
/// freed as long as `self` is not dropped.
162+
fn read_buffer(&self) -> (*const Self::Word, usize);
163+
}
164+
165+
/// Trait for buffers that can be given to DMA for writing.
166+
///
167+
/// # Safety
168+
///
169+
/// The implementing type must be safe to use for DMA writes. This means:
170+
///
171+
/// - It must be a pointer that references the actual buffer.
172+
/// - `Target` must be a type that is valid for any possible byte pattern.
173+
/// - The requirements documented on `write_buffer` must be fulfilled.
174+
pub unsafe trait WriteBuffer {
175+
type Word;
176+
177+
/// Provide a buffer usable for DMA writes.
178+
///
179+
/// The return value is:
180+
///
181+
/// - pointer to the start of the buffer
182+
/// - buffer size in words
183+
///
184+
/// # Safety
185+
///
186+
/// - This function must always return the same values, if called multiple
187+
/// times.
188+
/// - The memory specified by the returned pointer and size must not be
189+
/// freed as long as `self` is not dropped.
190+
fn write_buffer(&mut self) -> (*mut Self::Word, usize);
191+
}
192+
193+
// Blanked implementations for common DMA buffer types.
194+
195+
unsafe impl<B, T> ReadBuffer for B
196+
where
197+
B: Deref<Target = T> + StableDeref,
198+
T: ReadTarget + ?Sized,
199+
{
200+
type Word = T::Word;
201+
202+
fn read_buffer(&self) -> (*const Self::Word, usize) {
203+
self.as_read_buffer()
204+
}
205+
}
206+
207+
unsafe impl<B, T> WriteBuffer for B
208+
where
209+
B: DerefMut<Target = T> + StableDeref,
210+
T: WriteTarget + ?Sized,
211+
{
212+
type Word = T::Word;
213+
214+
fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
215+
self.as_write_buffer()
216+
}
217+
}
218+
219+
/// Trait for DMA word types used by the blanket DMA buffer impls.
220+
///
221+
/// # Safety
222+
///
223+
/// Types that implement this trait must be valid for every possible byte
224+
/// pattern. This is to ensure that, whatever DMA writes into the buffer,
225+
/// we won't get UB due to invalid values.
226+
pub unsafe trait Word {}
227+
228+
unsafe impl Word for u8 {}
229+
unsafe impl Word for u16 {}
230+
unsafe impl Word for u32 {}
231+
232+
/// Trait for `Deref` targets used by the blanket `DmaReadBuffer` impl.
233+
///
234+
/// This trait exists solely to work around
235+
/// https://github.com/rust-lang/rust/issues/20400.
236+
///
237+
/// # Safety
238+
///
239+
/// - `as_read_buffer` must adhere to the safety requirements
240+
/// documented for `DmaReadBuffer::dma_read_buffer`.
241+
pub unsafe trait ReadTarget {
242+
type Word: Word;
243+
244+
fn as_read_buffer(&self) -> (*const Self::Word, usize) {
245+
let ptr = self as *const _ as *const Self::Word;
246+
let len = mem::size_of_val(self) / mem::size_of::<Self::Word>();
247+
(ptr, len)
248+
}
249+
}
250+
251+
/// Trait for `DerefMut` targets used by the blanket `DmaWriteBuffer` impl.
252+
///
253+
/// This trait exists solely to work around
254+
/// https://github.com/rust-lang/rust/issues/20400.
255+
///
256+
/// # Safety
257+
///
258+
/// - `as_write_buffer` must adhere to the safety requirements
259+
/// documented for `DmaWriteBuffer::dma_write_buffer`.
260+
pub unsafe trait WriteTarget {
261+
type Word: Word;
262+
263+
fn as_write_buffer(&mut self) -> (*mut Self::Word, usize) {
264+
let ptr = self as *mut _ as *mut Self::Word;
265+
let len = mem::size_of_val(self) / mem::size_of::<Self::Word>();
266+
(ptr, len)
267+
}
268+
}
269+
270+
unsafe impl<W: Word> ReadTarget for W {
271+
type Word = W;
272+
}
273+
274+
unsafe impl<W: Word> WriteTarget for W {
275+
type Word = W;
276+
}
277+
278+
unsafe impl<T: ReadTarget> ReadTarget for [T] {
279+
type Word = T::Word;
280+
}
281+
282+
unsafe impl<T: WriteTarget> WriteTarget for [T] {
283+
type Word = T::Word;
284+
}
285+
286+
macro_rules! dma_target_array_impls {
287+
( $( $i:expr, )+ ) => {
288+
$(
289+
unsafe impl<T: ReadTarget> ReadTarget for [T; $i] {
290+
type Word = T::Word;
291+
}
292+
293+
unsafe impl<T: WriteTarget> WriteTarget for [T; $i] {
294+
type Word = T::Word;
295+
}
296+
)+
297+
};
298+
}
299+
300+
#[rustfmt::skip]
301+
dma_target_array_impls!(
302+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
303+
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
304+
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
305+
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
306+
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
307+
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
308+
60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
309+
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
310+
80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
311+
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
312+
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
313+
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
314+
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
315+
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
316+
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
317+
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
318+
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
319+
170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
320+
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
321+
190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
322+
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
323+
210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
324+
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
325+
230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
326+
240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
327+
250, 251, 252, 253, 254, 255, 256,
328+
329+
1 << 9,
330+
1 << 10,
331+
1 << 11,
332+
1 << 12,
333+
1 << 13,
334+
1 << 14,
335+
1 << 15,
336+
1 << 16,
337+
);
338+
339+
unsafe impl<T: WriteTarget> WriteTarget for MaybeUninit<T> {
340+
type Word = T::Word;
341+
}
342+
103343
/// DMA address increment mode
104344
pub enum Increment {
105345
/// Enable increment
@@ -117,26 +357,6 @@ impl From<Increment> for cr::PINC_A {
117357
}
118358
}
119359

120-
/// DMA word size
121-
pub enum WordSize {
122-
/// 8 bits
123-
Bits8,
124-
/// 16 bits
125-
Bits16,
126-
/// 32 bits
127-
Bits32,
128-
}
129-
130-
impl From<WordSize> for cr::PSIZE_A {
131-
fn from(size: WordSize) -> Self {
132-
match size {
133-
WordSize::Bits8 => cr::PSIZE_A::BITS8,
134-
WordSize::Bits16 => cr::PSIZE_A::BITS16,
135-
WordSize::Bits32 => cr::PSIZE_A::BITS32,
136-
}
137-
}
138-
}
139-
140360
/// Channel priority level
141361
pub enum Priority {
142362
/// Low
@@ -251,8 +471,16 @@ pub trait Channel: private::Channel {
251471
}
252472

253473
/// Set the word size
254-
fn set_word_size(&mut self, size: WordSize) {
255-
let psize = size.into();
474+
fn set_word_size<W>(&mut self) {
475+
use cr::PSIZE_A::*;
476+
477+
let psize = match mem::size_of::<W>() {
478+
1 => BITS8,
479+
2 => BITS16,
480+
4 => BITS32,
481+
s => panic!("unsupported word size: {}", s),
482+
};
483+
256484
self.ch().cr.modify(|_, w| {
257485
w.psize().variant(psize);
258486
w.msize().variant(psize)

0 commit comments

Comments
 (0)