1 // SPDX-License-Identifier: GPL-2.0 2 3 //! PCI interrupt infrastructure. 4 5 use super::Device; 6 use crate::{ 7 bindings, 8 device, 9 device::Bound, 10 devres, 11 error::to_result, 12 irq::{ 13 self, 14 IrqRequest, // 15 }, 16 prelude::*, 17 str::CStr, 18 sync::aref::ARef, // 19 }; 20 use core::ops::RangeInclusive; 21 22 /// IRQ type flags for PCI interrupt allocation. 23 #[derive(Debug, Clone, Copy)] 24 pub enum IrqType { 25 /// INTx interrupts. 26 Intx, 27 /// Message Signaled Interrupts (MSI). 28 Msi, 29 /// Extended Message Signaled Interrupts (MSI-X). 30 MsiX, 31 } 32 33 impl IrqType { 34 /// Convert to the corresponding kernel flags. as_raw(self) -> u3235 const fn as_raw(self) -> u32 { 36 match self { 37 IrqType::Intx => bindings::PCI_IRQ_INTX, 38 IrqType::Msi => bindings::PCI_IRQ_MSI, 39 IrqType::MsiX => bindings::PCI_IRQ_MSIX, 40 } 41 } 42 } 43 44 /// Set of IRQ types that can be used for PCI interrupt allocation. 45 #[derive(Debug, Clone, Copy, Default)] 46 pub struct IrqTypes(u32); 47 48 impl IrqTypes { 49 /// Create a set containing all IRQ types (MSI-X, MSI, and INTx). all() -> Self50 pub const fn all() -> Self { 51 Self(bindings::PCI_IRQ_ALL_TYPES) 52 } 53 54 /// Build a set of IRQ types. 55 /// 56 /// # Examples 57 /// 58 /// ```ignore 59 /// // Create a set with only MSI and MSI-X (no INTx interrupts). 60 /// let msi_only = IrqTypes::default() 61 /// .with(IrqType::Msi) 62 /// .with(IrqType::MsiX); 63 /// ``` with(self, irq_type: IrqType) -> Self64 pub const fn with(self, irq_type: IrqType) -> Self { 65 Self(self.0 | irq_type.as_raw()) 66 } 67 68 /// Get the raw flags value. as_raw(self) -> u3269 const fn as_raw(self) -> u32 { 70 self.0 71 } 72 } 73 74 /// Represents an allocated IRQ vector for a specific PCI device. 75 /// 76 /// This type ties an IRQ vector to the device it was allocated for, 77 /// ensuring the vector is only used with the correct device. 78 #[derive(Clone, Copy)] 79 pub struct IrqVector<'a> { 80 dev: &'a Device<Bound>, 81 index: u32, 82 } 83 84 impl<'a> IrqVector<'a> { 85 /// Creates a new [`IrqVector`] for the given device and index. 86 /// 87 /// # Safety 88 /// 89 /// - `index` must be a valid IRQ vector index for `dev`. 90 /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. new(dev: &'a Device<Bound>, index: u32) -> Self91 unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { 92 Self { dev, index } 93 } 94 95 /// Returns the raw vector index. index(&self) -> u3296 fn index(&self) -> u32 { 97 self.index 98 } 99 } 100 101 impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> { 102 type Error = Error; 103 try_into(self) -> Result<IrqRequest<'a>>104 fn try_into(self) -> Result<IrqRequest<'a>> { 105 // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. 106 let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; 107 if irq < 0 { 108 return Err(crate::error::Error::from_errno(irq)); 109 } 110 // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. 111 Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) 112 } 113 } 114 115 /// Represents an IRQ vector allocation for a PCI device. 116 /// 117 /// This type ensures that IRQ vectors are properly allocated and freed by 118 /// tying the allocation to the lifetime of this registration object. 119 /// 120 /// # Invariants 121 /// 122 /// The [`Device`] has successfully allocated IRQ vectors. 123 struct IrqVectorRegistration { 124 dev: ARef<Device>, 125 } 126 127 impl IrqVectorRegistration { 128 /// Allocate and register IRQ vectors for the given PCI device. 129 /// 130 /// Allocates IRQ vectors and registers them with devres for automatic cleanup. 131 /// Returns a range of valid IRQ vectors. register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>>132 fn register<'a>( 133 dev: &'a Device<Bound>, 134 min_vecs: u32, 135 max_vecs: u32, 136 irq_types: IrqTypes, 137 ) -> Result<RangeInclusive<IrqVector<'a>>> { 138 // SAFETY: 139 // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` 140 // by the type invariant of `Device`. 141 // - `pci_alloc_irq_vectors` internally validates all other parameters 142 // and returns error codes. 143 let ret = unsafe { 144 bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) 145 }; 146 147 to_result(ret)?; 148 let count = ret as u32; 149 150 // SAFETY: 151 // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. 152 // - Vectors are 0-based, so valid indices are [0, count-1]. 153 // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and 154 // `count - 1` are valid IRQ vector indices for `dev`. 155 let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; 156 157 // INVARIANT: The IRQ vector allocation for `dev` above was successful. 158 let irq_vecs = Self { dev: dev.into() }; 159 devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; 160 161 Ok(range) 162 } 163 } 164 165 impl Drop for IrqVectorRegistration { drop(&mut self)166 fn drop(&mut self) { 167 // SAFETY: 168 // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. 169 // - `self.dev` has successfully allocated IRQ vectors. 170 unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; 171 } 172 } 173 174 impl Device<device::Bound> { 175 /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. request_irq<'a, T: crate::irq::Handler + 'static>( &'a self, vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit<T, Error> + 'a, ) -> impl PinInit<irq::Registration<T>, Error> + 'a176 pub fn request_irq<'a, T: crate::irq::Handler + 'static>( 177 &'a self, 178 vector: IrqVector<'a>, 179 flags: irq::Flags, 180 name: &'static CStr, 181 handler: impl PinInit<T, Error> + 'a, 182 ) -> impl PinInit<irq::Registration<T>, Error> + 'a { 183 pin_init::pin_init_scope(move || { 184 let request = vector.try_into()?; 185 186 Ok(irq::Registration::<T>::new(request, flags, name, handler)) 187 }) 188 } 189 190 /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>( &'a self, vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit<T, Error> + 'a, ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a191 pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>( 192 &'a self, 193 vector: IrqVector<'a>, 194 flags: irq::Flags, 195 name: &'static CStr, 196 handler: impl PinInit<T, Error> + 'a, 197 ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a { 198 pin_init::pin_init_scope(move || { 199 let request = vector.try_into()?; 200 201 Ok(irq::ThreadedRegistration::<T>::new( 202 request, flags, name, handler, 203 )) 204 }) 205 } 206 207 /// Allocate IRQ vectors for this PCI device with automatic cleanup. 208 /// 209 /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. 210 /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types` 211 /// parameter and hardware capabilities. When multiple types are specified, the kernel 212 /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts. 213 /// 214 /// The allocated vectors are automatically freed when the device is unbound, using the 215 /// devres (device resource management) system. 216 /// 217 /// # Arguments 218 /// 219 /// * `min_vecs` - Minimum number of vectors required. 220 /// * `max_vecs` - Maximum number of vectors to allocate. 221 /// * `irq_types` - Types of interrupts that can be used. 222 /// 223 /// # Returns 224 /// 225 /// Returns a range of IRQ vectors that were successfully allocated, or an error if the 226 /// allocation fails or cannot meet the minimum requirement. 227 /// 228 /// # Examples 229 /// 230 /// ``` 231 /// # use kernel::{ device::Bound, pci}; 232 /// # fn no_run(dev: &pci::Device<Bound>) -> Result { 233 /// // Allocate using any available interrupt type in the order mentioned above. 234 /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; 235 /// 236 /// // Allocate MSI or MSI-X only (no INTx interrupts). 237 /// let msi_only = pci::IrqTypes::default() 238 /// .with(pci::IrqType::Msi) 239 /// .with(pci::IrqType::MsiX); 240 /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; 241 /// # Ok(()) 242 /// # } 243 /// ``` alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>>244 pub fn alloc_irq_vectors( 245 &self, 246 min_vecs: u32, 247 max_vecs: u32, 248 irq_types: IrqTypes, 249 ) -> Result<RangeInclusive<IrqVector<'_>>> { 250 IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) 251 } 252 } 253