projects/linux·blob master · refs

202 lines · 5.7 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3//! Rust auxiliary driver sample (based on a PCI driver for QEMU's `pci-testdev`).
4//!
5//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
6
7use kernel::{
8 auxiliary,
9 device::{
10 Bound,
11 Core, //
12 },
13 driver,
14 new_mutex,
15 pci,
16 prelude::*,
17 sync::Mutex,
18 types::{
19 CovariantForLt,
20 ForLt, //
21 },
22 InPlaceModule, //
23};
24
25const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
26const AUXILIARY_NAME: &CStr = c"auxiliary";
27const COVARIANT_DEV_ID: u32 = 0;
28const INVARIANT_DEV_ID: u32 = 1;
29
30struct AuxiliaryDriver;
31
32kernel::auxiliary_device_table!(
33 AUX_TABLE,
34 <AuxiliaryDriver as auxiliary::Driver>::IdInfo,
35 [(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())]
36);
37
38impl auxiliary::Driver for AuxiliaryDriver {
39 type IdInfo = ();
40 type Data<'bound> = Self;
41
42 const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
43
44 fn probe<'bound>(
45 adev: &'bound auxiliary::Device<Core<'_>>,
46 _info: &'bound Self::IdInfo,
47 ) -> impl PinInit<Self, Error> + 'bound {
48 dev_info!(
49 adev,
50 "Probing auxiliary driver for auxiliary device with id={}\n",
51 adev.id()
52 );
53
54 ParentDriver::connect(adev)?;
55
56 Ok(Self)
57 }
58}
59
60struct Data<'bound> {
61 index: u32,
62 parent: &'bound pci::Device<Bound>,
63}
64
65/// Registration data with interior mutability.
66///
67/// `Mutex<&'bound T>` is invariant over `'bound`, so this type cannot implement
68/// [`CovariantForLt`](trait@CovariantForLt). Access must go through the closure-based
69/// [`auxiliary::Device::registration_data_with()`].
70#[pin_data]
71struct MutexData<'bound> {
72 #[pin]
73 parent: Mutex<&'bound pci::Device<Bound>>,
74 index: u32,
75}
76
77struct ParentDriver;
78
79#[allow(clippy::type_complexity)]
80#[pin_data]
81struct ParentData<'bound> {
82 _reg0: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
83 #[pin]
84 _reg1: auxiliary::Registration<'bound, ForLt!(MutexData<'_>)>,
85}
86
87kernel::pci_device_table!(
88 PCI_TABLE,
89 <ParentDriver as pci::Driver>::IdInfo,
90 [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
91);
92
93impl pci::Driver for ParentDriver {
94 type IdInfo = ();
95 type Data<'bound> = ParentData<'bound>;
96
97 const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
98
99 fn probe<'bound>(
100 pdev: &'bound pci::Device<Core<'_>>,
101 _info: Option<&'bound Self::IdInfo>,
102 ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
103 try_pin_init!(ParentData {
104 // SAFETY: `ParentData` is the driver's private data, which is dropped when the
105 // device is unbound; i.e. `mem::forget()` is never called on it.
106 _reg0: unsafe {
107 auxiliary::Registration::new_with_lt(
108 pdev.as_ref(),
109 AUXILIARY_NAME,
110 COVARIANT_DEV_ID,
111 MODULE_NAME,
112 Data {
113 index: COVARIANT_DEV_ID,
114 parent: pdev,
115 },
116 )?
117 },
118 // SAFETY: See `_reg0` above.
119 _reg1: unsafe {
120 auxiliary::Registration::new_with_lt(
121 pdev.as_ref(),
122 AUXILIARY_NAME,
123 INVARIANT_DEV_ID,
124 MODULE_NAME,
125 pin_init!(MutexData {
126 parent <- {
127 let pdev: &pci::Device<Bound> = pdev;
128
129 new_mutex!(pdev)
130 },
131 index: INVARIANT_DEV_ID,
132 }),
133 )?
134 },
135 })
136 }
137}
138
139impl ParentDriver {
140 fn connect(adev: &auxiliary::Device<Bound>) -> Result {
141 match adev.id() {
142 // CovariantForLt types can use the direct-reference accessor.
143 COVARIANT_DEV_ID => {
144 let data = adev.registration_data::<CovariantForLt!(Data<'_>)>()?;
145 let pdev = data.parent;
146
147 dev_info!(
148 pdev,
149 "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
150 adev.id(),
151 pdev.vendor_id(),
152 pdev.device_id()
153 );
154
155 dev_info!(
156 pdev,
157 "Connected to auxiliary device with index {}.\n",
158 data.index
159 );
160 }
161 // Invariant ForLt types (e.g. containing a Mutex) require the closure-based accessor.
162 INVARIANT_DEV_ID => {
163 adev.registration_data_with::<ForLt!(MutexData<'_>), _>(|data| {
164 let pdev = *data.parent.lock();
165 dev_info!(
166 pdev,
167 "Connected to auxiliary device with index {} (via Mutex).\n",
168 data.index
169 );
170 })?;
171 }
172 _ => return Err(EINVAL),
173 }
174
175 Ok(())
176 }
177}
178
179#[pin_data]
180struct SampleModule {
181 #[pin]
182 _pci_driver: driver::Registration<pci::Adapter<ParentDriver>>,
183 #[pin]
184 _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
185}
186
187impl InPlaceModule for SampleModule {
188 fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
189 try_pin_init!(Self {
190 _pci_driver <- driver::Registration::new(MODULE_NAME, module),
191 _aux_driver <- driver::Registration::new(MODULE_NAME, module),
192 })
193 }
194}
195
196module! {
197 type: SampleModule,
198 name: "rust_driver_auxiliary",
199 authors: ["Danilo Krummrich"],
200 description: "Rust auxiliary driver",
201 license: "GPL v2",
202}