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 struct SampleDriver {
29     pdev: ARef<pci::Device>,
30     bar: Devres<Bar0>,
31 }
32 
33 kernel::pci_device_table!(
34     PCI_TABLE,
35     MODULE_PCI_TABLE,
36     <SampleDriver as pci::Driver>::IdInfo,
37     [(
38         pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
39         TestIndex::NO_EVENTFD
40     )]
41 );
42 
43 impl SampleDriver {
44     fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
45         // Select the test.
46         bar.write8(index.0, Regs::TEST);
47 
48         let offset = u32::from_le(bar.read32(Regs::OFFSET)) as usize;
49         let data = bar.read8(Regs::DATA);
50 
51         // Write `data` to `offset` to increase `count` by one.
52         //
53         // Note that we need `try_write8`, since `offset` can't be checked at compile-time.
54         bar.try_write8(data, offset)?;
55 
56         Ok(bar.read32(Regs::COUNT))
57     }
58 }
59 
60 impl pci::Driver for SampleDriver {
61     type IdInfo = TestIndex;
62 
63     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
64 
65     fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
66         dev_dbg!(
67             pdev.as_ref(),
68             "Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n",
69             pdev.vendor_id(),
70             pdev.device_id()
71         );
72 
73         pdev.enable_device_mem()?;
74         pdev.set_master();
75 
76         let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?;
77 
78         let drvdata = KBox::new(
79             Self {
80                 pdev: pdev.into(),
81                 bar,
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.into())
94     }
95 }
96 
97 impl Drop for SampleDriver {
98     fn drop(&mut self) {
99         dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
100     }
101 }
102 
103 kernel::module_pci_driver! {
104     type: SampleDriver,
105     name: "rust_driver_pci",
106     authors: ["Danilo Krummrich"],
107     description: "Rust PCI driver",
108     license: "GPL v2",
109 }
110