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