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