1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
// SPDX-License-Identifier: GPL-2.0
//! Rust DMA api test (based on QEMU's `pci-testdev`).
//!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{
device::Core,
dma::{
Coherent,
DataDirection,
Device,
DmaMask, //
},
page, pci,
prelude::*,
scatterlist::{Owned, SGTable},
sync::aref::ARef,
};
#[pin_data(PinnedDrop)]
struct DmaSampleDriver {
pdev: ARef<pci::Device>,
ca: Coherent<[MyStruct]>,
#[pin]
sgt: SGTable<Owned<VVec<u8>>>,
}
const TEST_VALUES: [(u32, u32); 5] = [
(0xa, 0xb),
(0xc, 0xd),
(0xe, 0xf),
(0xab, 0xba),
(0xcd, 0xef),
];
struct MyStruct {
h: u32,
b: u32,
}
impl MyStruct {
fn new(h: u32, b: u32) -> Self {
Self { h, b }
}
}
// SAFETY: All bit patterns are acceptable values for `MyStruct`.
unsafe impl kernel::transmute::AsBytes for MyStruct {}
// SAFETY: Instances of `MyStruct` have no uninitialized portions.
unsafe impl kernel::transmute::FromBytes for MyStruct {}
kernel::pci_device_table!(
PCI_TABLE,
MODULE_PCI_TABLE,
<DmaSampleDriver as pci::Driver>::IdInfo,
[(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
);
impl pci::Driver for DmaSampleDriver {
type IdInfo = ();
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
pin_init::pin_init_scope(move || {
dev_info!(pdev, "Probe DMA test driver.\n");
let mask = DmaMask::new::<64>();
// SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.
unsafe { pdev.dma_set_mask_and_coherent(mask)? };
let ca: Coherent<[MyStruct]> =
Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
for (i, value) in TEST_VALUES.into_iter().enumerate() {
kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1));
}
let size = 4 * page::PAGE_SIZE;
let pages = VVec::with_capacity(size, GFP_KERNEL)?;
let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
Ok(try_pin_init!(Self {
pdev: pdev.into(),
ca,
sgt <- sgt,
}))
})
}
}
impl DmaSampleDriver {
fn check_dma(&self) -> Result {
for (i, value) in TEST_VALUES.into_iter().enumerate() {
let val0 = kernel::dma_read!(self.ca, [i]?.h);
let val1 = kernel::dma_read!(self.ca, [i]?.b);
assert_eq!(val0, value.0);
assert_eq!(val1, value.1);
}
Ok(())
}
}
#[pinned_drop]
impl PinnedDrop for DmaSampleDriver {
fn drop(self: Pin<&mut Self>) {
dev_info!(self.pdev, "Unload DMA test driver.\n");
assert!(self.check_dma().is_ok());
for (i, entry) in self.sgt.iter().enumerate() {
dev_info!(
self.pdev,
"Entry[{}]: DMA address: {:#x}",
i,
entry.dma_address(),
);
}
}
}
kernel::module_pci_driver! {
type: DmaSampleDriver,
name: "rust_dma",
authors: ["Abdiel Janulgue"],
description: "Rust DMA test",
license: "GPL v2",
}
|