use crate::{
bindings,
container_of,
device,
device_id::{
RawDeviceId,
RawDeviceIdIndex, },
driver,
error::{
from_result,
to_result, },
prelude::*,
str::CStr,
types::Opaque,
ThisModule, };
use core::{
marker::PhantomData,
mem::offset_of,
ptr::{
addr_of_mut,
NonNull, },
};
mod id;
mod io;
mod irq;
pub use self::id::{
Class,
ClassMask,
Vendor, };
pub use self::io::{
Bar,
ConfigSpace,
ConfigSpaceKind,
ConfigSpaceSize,
Extended,
Normal, };
pub use self::irq::{
IrqType,
IrqTypes,
IrqVector, };
pub struct Adapter<T: Driver>(T);
unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
type DriverType = bindings::pci_driver;
type DriverData = T;
const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
}
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
unsafe fn register(
pdrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
unsafe {
(*pdrv.get()).name = name.as_char_ptr();
(*pdrv.get()).probe = Some(Self::probe_callback);
(*pdrv.get()).remove = Some(Self::remove_callback);
(*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
}
to_result(unsafe {
bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr())
})
}
unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
unsafe { bindings::pci_unregister_driver(pdrv.get()) }
}
}
impl<T: Driver + 'static> Adapter<T> {
extern "C" fn probe_callback(
pdev: *mut bindings::pci_dev,
id: *const bindings::pci_device_id,
) -> c_int {
let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
let id = unsafe { &*id.cast::<DeviceId>() };
let info = T::ID_TABLE.info(id.index());
from_result(|| {
let data = T::probe(pdev, info);
pdev.as_ref().set_drvdata(data)?;
Ok(0)
})
}
extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
let data = unsafe { pdev.as_ref().drvdata_borrow::<T>() };
T::unbind(pdev, data);
}
}
#[macro_export]
macro_rules! module_pci_driver {
($($f:tt)*) => {
$crate::module_driver!(<T>, $crate::pci::Adapter<T>, { $($f)* });
};
}
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct DeviceId(bindings::pci_device_id);
impl DeviceId {
const PCI_ANY_ID: u32 = !0;
#[inline]
pub const fn from_id(vendor: Vendor, device: u32) -> Self {
Self(bindings::pci_device_id {
vendor: vendor.as_raw() as u32,
device,
subvendor: DeviceId::PCI_ANY_ID,
subdevice: DeviceId::PCI_ANY_ID,
class: 0,
class_mask: 0,
driver_data: 0,
override_only: 0,
})
}
#[inline]
pub const fn from_class(class: u32, class_mask: u32) -> Self {
Self(bindings::pci_device_id {
vendor: DeviceId::PCI_ANY_ID,
device: DeviceId::PCI_ANY_ID,
subvendor: DeviceId::PCI_ANY_ID,
subdevice: DeviceId::PCI_ANY_ID,
class,
class_mask,
driver_data: 0,
override_only: 0,
})
}
#[inline]
pub const fn from_class_and_vendor(
class: Class,
class_mask: ClassMask,
vendor: Vendor,
) -> Self {
Self(bindings::pci_device_id {
vendor: vendor.as_raw() as u32,
device: DeviceId::PCI_ANY_ID,
subvendor: DeviceId::PCI_ANY_ID,
subdevice: DeviceId::PCI_ANY_ID,
class: class.as_raw(),
class_mask: class_mask.as_raw(),
driver_data: 0,
override_only: 0,
})
}
}
unsafe impl RawDeviceId for DeviceId {
type RawType = bindings::pci_device_id;
}
unsafe impl RawDeviceIdIndex for DeviceId {
const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data);
fn index(&self) -> usize {
self.0.driver_data
}
}
pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
#[macro_export]
macro_rules! pci_device_table {
($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
const $table_name: $crate::device_id::IdArray<
$crate::pci::DeviceId,
$id_info_type,
{ $table_data.len() },
> = $crate::device_id::IdArray::new($table_data);
$crate::module_device_table!("pci", $module_table_name, $table_name);
};
}
pub trait Driver: Send {
type IdInfo: 'static;
const ID_TABLE: IdTable<Self::IdInfo>;
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
let _ = (dev, this);
}
}
#[repr(transparent)]
pub struct Device<Ctx: device::DeviceContext = device::Normal>(
Opaque<bindings::pci_dev>,
PhantomData<Ctx>,
);
impl<Ctx: device::DeviceContext> Device<Ctx> {
#[inline]
fn as_raw(&self) -> *mut bindings::pci_dev {
self.0.get()
}
}
impl Device {
#[inline]
pub fn vendor_id(&self) -> Vendor {
let vendor_id = unsafe { (*self.as_raw()).vendor };
Vendor::from_raw(vendor_id)
}
#[inline]
pub fn device_id(&self) -> u16 {
unsafe { (*self.as_raw()).device }
}
#[inline]
pub fn revision_id(&self) -> u8 {
unsafe { (*self.as_raw()).revision }
}
#[inline]
pub fn dev_id(&self) -> u16 {
unsafe { bindings::pci_dev_id(self.as_raw()) }
}
#[inline]
pub fn subsystem_vendor_id(&self) -> u16 {
unsafe { (*self.as_raw()).subsystem_vendor }
}
#[inline]
pub fn subsystem_device_id(&self) -> u16 {
unsafe { (*self.as_raw()).subsystem_device }
}
pub fn resource_start(&self, bar: u32) -> Result<bindings::resource_size_t> {
if !Bar::index_is_valid(bar) {
return Err(EINVAL);
}
Ok(unsafe { bindings::pci_resource_start(self.as_raw(), bar.try_into()?) })
}
pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
if !Bar::index_is_valid(bar) {
return Err(EINVAL);
}
Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
}
#[inline]
pub fn pci_class(&self) -> Class {
Class::from_raw(unsafe { (*self.as_raw()).class })
}
}
impl Device<device::Core> {
pub fn enable_device_mem(&self) -> Result {
to_result(unsafe { bindings::pci_enable_device_mem(self.as_raw()) })
}
#[inline]
pub fn set_master(&self) {
unsafe { bindings::pci_set_master(self.as_raw()) };
}
}
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
const OFFSET: usize = offset_of!(bindings::pci_dev, dev);
}
kernel::impl_device_context_deref!(unsafe { Device });
kernel::impl_device_context_into_aref!(Device);
impl crate::dma::Device for Device<device::Core> {}
unsafe impl crate::sync::aref::AlwaysRefCounted for Device {
fn inc_ref(&self) {
unsafe { bindings::pci_dev_get(self.as_raw()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
unsafe { bindings::pci_dev_put(obj.cast().as_ptr()) }
}
}
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
fn as_ref(&self) -> &device::Device<Ctx> {
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
unsafe { device::Device::from_raw(dev) }
}
}
impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &Device<Ctx> {
type Error = kernel::error::Error;
fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
if !unsafe { bindings::dev_is_pci(dev.as_raw()) } {
return Err(EINVAL);
}
let pdev = unsafe { container_of!(dev.as_raw(), bindings::pci_dev, dev) };
Ok(unsafe { &*pdev.cast() })
}
}
unsafe impl Send for Device {}
unsafe impl Sync for Device {}