use super::Device;
use crate::{
bindings,
device,
device::Bound,
devres,
error::to_result,
irq::{
self,
IrqRequest, },
prelude::*,
str::CStr,
sync::aref::ARef, };
use core::ops::RangeInclusive;
#[derive(Debug, Clone, Copy)]
pub enum IrqType {
Intx,
Msi,
MsiX,
}
impl IrqType {
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,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct IrqTypes(u32);
impl IrqTypes {
pub const fn all() -> Self {
Self(bindings::PCI_IRQ_ALL_TYPES)
}
pub const fn with(self, irq_type: IrqType) -> Self {
Self(self.0 | irq_type.as_raw())
}
const fn as_raw(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy)]
pub struct IrqVector<'a> {
dev: &'a Device<Bound>,
index: u32,
}
impl<'a> IrqVector<'a> {
unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self {
Self { dev, 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>> {
let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) };
if irq < 0 {
return Err(crate::error::Error::from_errno(irq));
}
Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) })
}
}
struct IrqVectorRegistration {
dev: ARef<Device>,
}
impl IrqVectorRegistration {
fn register<'a>(
dev: &'a Device<Bound>,
min_vecs: u32,
max_vecs: u32,
irq_types: IrqTypes,
) -> Result<RangeInclusive<IrqVector<'a>>> {
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;
let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };
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) {
unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
}
}
impl Device<device::Bound> {
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,
) -> impl PinInit<irq::Registration<T>, Error> + 'a {
pin_init::pin_init_scope(move || {
let request = vector.try_into()?;
Ok(irq::Registration::<T>::new(request, flags, name, handler))
})
}
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,
) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
pin_init::pin_init_scope(move || {
let request = vector.try_into()?;
Ok(irq::ThreadedRegistration::<T>::new(
request, flags, name, handler,
))
})
}
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)
}
}