xref: /linux/samples/rust/rust_driver_pci.rs (revision 1b5f3c51fbb8042efb314484b47b2092cdd40bf6)
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, devres::Devres, pci, prelude::*};
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: 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.writeb(index.0, Regs::TEST);
47 
48         let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
49         let data = bar.readb(Regs::DATA);
50 
51         // Write `data` to `offset` to increase `count` by one.
52         //
53         // Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
54         bar.try_writeb(data, offset)?;
55 
56         Ok(bar.readl(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: &mut pci::Device, 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.clone(),
81                 bar,
82             },
83             GFP_KERNEL,
84         )?;
85 
86         let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
87 
88         dev_info!(
89             pdev.as_ref(),
90             "pci-testdev data-match count: {}\n",
91             Self::testdev(info, &bar)?
92         );
93 
94         Ok(drvdata.into())
95     }
96 }
97 
98 impl Drop for SampleDriver {
99     fn drop(&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     author: "Danilo Krummrich",
108     description: "Rust PCI driver",
109     license: "GPL v2",
110 }
111