Loading rust/kernel/pci.rs +3 −233 Original line number Diff line number Diff line Loading @@ -6,80 +6,26 @@ use crate::{ bindings, container_of, device, device::Bound, device_id::{RawDeviceId, RawDeviceIdIndex}, devres, driver, driver, error::{from_result, to_result, Result}, irq::{self, IrqRequest}, str::CStr, sync::aref::ARef, types::Opaque, ThisModule, }; use core::{ marker::PhantomData, ops::RangeInclusive, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; mod id; mod io; mod irq; pub use self::id::{Class, ClassMask, Vendor}; pub use self::io::Bar; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] pub enum IrqType { /// INTx interrupts. Intx, /// Message Signaled Interrupts (MSI). Msi, /// Extended Message Signaled Interrupts (MSI-X). MsiX, } impl IrqType { /// Convert to the corresponding kernel flags. const fn as_raw(self) -> u32 { match self { IrqType::Intx => bindings::PCI_IRQ_INTX, IrqType::Msi => bindings::PCI_IRQ_MSI, IrqType::MsiX => bindings::PCI_IRQ_MSIX, } } } /// Set of IRQ types that can be used for PCI interrupt allocation. #[derive(Debug, Clone, Copy, Default)] pub struct IrqTypes(u32); impl IrqTypes { /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). pub const fn all() -> Self { Self(bindings::PCI_IRQ_ALL_TYPES) } /// Build a set of IRQ types. /// /// # Examples /// /// ```ignore /// // Create a set with only MSI and MSI-X (no legacy interrupts). /// let msi_only = IrqTypes::default() /// .with(IrqType::Msi) /// .with(IrqType::MsiX); /// ``` pub const fn with(self, irq_type: IrqType) -> Self { Self(self.0 | irq_type.as_raw()) } /// Get the raw flags value. const fn as_raw(self) -> u32 { self.0 } } pub use self::irq::{IrqType, IrqTypes, IrqVector}; /// An adapter for the registration of PCI drivers. pub struct Adapter<T: Driver>(T); Loading Loading @@ -463,182 +409,6 @@ pub fn pci_class(&self) -> Class { } } /// Represents an allocated IRQ vector for a specific PCI device. /// /// This type ties an IRQ vector to the device it was allocated for, /// ensuring the vector is only used with the correct device. #[derive(Clone, Copy)] pub struct IrqVector<'a> { dev: &'a Device<Bound>, index: u32, } impl<'a> IrqVector<'a> { /// Creates a new [`IrqVector`] for the given device and index. /// /// # Safety /// /// - `index` must be a valid IRQ vector index for `dev`. /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { Self { dev, index } } /// Returns the raw vector index. fn index(&self) -> u32 { self.index } } impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> { type Error = Error; fn try_into(self) -> Result<IrqRequest<'a>> { // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; if irq < 0 { return Err(crate::error::Error::from_errno(irq)); } // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) } } /// Represents an IRQ vector allocation for a PCI device. /// /// This type ensures that IRQ vectors are properly allocated and freed by /// tying the allocation to the lifetime of this registration object. /// /// # Invariants /// /// The [`Device`] has successfully allocated IRQ vectors. struct IrqVectorRegistration { dev: ARef<Device>, } impl IrqVectorRegistration { /// Allocate and register IRQ vectors for the given PCI device. /// /// Allocates IRQ vectors and registers them with devres for automatic cleanup. /// Returns a range of valid IRQ vectors. fn register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>> { // SAFETY: // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` // by the type invariant of `Device`. // - `pci_alloc_irq_vectors` internally validates all other parameters // and returns error codes. let ret = unsafe { bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) }; to_result(ret)?; let count = ret as u32; // SAFETY: // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. // - Vectors are 0-based, so valid indices are [0, count-1]. // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and // `count - 1` are valid IRQ vector indices for `dev`. let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; // INVARIANT: The IRQ vector allocation for `dev` above was successful. let irq_vecs = Self { dev: dev.into() }; devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; Ok(range) } } impl Drop for IrqVectorRegistration { fn drop(&mut self) { // SAFETY: // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. // - `self.dev` has successfully allocated IRQ vectors. unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; } } impl Device<device::Bound> { /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::Registration::<T>::new(request, flags, name, handler)) } /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::ThreadedRegistration::<T>::new( request, flags, name, handler, )) } /// Allocate IRQ vectors for this PCI device with automatic cleanup. /// /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. /// The allocation will use MSI-X, MSI, or legacy interrupts based on the `irq_types` /// parameter and hardware capabilities. When multiple types are specified, the kernel /// will try them in order of preference: MSI-X first, then MSI, then legacy interrupts. /// /// The allocated vectors are automatically freed when the device is unbound, using the /// devres (device resource management) system. /// /// # Arguments /// /// * `min_vecs` - Minimum number of vectors required. /// * `max_vecs` - Maximum number of vectors to allocate. /// * `irq_types` - Types of interrupts that can be used. /// /// # Returns /// /// Returns a range of IRQ vectors that were successfully allocated, or an error if the /// allocation fails or cannot meet the minimum requirement. /// /// # Examples /// /// ``` /// # use kernel::{ device::Bound, pci}; /// # fn no_run(dev: &pci::Device<Bound>) -> Result { /// // Allocate using any available interrupt type in the order mentioned above. /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; /// /// // Allocate MSI or MSI-X only (no legacy interrupts). /// let msi_only = pci::IrqTypes::default() /// .with(pci::IrqType::Msi) /// .with(pci::IrqType::MsiX); /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; /// # Ok(()) /// # } /// ``` pub fn alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>> { IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) } } impl Device<device::Core> { /// Enable memory resources for this device. pub fn enable_device_mem(&self) -> Result { Loading rust/kernel/pci/irq.rs 0 → 100644 +244 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 //! PCI interrupt infrastructure. use super::Device; use crate::{ bindings, device, device::Bound, devres, error::{to_result, Result}, irq::{self, IrqRequest}, str::CStr, sync::aref::ARef, }; use core::ops::RangeInclusive; use kernel::prelude::*; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] pub enum IrqType { /// INTx interrupts. Intx, /// Message Signaled Interrupts (MSI). Msi, /// Extended Message Signaled Interrupts (MSI-X). MsiX, } impl IrqType { /// Convert to the corresponding kernel flags. const fn as_raw(self) -> u32 { match self { IrqType::Intx => bindings::PCI_IRQ_INTX, IrqType::Msi => bindings::PCI_IRQ_MSI, IrqType::MsiX => bindings::PCI_IRQ_MSIX, } } } /// Set of IRQ types that can be used for PCI interrupt allocation. #[derive(Debug, Clone, Copy, Default)] pub struct IrqTypes(u32); impl IrqTypes { /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). pub const fn all() -> Self { Self(bindings::PCI_IRQ_ALL_TYPES) } /// Build a set of IRQ types. /// /// # Examples /// /// ```ignore /// // Create a set with only MSI and MSI-X (no legacy interrupts). /// let msi_only = IrqTypes::default() /// .with(IrqType::Msi) /// .with(IrqType::MsiX); /// ``` pub const fn with(self, irq_type: IrqType) -> Self { Self(self.0 | irq_type.as_raw()) } /// Get the raw flags value. const fn as_raw(self) -> u32 { self.0 } } /// Represents an allocated IRQ vector for a specific PCI device. /// /// This type ties an IRQ vector to the device it was allocated for, /// ensuring the vector is only used with the correct device. #[derive(Clone, Copy)] pub struct IrqVector<'a> { dev: &'a Device<Bound>, index: u32, } impl<'a> IrqVector<'a> { /// Creates a new [`IrqVector`] for the given device and index. /// /// # Safety /// /// - `index` must be a valid IRQ vector index for `dev`. /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { Self { dev, index } } /// Returns the raw vector index. fn index(&self) -> u32 { self.index } } impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> { type Error = Error; fn try_into(self) -> Result<IrqRequest<'a>> { // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; if irq < 0 { return Err(crate::error::Error::from_errno(irq)); } // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) } } /// Represents an IRQ vector allocation for a PCI device. /// /// This type ensures that IRQ vectors are properly allocated and freed by /// tying the allocation to the lifetime of this registration object. /// /// # Invariants /// /// The [`Device`] has successfully allocated IRQ vectors. struct IrqVectorRegistration { dev: ARef<Device>, } impl IrqVectorRegistration { /// Allocate and register IRQ vectors for the given PCI device. /// /// Allocates IRQ vectors and registers them with devres for automatic cleanup. /// Returns a range of valid IRQ vectors. fn register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>> { // SAFETY: // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` // by the type invariant of `Device`. // - `pci_alloc_irq_vectors` internally validates all other parameters // and returns error codes. let ret = unsafe { bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) }; to_result(ret)?; let count = ret as u32; // SAFETY: // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. // - Vectors are 0-based, so valid indices are [0, count-1]. // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and // `count - 1` are valid IRQ vector indices for `dev`. let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; // INVARIANT: The IRQ vector allocation for `dev` above was successful. let irq_vecs = Self { dev: dev.into() }; devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; Ok(range) } } impl Drop for IrqVectorRegistration { fn drop(&mut self) { // SAFETY: // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. // - `self.dev` has successfully allocated IRQ vectors. unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; } } impl Device<device::Bound> { /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::Registration::<T>::new(request, flags, name, handler)) } /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::ThreadedRegistration::<T>::new( request, flags, name, handler, )) } /// Allocate IRQ vectors for this PCI device with automatic cleanup. /// /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. /// The allocation will use MSI-X, MSI, or legacy interrupts based on the `irq_types` /// parameter and hardware capabilities. When multiple types are specified, the kernel /// will try them in order of preference: MSI-X first, then MSI, then legacy interrupts. /// /// The allocated vectors are automatically freed when the device is unbound, using the /// devres (device resource management) system. /// /// # Arguments /// /// * `min_vecs` - Minimum number of vectors required. /// * `max_vecs` - Maximum number of vectors to allocate. /// * `irq_types` - Types of interrupts that can be used. /// /// # Returns /// /// Returns a range of IRQ vectors that were successfully allocated, or an error if the /// allocation fails or cannot meet the minimum requirement. /// /// # Examples /// /// ``` /// # use kernel::{ device::Bound, pci}; /// # fn no_run(dev: &pci::Device<Bound>) -> Result { /// // Allocate using any available interrupt type in the order mentioned above. /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; /// /// // Allocate MSI or MSI-X only (no legacy interrupts). /// let msi_only = pci::IrqTypes::default() /// .with(pci::IrqType::Msi) /// .with(pci::IrqType::MsiX); /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; /// # Ok(()) /// # } /// ``` pub fn alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>> { IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) } } Loading
rust/kernel/pci.rs +3 −233 Original line number Diff line number Diff line Loading @@ -6,80 +6,26 @@ use crate::{ bindings, container_of, device, device::Bound, device_id::{RawDeviceId, RawDeviceIdIndex}, devres, driver, driver, error::{from_result, to_result, Result}, irq::{self, IrqRequest}, str::CStr, sync::aref::ARef, types::Opaque, ThisModule, }; use core::{ marker::PhantomData, ops::RangeInclusive, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; mod id; mod io; mod irq; pub use self::id::{Class, ClassMask, Vendor}; pub use self::io::Bar; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] pub enum IrqType { /// INTx interrupts. Intx, /// Message Signaled Interrupts (MSI). Msi, /// Extended Message Signaled Interrupts (MSI-X). MsiX, } impl IrqType { /// Convert to the corresponding kernel flags. const fn as_raw(self) -> u32 { match self { IrqType::Intx => bindings::PCI_IRQ_INTX, IrqType::Msi => bindings::PCI_IRQ_MSI, IrqType::MsiX => bindings::PCI_IRQ_MSIX, } } } /// Set of IRQ types that can be used for PCI interrupt allocation. #[derive(Debug, Clone, Copy, Default)] pub struct IrqTypes(u32); impl IrqTypes { /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). pub const fn all() -> Self { Self(bindings::PCI_IRQ_ALL_TYPES) } /// Build a set of IRQ types. /// /// # Examples /// /// ```ignore /// // Create a set with only MSI and MSI-X (no legacy interrupts). /// let msi_only = IrqTypes::default() /// .with(IrqType::Msi) /// .with(IrqType::MsiX); /// ``` pub const fn with(self, irq_type: IrqType) -> Self { Self(self.0 | irq_type.as_raw()) } /// Get the raw flags value. const fn as_raw(self) -> u32 { self.0 } } pub use self::irq::{IrqType, IrqTypes, IrqVector}; /// An adapter for the registration of PCI drivers. pub struct Adapter<T: Driver>(T); Loading Loading @@ -463,182 +409,6 @@ pub fn pci_class(&self) -> Class { } } /// Represents an allocated IRQ vector for a specific PCI device. /// /// This type ties an IRQ vector to the device it was allocated for, /// ensuring the vector is only used with the correct device. #[derive(Clone, Copy)] pub struct IrqVector<'a> { dev: &'a Device<Bound>, index: u32, } impl<'a> IrqVector<'a> { /// Creates a new [`IrqVector`] for the given device and index. /// /// # Safety /// /// - `index` must be a valid IRQ vector index for `dev`. /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { Self { dev, index } } /// Returns the raw vector index. fn index(&self) -> u32 { self.index } } impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> { type Error = Error; fn try_into(self) -> Result<IrqRequest<'a>> { // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; if irq < 0 { return Err(crate::error::Error::from_errno(irq)); } // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) } } /// Represents an IRQ vector allocation for a PCI device. /// /// This type ensures that IRQ vectors are properly allocated and freed by /// tying the allocation to the lifetime of this registration object. /// /// # Invariants /// /// The [`Device`] has successfully allocated IRQ vectors. struct IrqVectorRegistration { dev: ARef<Device>, } impl IrqVectorRegistration { /// Allocate and register IRQ vectors for the given PCI device. /// /// Allocates IRQ vectors and registers them with devres for automatic cleanup. /// Returns a range of valid IRQ vectors. fn register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>> { // SAFETY: // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` // by the type invariant of `Device`. // - `pci_alloc_irq_vectors` internally validates all other parameters // and returns error codes. let ret = unsafe { bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) }; to_result(ret)?; let count = ret as u32; // SAFETY: // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. // - Vectors are 0-based, so valid indices are [0, count-1]. // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and // `count - 1` are valid IRQ vector indices for `dev`. let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; // INVARIANT: The IRQ vector allocation for `dev` above was successful. let irq_vecs = Self { dev: dev.into() }; devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; Ok(range) } } impl Drop for IrqVectorRegistration { fn drop(&mut self) { // SAFETY: // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. // - `self.dev` has successfully allocated IRQ vectors. unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; } } impl Device<device::Bound> { /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::Registration::<T>::new(request, flags, name, handler)) } /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::ThreadedRegistration::<T>::new( request, flags, name, handler, )) } /// Allocate IRQ vectors for this PCI device with automatic cleanup. /// /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. /// The allocation will use MSI-X, MSI, or legacy interrupts based on the `irq_types` /// parameter and hardware capabilities. When multiple types are specified, the kernel /// will try them in order of preference: MSI-X first, then MSI, then legacy interrupts. /// /// The allocated vectors are automatically freed when the device is unbound, using the /// devres (device resource management) system. /// /// # Arguments /// /// * `min_vecs` - Minimum number of vectors required. /// * `max_vecs` - Maximum number of vectors to allocate. /// * `irq_types` - Types of interrupts that can be used. /// /// # Returns /// /// Returns a range of IRQ vectors that were successfully allocated, or an error if the /// allocation fails or cannot meet the minimum requirement. /// /// # Examples /// /// ``` /// # use kernel::{ device::Bound, pci}; /// # fn no_run(dev: &pci::Device<Bound>) -> Result { /// // Allocate using any available interrupt type in the order mentioned above. /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; /// /// // Allocate MSI or MSI-X only (no legacy interrupts). /// let msi_only = pci::IrqTypes::default() /// .with(pci::IrqType::Msi) /// .with(pci::IrqType::MsiX); /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; /// # Ok(()) /// # } /// ``` pub fn alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>> { IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) } } impl Device<device::Core> { /// Enable memory resources for this device. pub fn enable_device_mem(&self) -> Result { Loading
rust/kernel/pci/irq.rs 0 → 100644 +244 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 //! PCI interrupt infrastructure. use super::Device; use crate::{ bindings, device, device::Bound, devres, error::{to_result, Result}, irq::{self, IrqRequest}, str::CStr, sync::aref::ARef, }; use core::ops::RangeInclusive; use kernel::prelude::*; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] pub enum IrqType { /// INTx interrupts. Intx, /// Message Signaled Interrupts (MSI). Msi, /// Extended Message Signaled Interrupts (MSI-X). MsiX, } impl IrqType { /// Convert to the corresponding kernel flags. const fn as_raw(self) -> u32 { match self { IrqType::Intx => bindings::PCI_IRQ_INTX, IrqType::Msi => bindings::PCI_IRQ_MSI, IrqType::MsiX => bindings::PCI_IRQ_MSIX, } } } /// Set of IRQ types that can be used for PCI interrupt allocation. #[derive(Debug, Clone, Copy, Default)] pub struct IrqTypes(u32); impl IrqTypes { /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). pub const fn all() -> Self { Self(bindings::PCI_IRQ_ALL_TYPES) } /// Build a set of IRQ types. /// /// # Examples /// /// ```ignore /// // Create a set with only MSI and MSI-X (no legacy interrupts). /// let msi_only = IrqTypes::default() /// .with(IrqType::Msi) /// .with(IrqType::MsiX); /// ``` pub const fn with(self, irq_type: IrqType) -> Self { Self(self.0 | irq_type.as_raw()) } /// Get the raw flags value. const fn as_raw(self) -> u32 { self.0 } } /// Represents an allocated IRQ vector for a specific PCI device. /// /// This type ties an IRQ vector to the device it was allocated for, /// ensuring the vector is only used with the correct device. #[derive(Clone, Copy)] pub struct IrqVector<'a> { dev: &'a Device<Bound>, index: u32, } impl<'a> IrqVector<'a> { /// Creates a new [`IrqVector`] for the given device and index. /// /// # Safety /// /// - `index` must be a valid IRQ vector index for `dev`. /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self { Self { dev, index } } /// Returns the raw vector index. fn index(&self) -> u32 { self.index } } impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> { type Error = Error; fn try_into(self) -> Result<IrqRequest<'a>> { // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; if irq < 0 { return Err(crate::error::Error::from_errno(irq)); } // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) } } /// Represents an IRQ vector allocation for a PCI device. /// /// This type ensures that IRQ vectors are properly allocated and freed by /// tying the allocation to the lifetime of this registration object. /// /// # Invariants /// /// The [`Device`] has successfully allocated IRQ vectors. struct IrqVectorRegistration { dev: ARef<Device>, } impl IrqVectorRegistration { /// Allocate and register IRQ vectors for the given PCI device. /// /// Allocates IRQ vectors and registers them with devres for automatic cleanup. /// Returns a range of valid IRQ vectors. fn register<'a>( dev: &'a Device<Bound>, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'a>>> { // SAFETY: // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev` // by the type invariant of `Device`. // - `pci_alloc_irq_vectors` internally validates all other parameters // and returns error codes. let ret = unsafe { bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw()) }; to_result(ret)?; let count = ret as u32; // SAFETY: // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success. // - Vectors are 0-based, so valid indices are [0, count-1]. // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and // `count - 1` are valid IRQ vector indices for `dev`. let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) }; // INVARIANT: The IRQ vector allocation for `dev` above was successful. let irq_vecs = Self { dev: dev.into() }; devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?; Ok(range) } } impl Drop for IrqVectorRegistration { fn drop(&mut self) { // SAFETY: // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. // - `self.dev` has successfully allocated IRQ vectors. unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) }; } } impl Device<device::Bound> { /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::Registration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::Registration::<T>::new(request, flags, name, handler)) } /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. pub fn 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, ) -> Result<impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a> { let request = vector.try_into()?; Ok(irq::ThreadedRegistration::<T>::new( request, flags, name, handler, )) } /// Allocate IRQ vectors for this PCI device with automatic cleanup. /// /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device. /// The allocation will use MSI-X, MSI, or legacy interrupts based on the `irq_types` /// parameter and hardware capabilities. When multiple types are specified, the kernel /// will try them in order of preference: MSI-X first, then MSI, then legacy interrupts. /// /// The allocated vectors are automatically freed when the device is unbound, using the /// devres (device resource management) system. /// /// # Arguments /// /// * `min_vecs` - Minimum number of vectors required. /// * `max_vecs` - Maximum number of vectors to allocate. /// * `irq_types` - Types of interrupts that can be used. /// /// # Returns /// /// Returns a range of IRQ vectors that were successfully allocated, or an error if the /// allocation fails or cannot meet the minimum requirement. /// /// # Examples /// /// ``` /// # use kernel::{ device::Bound, pci}; /// # fn no_run(dev: &pci::Device<Bound>) -> Result { /// // Allocate using any available interrupt type in the order mentioned above. /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; /// /// // Allocate MSI or MSI-X only (no legacy interrupts). /// let msi_only = pci::IrqTypes::default() /// .with(pci::IrqType::Msi) /// .with(pci::IrqType::MsiX); /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; /// # Ok(()) /// # } /// ``` pub fn alloc_irq_vectors( &self, min_vecs: u32, max_vecs: u32, irq_types: IrqTypes, ) -> Result<RangeInclusive<IrqVector<'_>>> { IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) } }