kernel/pci/irq.rs
1// SPDX-License-Identifier: GPL-2.0
2
3//! PCI interrupt infrastructure.
4
5use super::Device;
6use 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};
15use core::ops::RangeInclusive;
16use kernel::prelude::*;
17
18/// IRQ type flags for PCI interrupt allocation.
19#[derive(Debug, Clone, Copy)]
20pub 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
29impl 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)]
42pub struct IrqTypes(u32);
43
44impl IrqTypes {
45    /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
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 INTx 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)]
75pub struct IrqVector<'a> {
76    dev: &'a Device<Bound>,
77    index: u32,
78}
79
80impl<'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
97impl<'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.
119struct IrqVectorRegistration {
120    dev: ARef<Device>,
121}
122
123impl 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
161impl 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
170impl 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 INTx 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 INTx 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 INTx 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}