summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@gmail.com>2022-08-05 05:27:24 +0000
committerDanilo Krummrich <dakr@redhat.com>2023-11-22 22:00:54 +0100
commitb517bc329cb6775323b44413afbf804f3ed40320 (patch)
tree7255d94a81638673a9362578d8e3082b18fc8c70
parent24e0ea9eeb1a5a5983fae0a6a468665b1e05c04d (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.h5
-rw-r--r--rust/helpers.c13
-rw-r--r--rust/kernel/lib.rs3
-rw-r--r--rust/kernel/pci.rs108
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 }
+ }
+}