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