Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
7use kernel::{
8 device::{
9 Bound,
10 Core, //
11 },
12 devres::Devres,
13 io::{
14 register,
15 register::Array,
16 Io, //
17 },
18 num::Bounded,
19 pci,
20 prelude::*,
21 sync::aref::ARef, //
22};
23
24mod regs {
25 use super::*;
26
27 register! {
28 pub(super) TEST(u8) @ 0x0 {
29 7:0 index => TestIndex;
30 }
31
32 pub(super) OFFSET(u32) @ 0x4 {
33 31:0 offset;
34 }
35
36 pub(super) DATA(u8) @ 0x8 {
37 7:0 data;
38 }
39
40 pub(super) COUNT(u32) @ 0xC {
41 31:0 count;
42 }
43 }
44
45 pub(super) const END: usize = 0x10;
46}
47
48type Bar0 = pci::Bar<{ regs::END }>;
49
50#[derive(Copy, Clone, Debug)]
51struct TestIndex(u8);
52
53impl From<Bounded<u8, 8>> for TestIndex {
54 fn from(value: Bounded<u8, 8>) -> Self {
55 Self(value.into())
56 }
57}
58
59impl From<TestIndex> for Bounded<u8, 8> {
60 fn from(value: TestIndex) -> Self {
61 value.0.into()
62 }
63}
64
65impl TestIndex {
66 const NO_EVENTFD: Self = Self(0);
67}
68
69#[pin_data(PinnedDrop)]
70struct SampleDriver {
71 pdev: ARef<pci::Device>,
72 #[pin]
73 bar: Devres<Bar0>,
74 index: TestIndex,
75}
76
77kernel::pci_device_table!(
78 PCI_TABLE,
79 MODULE_PCI_TABLE,
80 <SampleDriver as pci::Driver>::IdInfo,
81 [(
82 pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
83 TestIndex::NO_EVENTFD
84 )]
85);
86
87impl SampleDriver {
88 fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
89 // Select the test.
90 bar.write_reg(regs::TEST::zeroed().with_index(*index));
91
92 let offset = bar.read(regs::OFFSET).into_raw() as usize;
93 let data = bar.read(regs::DATA).into();
94
95 // Write `data` to `offset` to increase `count` by one.
96 //
97 // Note that we need `try_write8`, since `offset` can't be checked at compile-time.
98 bar.try_write8(data, offset)?;
99
100 Ok(bar.read(regs::COUNT).into())
101 }
102
103 fn config_space(pdev: &pci::Device<Bound>) {
104 let config = pdev.config_space();
105
106 // Some PCI configuration space registers.
107 register! {
108 VENDOR_ID(u16) @ 0x0 {
109 15:0 vendor_id;
110 }
111
112 REVISION_ID(u8) @ 0x8 {
113 7:0 revision_id;
114 }
115
116 BAR(u32)[6] @ 0x10 {
117 31:0 value;
118 }
119 }
120
121 dev_info!(
122 pdev,
123 "pci-testdev config space read8 rev ID: {:x}\n",
124 config.read(REVISION_ID).revision_id()
125 );
126
127 dev_info!(
128 pdev,
129 "pci-testdev config space read16 vendor ID: {:x}\n",
130 config.read(VENDOR_ID).vendor_id()
131 );
132
133 dev_info!(
134 pdev,
135 "pci-testdev config space read32 BAR 0: {:x}\n",
136 config.read(BAR::at(0)).value()
137 );
138 }
139}
140
141impl pci::Driver for SampleDriver {
142 type IdInfo = TestIndex;
143
144 const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
145
146 fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
147 pin_init::pin_init_scope(move || {
148 let vendor = pdev.vendor_id();
149 dev_dbg!(
150 pdev,
151 "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
152 vendor,
153 pdev.device_id()
154 );
155
156 pdev.enable_device_mem()?;
157 pdev.set_master();
158
159 Ok(try_pin_init!(Self {
160 bar <- pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci"),
161 index: *info,
162 _: {
163 let bar = bar.access(pdev.as_ref())?;
164
165 dev_info!(
166 pdev,
167 "pci-testdev data-match count: {}\n",
168 Self::testdev(info, bar)?
169 );
170 Self::config_space(pdev);
171 },
172 pdev: pdev.into(),
173 }))
174 })
175 }
176
177 fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
178 if let Ok(bar) = this.bar.access(pdev.as_ref()) {
179 // Reset pci-testdev by writing a new test index.
180 bar.write_reg(regs::TEST::zeroed().with_index(this.index));
181 }
182 }
183}
184
185#[pinned_drop]
186impl PinnedDrop for SampleDriver {
187 fn drop(self: Pin<&mut Self>) {
188 dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n");
189 }
190}
191
192kernel::module_pci_driver! {
193 type: SampleDriver,
194 name: "rust_driver_pci",
195 authors: ["Danilo Krummrich"],
196 description: "Rust PCI driver",
197 license: "GPL v2",
198}