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