xref: /linux/samples/rust/rust_driver_pci.rs (revision eb3dad518e4da48ab6c6df16aa8895b8b0bd6ecf)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Rust PCI driver sample (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::Bound,
9     device::Core,
10     devres::Devres,
11     io::Io,
12     pci,
13     prelude::*,
14     sync::aref::ARef, //
15 };
16 
17 struct Regs;
18 
19 impl Regs {
20     const TEST: usize = 0x0;
21     const OFFSET: usize = 0x4;
22     const DATA: usize = 0x8;
23     const COUNT: usize = 0xC;
24     const END: usize = 0x10;
25 }
26 
27 type Bar0 = pci::Bar<{ Regs::END }>;
28 
29 #[derive(Copy, Clone, Debug)]
30 struct TestIndex(u8);
31 
32 impl TestIndex {
33     const NO_EVENTFD: Self = Self(0);
34 }
35 
36 #[pin_data(PinnedDrop)]
37 struct SampleDriver {
38     pdev: ARef<pci::Device>,
39     #[pin]
40     bar: Devres<Bar0>,
41     index: TestIndex,
42 }
43 
44 kernel::pci_device_table!(
45     PCI_TABLE,
46     MODULE_PCI_TABLE,
47     <SampleDriver as pci::Driver>::IdInfo,
48     [(
49         pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
50         TestIndex::NO_EVENTFD
51     )]
52 );
53 
54 impl SampleDriver {
55     fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
56         // Select the test.
57         bar.write8(index.0, Regs::TEST);
58 
59         let offset = bar.read32(Regs::OFFSET) as usize;
60         let data = bar.read8(Regs::DATA);
61 
62         // Write `data` to `offset` to increase `count` by one.
63         //
64         // Note that we need `try_write8`, since `offset` can't be checked at compile-time.
65         bar.try_write8(data, offset)?;
66 
67         Ok(bar.read32(Regs::COUNT))
68     }
69 
70     fn config_space(pdev: &pci::Device<Bound>) {
71         let config = pdev.config_space();
72 
73         // TODO: use the register!() macro for defining PCI configuration space registers once it
74         // has been move out of nova-core.
75         dev_info!(
76             pdev.as_ref(),
77             "pci-testdev config space read8 rev ID: {:x}\n",
78             config.read8(0x8)
79         );
80 
81         dev_info!(
82             pdev.as_ref(),
83             "pci-testdev config space read16 vendor ID: {:x}\n",
84             config.read16(0)
85         );
86 
87         dev_info!(
88             pdev.as_ref(),
89             "pci-testdev config space read32 BAR 0: {:x}\n",
90             config.read32(0x10)
91         );
92     }
93 }
94 
95 impl pci::Driver for SampleDriver {
96     type IdInfo = TestIndex;
97 
98     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
99 
100     fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
101         pin_init::pin_init_scope(move || {
102             let vendor = pdev.vendor_id();
103             dev_dbg!(
104                 pdev,
105                 "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
106                 vendor,
107                 pdev.device_id()
108             );
109 
110             pdev.enable_device_mem()?;
111             pdev.set_master();
112 
113             Ok(try_pin_init!(Self {
114                 bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c"rust_driver_pci"),
115                 index: *info,
116                 _: {
117                     let bar = bar.access(pdev.as_ref())?;
118 
119                     dev_info!(
120                         pdev,
121                         "pci-testdev data-match count: {}\n",
122                         Self::testdev(info, bar)?
123                     );
124                     Self::config_space(pdev);
125                 },
126                 pdev: pdev.into(),
127             }))
128         })
129     }
130 
131     fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
132         if let Ok(bar) = this.bar.access(pdev.as_ref()) {
133             // Reset pci-testdev by writing a new test index.
134             bar.write8(this.index.0, Regs::TEST);
135         }
136     }
137 }
138 
139 #[pinned_drop]
140 impl PinnedDrop for SampleDriver {
141     fn drop(self: Pin<&mut Self>) {
142         dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n");
143     }
144 }
145 
146 kernel::module_pci_driver! {
147     type: SampleDriver,
148     name: "rust_driver_pci",
149     authors: ["Danilo Krummrich"],
150     description: "Rust PCI driver",
151     license: "GPL v2",
152 }
153