diff options
author | FUJITA Tomonori <fujita.tomonori@gmail.com> | 2022-08-05 05:27:24 +0000 |
---|---|---|
committer | Danilo Krummrich <dakr@redhat.com> | 2023-11-22 22:00:54 +0100 |
commit | b517bc329cb6775323b44413afbf804f3ed40320 (patch) | |
tree | 7255d94a81638673a9362578d8e3082b18fc8c70 | |
parent | 24e0ea9eeb1a5a5983fae0a6a468665b1e05c04d (diff) |
rust: add initial PCI support
Added minimum abstration APIs for PCI.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
-rw-r--r-- | rust/bindings/bindings_helper.h | 5 | ||||
-rw-r--r-- | rust/helpers.c | 13 | ||||
-rw-r--r-- | rust/kernel/lib.rs | 3 | ||||
-rw-r--r-- | rust/kernel/pci.rs | 108 |
4 files changed, 127 insertions, 2 deletions
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 5da948cadf2b..5a453a5447a9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,12 +11,13 @@ #include <linux/errname.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/pci.h> #include <linux/slab.h> #include <linux/refcount.h> #include <linux/wait.h> #include <linux/sched.h> -/* `bindgen` gets confused at certain things. */ -const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; + /* `bindgen` gets confused at certain things. */ + const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO; diff --git a/rust/helpers.c b/rust/helpers.c index 0abfc5228803..3d9cf590a45a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -28,6 +28,7 @@ #include <linux/errname.h> #include <linux/highmem.h> #include <linux/mutex.h> +#include <linux/pci.h> #include <linux/refcount.h> #include <linux/sched/signal.h> #include <linux/spinlock.h> @@ -188,6 +189,18 @@ const char *rust_helper_dev_name(const struct device *dev) } EXPORT_SYMBOL_GPL(rust_helper_dev_name); +void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) +{ + pci_set_drvdata(pdev, data); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_set_drvdata); + +void *rust_helper_pci_get_drvdata(struct pci_dev *pdev) +{ + return pci_get_drvdata(pdev); +} +EXPORT_SYMBOL_GPL(rust_helper_pci_get_drvdata); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 976d1e2084b9..32ebadad2c5c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -13,6 +13,7 @@ #![no_std] #![feature(allocator_api)] +#![feature(associated_type_defaults)] #![feature(coerce_unsized)] #![feature(const_refs_to_cell)] #![feature(const_trait_impl)] @@ -55,6 +56,8 @@ pub mod types; #[doc(hidden)] pub use bindings; pub use macros; +#[cfg(CONFIG_PCI)] +pub mod pci; pub use uapi; #[doc(hidden)] diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs new file mode 100644 index 000000000000..dacbdd17ff6b --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI devices and drivers. +//! +//! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h) + +use crate::{ + bindings, device, driver, + error::{from_result, to_result, Result}, + str::CStr, + types::ForeignOwnable, + ThisModule, +}; + +/// An adapter for the registration of PCI drivers. +pub struct Adapter<T: Driver>(T); + +impl<T: Driver> driver::DriverOps for Adapter<T> { + type RegType = bindings::pci_driver; + + unsafe fn register( + reg: *mut bindings::pci_driver, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg }; + + pdrv.name = name.as_char_ptr(); + pdrv.probe = Some(Self::probe_callback); + pdrv.remove = Some(Self::remove_callback); + pdrv.id_table = T::PCI_ID_TABLE.as_ptr(); + to_result(unsafe { bindings::__pci_register_driver(reg, module.0, name.as_char_ptr()) }) + } + + unsafe fn unregister(reg: *mut bindings::pci_driver) { + unsafe { bindings::pci_unregister_driver(reg) } + } +} + +impl<T: Driver> Adapter<T> { + extern "C" fn probe_callback( + pdev: *mut bindings::pci_dev, + _id: *const bindings::pci_device_id, + ) -> core::ffi::c_int { + from_result(|| { + let mut dev = unsafe { Device::from_ptr(pdev) }; + let data = T::probe(&mut dev)?; + unsafe { bindings::pci_set_drvdata(pdev, data.into_foreign() as _) }; + Ok(0) + }) + } + + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { + let ptr = unsafe { bindings::pci_get_drvdata(pdev) }; + let data = unsafe { T::Data::from_foreign(ptr) }; + T::remove(&data); + <T::Data as driver::DeviceRemoval>::device_remove(&data); + } +} + +/// A PCI driver +pub trait Driver { + /// Data stored on device by driver. + /// + /// Corresponds to the data set or retrieved via the kernel's + /// `pci_{set,get}_drvdata()` functions. + /// + /// Require that `Data` implements `ForeignOwnable`. We guarantee to + /// never move the underlying wrapped data structure. This allows + type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = (); + + /// The table of device ids supported by the driver. + const PCI_ID_TABLE: &'static [bindings::pci_device_id]; + + /// PCI driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device) -> Result<Self::Data>; + + /// PCI driver remove. + /// + /// Called when a platform device is removed. + /// Implementers should prepare the device for complete removal here. + fn remove(_data: &Self::Data); +} + +/// A PCI device. +/// +/// # Invariants +/// +/// The field `ptr` is non-null and valid for the lifetime of the object. +pub struct Device { + ptr: *mut bindings::pci_dev, +} + +impl Device { + unsafe fn from_ptr(ptr: *mut bindings::pci_dev) -> Self { + Self { ptr } + } +} + +unsafe impl device::RawDevice for Device { + fn raw_device(&self) -> *mut bindings::device { + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { &mut (*self.ptr).dev } + } +} |