kernel/pci/irq.rs
1// SPDX-License-Identifier: GPL-2.0
2
3//! PCI interrupt infrastructure.
4
5use super::Device;
6use crate::{
7 bindings,
8 device,
9 device::Bound,
10 error::to_result,
11 irq::IrqRequest,
12 prelude::*, //
13};
14use core::num::NonZero;
15
16/// IRQ type flags for PCI interrupt allocation.
17#[derive(Debug, Clone, Copy)]
18pub enum IrqType {
19 /// INTx interrupts.
20 Intx,
21 /// Message Signaled Interrupts (MSI).
22 Msi,
23 /// Extended Message Signaled Interrupts (MSI-X).
24 MsiX,
25}
26
27impl IrqType {
28 /// Convert to the corresponding kernel flags.
29 const fn as_raw(self) -> u32 {
30 match self {
31 IrqType::Intx => bindings::PCI_IRQ_INTX,
32 IrqType::Msi => bindings::PCI_IRQ_MSI,
33 IrqType::MsiX => bindings::PCI_IRQ_MSIX,
34 }
35 }
36
37 /// Construct from raw value.
38 #[inline]
39 const fn from_raw(raw: u32) -> Self {
40 match raw {
41 bindings::PCI_IRQ_MSIX => IrqType::MsiX,
42 bindings::PCI_IRQ_MSI => IrqType::Msi,
43 _ => IrqType::Intx,
44 }
45 }
46}
47
48/// Set of IRQ types that can be used for PCI interrupt allocation.
49#[derive(Debug, Clone, Copy, Default)]
50pub struct IrqTypes(u32);
51
52impl IrqTypes {
53 /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
54 pub const fn all() -> Self {
55 Self(bindings::PCI_IRQ_ALL_TYPES)
56 }
57
58 /// Build a set of IRQ types.
59 ///
60 /// # Examples
61 ///
62 /// ```ignore
63 /// // Create a set with only MSI and MSI-X (no INTx interrupts).
64 /// let msi_only = IrqTypes::default()
65 /// .with(IrqType::Msi)
66 /// .with(IrqType::MsiX);
67 /// ```
68 pub const fn with(self, irq_type: IrqType) -> Self {
69 Self(self.0 | irq_type.as_raw())
70 }
71
72 /// Get the raw flags value.
73 const fn as_raw(self) -> u32 {
74 self.0
75 }
76}
77
78/// A resolved IRQ vector from a PCI interrupt vector allocation.
79///
80/// Created by [`IrqVectorRegistration::index`]. Convert to [`IrqRequest`] via [`From`] to register
81/// a handler with [`irq::Registration::new`](crate::irq::Registration::new).
82pub struct IrqVector<'a> {
83 request: IrqRequest<'a>,
84 reg: &'a IrqVectorRegistration<'a>,
85}
86
87impl<'a> IrqVector<'a> {
88 /// Creates a new [`IrqVector`] with an already resolved [`IrqRequest`].
89 ///
90 /// # Safety
91 ///
92 /// `request` must have been resolved from `reg`.
93 #[inline]
94 unsafe fn new(request: IrqRequest<'a>, reg: &'a IrqVectorRegistration<'a>) -> Self {
95 Self { request, reg }
96 }
97
98 /// Returns the [`IrqVectorRegistration`] this vector was derived from.
99 #[inline]
100 pub fn vectors(&self) -> &'a IrqVectorRegistration<'a> {
101 self.reg
102 }
103
104 /// Returns the interrupt type the PCI core selected for this vector's allocation.
105 #[inline]
106 pub fn irq_type(&self) -> IrqType {
107 self.reg.irq_type()
108 }
109}
110
111impl<'a> From<IrqVector<'a>> for IrqRequest<'a> {
112 #[inline]
113 fn from(vector: IrqVector<'a>) -> Self {
114 vector.request
115 }
116}
117
118/// An allocation of PCI interrupt vectors for a device.
119///
120/// This type owns the vector allocation; dropping it frees the vectors. IRQ handlers borrow from
121/// this registration and must be dropped before it is.
122///
123/// # Invariants
124///
125/// `dev` has an allocation of `len` interrupt vectors.
126pub struct IrqVectorRegistration<'a> {
127 dev: &'a Device<Bound>,
128 len: NonZero<usize>,
129}
130
131impl<'a> IrqVectorRegistration<'a> {
132 /// Returns the number of allocated vectors.
133 ///
134 /// This is at least the `min_vecs` that [`Device::alloc_irq_vectors`] was asked for.
135 #[inline]
136 #[allow(clippy::len_without_is_empty)]
137 pub fn len(&self) -> usize {
138 self.len.get()
139 }
140
141 /// Returns the interrupt type the PCI core selected for this allocation.
142 #[inline]
143 pub fn irq_type(&self) -> IrqType {
144 // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
145 IrqType::from_raw(unsafe { bindings::pci_irq_type(self.dev.as_raw()) })
146 }
147
148 /// Returns the [`IrqVector`] at `index`.
149 ///
150 /// Returns [`EINVAL`] if the `index` is out of bounds for the length reported by
151 /// [`Self::len()`].
152 #[inline]
153 pub fn index(&self, index: usize) -> Result<IrqVector<'_>> {
154 // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
155 let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index as u32) };
156 if irq < 0 {
157 return Err(Error::from_errno(irq));
158 }
159
160 // SAFETY: `irq` is a valid IRQ number for `self.dev`, resolved from this registration.
161 Ok(unsafe { IrqVector::new(IrqRequest::new(self.dev.as_ref(), irq as u32), self) })
162 }
163}
164
165impl Drop for IrqVectorRegistration<'_> {
166 #[inline]
167 fn drop(&mut self) {
168 // SAFETY: By the type invariant, `self.dev.as_raw()` is a valid pointer to a
169 // `struct pci_dev` that has successfully allocated IRQ vectors.
170 unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
171 }
172}
173
174impl Device<device::Bound> {
175 /// Allocate IRQ vectors for this PCI device.
176 ///
177 /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
178 /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
179 /// parameter and hardware capabilities. When multiple types are specified, the kernel
180 /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
181 ///
182 /// The allocated vectors are freed when the returned [`IrqVectorRegistration`] is dropped.
183 /// Use [`IrqVectorRegistration::index`] to obtain an [`IrqVector`] for a given vector
184 /// index.
185 ///
186 /// # Arguments
187 ///
188 /// * `min_vecs` - Minimum number of vectors required.
189 /// * `max_vecs` - Maximum number of vectors to allocate.
190 /// * `irq_types` - Types of interrupts that can be used.
191 ///
192 /// # Returns
193 ///
194 /// Returns the IRQ vector registration, or an error if `min_vecs` vectors cannot be
195 /// allocated.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// # use kernel::{ device::Bound, pci};
201 /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
202 /// // Allocate using any available interrupt type in the order mentioned above.
203 /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
204 ///
205 /// // Allocate MSI or MSI-X only (no INTx interrupts).
206 /// let msi_only = pci::IrqTypes::default()
207 /// .with(pci::IrqType::Msi)
208 /// .with(pci::IrqType::MsiX);
209 /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
210 /// # Ok(())
211 /// # }
212 /// ```
213 pub fn alloc_irq_vectors(
214 &self,
215 min_vecs: u32,
216 max_vecs: u32,
217 irq_types: IrqTypes,
218 ) -> Result<IrqVectorRegistration<'_>> {
219 // SAFETY:
220 // - `self.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
221 // by the type invariant of `Device`.
222 // - `pci_alloc_irq_vectors` internally validates all other parameters
223 // and returns error codes.
224 let ret = unsafe {
225 bindings::pci_alloc_irq_vectors(self.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
226 };
227 to_result(ret)?;
228
229 let len = NonZero::new(ret as usize).ok_or(EINVAL)?;
230
231 // INVARIANT: `pci_alloc_irq_vectors()` allocated `len` vectors for `self`.
232 Ok(IrqVectorRegistration { dev: self, len })
233 }
234}