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 7 use 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 25 const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME; 26 const AUXILIARY_NAME: &CStr = c"auxiliary"; 27 const COVARIANT_DEV_ID: u32 = 0; 28 const INVARIANT_DEV_ID: u32 = 1; 29 30 struct AuxiliaryDriver; 31 32 kernel::auxiliary_device_table!( 33 AUX_TABLE, 34 <AuxiliaryDriver as auxiliary::Driver>::IdInfo, 35 [(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())] 36 ); 37 38 impl 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 60 struct 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] 71 struct MutexData<'bound> { 72 #[pin] 73 parent: Mutex<&'bound pci::Device<Bound>>, 74 index: u32, 75 } 76 77 struct ParentDriver; 78 79 #[allow(clippy::type_complexity)] 80 #[pin_data] 81 struct ParentData<'bound> { 82 _reg0: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>, 83 #[pin] 84 _reg1: auxiliary::Registration<'bound, ForLt!(MutexData<'_>)>, 85 } 86 87 kernel::pci_device_table!( 88 PCI_TABLE, 89 <ParentDriver as pci::Driver>::IdInfo, 90 [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())] 91 ); 92 93 impl 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 139 impl 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] 180 struct SampleModule { 181 #[pin] 182 _pci_driver: driver::Registration<pci::Adapter<ParentDriver>>, 183 #[pin] 184 _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>, 185 } 186 187 impl 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 196 module! { 197 type: SampleModule, 198 name: "rust_driver_auxiliary", 199 authors: ["Danilo Krummrich"], 200 description: "Rust auxiliary driver", 201 license: "GPL v2", 202 } 203