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::Bound,
9 device::Core,
10 devres::Devres,
11 io::Io,
12 pci,
13 prelude::*,
14 sync::aref::ARef, //
15};
16
17struct Regs;
18
19impl 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
27type Bar0 = pci::Bar<{ Regs::END }>;
28
29#[derive(Copy, Clone, Debug)]
30struct TestIndex(u8);
31
32impl TestIndex {
33 const NO_EVENTFD: Self = Self(0);
34}
35
36#[pin_data(PinnedDrop)]
37struct SampleDriver {
38 pdev: ARef<pci::Device>,
39 #[pin]
40 bar: Devres<Bar0>,
41 index: TestIndex,
42}
43
44kernel::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
54impl 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,
77 "pci-testdev config space read8 rev ID: {:x}\n",
78 config.read8(0x8)
79 );
80
81 dev_info!(
82 pdev,
83 "pci-testdev config space read16 vendor ID: {:x}\n",
84 config.read16(0)
85 );
86
87 dev_info!(
88 pdev,
89 "pci-testdev config space read32 BAR 0: {:x}\n",
90 config.read32(0x10)
91 );
92 }
93}
94
95impl 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]
140impl PinnedDrop for SampleDriver {
141 fn drop(self: Pin<&mut Self>) {
142 dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n");
143 }
144}
145
146kernel::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}