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