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