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::{c_str, device::Core, devres::Devres, pci, prelude::*, sync::aref::ARef};
8
9struct Regs;
10
11impl Regs {
12 const TEST: usize = 0x0;
13 const OFFSET: usize = 0x4;
14 const DATA: usize = 0x8;
15 const COUNT: usize = 0xC;
16 const END: usize = 0x10;
17}
18
19type Bar0 = pci::Bar<{ Regs::END }>;
20
21#[derive(Copy, Clone, Debug)]
22struct TestIndex(u8);
23
24impl TestIndex {
25 const NO_EVENTFD: Self = Self(0);
26}
27
28#[pin_data(PinnedDrop)]
29struct SampleDriver {
30 pdev: ARef<pci::Device>,
31 #[pin]
32 bar: Devres<Bar0>,
33 index: TestIndex,
34}
35
36kernel::pci_device_table!(
37 PCI_TABLE,
38 MODULE_PCI_TABLE,
39 <SampleDriver as pci::Driver>::IdInfo,
40 [(
41 pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
42 TestIndex::NO_EVENTFD
43 )]
44);
45
46impl SampleDriver {
47 fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
48 // Select the test.
49 bar.write8(index.0, Regs::TEST);
50
51 let offset = u32::from_le(bar.read32(Regs::OFFSET)) as usize;
52 let data = bar.read8(Regs::DATA);
53
54 // Write `data` to `offset` to increase `count` by one.
55 //
56 // Note that we need `try_write8`, since `offset` can't be checked at compile-time.
57 bar.try_write8(data, offset)?;
58
59 Ok(bar.read32(Regs::COUNT))
60 }
61}
62
63impl pci::Driver for SampleDriver {
64 type IdInfo = TestIndex;
65
66 const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
67
68 fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
69 let vendor = pdev.vendor_id();
70 dev_dbg!(
71 pdev.as_ref(),
72 "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
73 vendor,
74 pdev.device_id()
75 );
76
77 pdev.enable_device_mem()?;
78 pdev.set_master();
79
80 let drvdata = KBox::pin_init(
81 try_pin_init!(Self {
82 bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")),
83 pdev: pdev.into(),
84 index: *info,
85 }),
86 GFP_KERNEL,
87 )?;
88
89 let bar = drvdata.bar.access(pdev.as_ref())?;
90 dev_info!(
91 pdev.as_ref(),
92 "pci-testdev data-match count: {}\n",
93 Self::testdev(info, bar)?
94 );
95
96 Ok(drvdata)
97 }
98
99 fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
100 if let Ok(bar) = this.bar.access(pdev.as_ref()) {
101 // Reset pci-testdev by writing a new test index.
102 bar.write8(this.index.0, Regs::TEST);
103 }
104 }
105}
106
107#[pinned_drop]
108impl PinnedDrop for SampleDriver {
109 fn drop(self: Pin<&mut Self>) {
110 dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
111 }
112}
113
114kernel::module_pci_driver! {
115 type: SampleDriver,
116 name: "rust_driver_pci",
117 authors: ["Danilo Krummrich"],
118 description: "Rust PCI driver",
119 license: "GPL v2",
120}