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