Skip to content

Commit 6062ddc

Browse files
fujitametaspace
authored andcommitted
rust: add initial PCI support
Added minimum abstration APIs for PCI. --- Original-author: FUJITA Tomonori <[email protected]> Modified to apply to this tree.
1 parent 2190bba commit 6062ddc

File tree

5 files changed

+128
-0
lines changed

5 files changed

+128
-0
lines changed

rust/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ RUST_HELPERS := \
1414
err \
1515
kunit \
1616
mutex \
17+
pci \
1718
rcu \
1819
refcount \
1920
signal \

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/ethtool.h>
1313
#include <linux/jiffies.h>
1414
#include <linux/mdio.h>
15+
#include <linux/pci.h>
1516
#include <linux/phy.h>
1617
#include <linux/refcount.h>
1718
#include <linux/sched.h>

rust/helpers/pci.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/pci.h>
4+
5+
void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
6+
{
7+
pci_set_drvdata(pdev, data);
8+
}
9+
EXPORT_SYMBOL_GPL(rust_helper_pci_set_drvdata);
10+
11+
void *rust_helper_pci_get_drvdata(struct pci_dev *pdev)
12+
{
13+
return pci_get_drvdata(pdev);
14+
}
15+
EXPORT_SYMBOL_GPL(rust_helper_pci_get_drvdata);

rust/kernel/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
#![no_std]
1515
#![feature(allocator_api)]
16+
#![feature(associated_type_defaults)]
1617
#![feature(coerce_unsized)]
1718
#![feature(const_refs_to_cell)]
1819
#![feature(dispatch_from_dyn)]
@@ -58,6 +59,8 @@ pub mod workqueue;
5859
#[doc(hidden)]
5960
pub use bindings;
6061
pub use macros;
62+
#[cfg(CONFIG_PCI)]
63+
pub mod pci;
6164
pub use uapi;
6265

6366
#[doc(hidden)]

rust/kernel/pci.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! PCI devices and drivers.
4+
//!
5+
//! C header: [`include/linux/pci.h`](../../../../include/linux/pci.h)
6+
7+
use crate::{
8+
bindings, device, driver,
9+
error::{from_result, to_result, Result},
10+
str::CStr,
11+
types::ForeignOwnable,
12+
ThisModule,
13+
};
14+
15+
/// An adapter for the registration of PCI drivers.
16+
pub struct Adapter<T: Driver>(T);
17+
18+
impl<T: Driver> driver::DriverOps for Adapter<T> {
19+
type RegType = bindings::pci_driver;
20+
21+
unsafe fn register(
22+
reg: *mut bindings::pci_driver,
23+
name: &'static CStr,
24+
module: &'static ThisModule,
25+
) -> Result {
26+
let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg };
27+
28+
pdrv.name = name.as_char_ptr();
29+
pdrv.probe = Some(Self::probe_callback);
30+
pdrv.remove = Some(Self::remove_callback);
31+
pdrv.id_table = T::PCI_ID_TABLE.as_ptr();
32+
to_result(unsafe { bindings::__pci_register_driver(reg, module.0, name.as_char_ptr()) })
33+
}
34+
35+
unsafe fn unregister(reg: *mut bindings::pci_driver) {
36+
unsafe { bindings::pci_unregister_driver(reg) }
37+
}
38+
}
39+
40+
impl<T: Driver> Adapter<T> {
41+
extern "C" fn probe_callback(
42+
pdev: *mut bindings::pci_dev,
43+
_id: *const bindings::pci_device_id,
44+
) -> core::ffi::c_int {
45+
from_result(|| {
46+
let mut dev = unsafe { Device::from_ptr(pdev) };
47+
let data = T::probe(&mut dev)?;
48+
unsafe { bindings::pci_set_drvdata(pdev, data.into_foreign() as _) };
49+
Ok(0)
50+
})
51+
}
52+
53+
extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
54+
let ptr = unsafe { bindings::pci_get_drvdata(pdev) };
55+
let data = unsafe { T::Data::from_foreign(ptr) };
56+
T::remove(&data);
57+
<T::Data as driver::DeviceRemoval>::device_remove(&data);
58+
}
59+
}
60+
61+
/// A PCI driver
62+
pub trait Driver {
63+
/// Data stored on device by driver.
64+
///
65+
/// Corresponds to the data set or retrieved via the kernel's
66+
/// `pci_{set,get}_drvdata()` functions.
67+
///
68+
/// Require that `Data` implements `ForeignOwnable`. We guarantee to
69+
/// never move the underlying wrapped data structure. This allows
70+
type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = ();
71+
72+
/// The table of device ids supported by the driver.
73+
const PCI_ID_TABLE: &'static [bindings::pci_device_id];
74+
75+
/// PCI driver probe.
76+
///
77+
/// Called when a new platform device is added or discovered.
78+
/// Implementers should attempt to initialize the device here.
79+
fn probe(dev: &mut Device) -> Result<Self::Data>;
80+
81+
/// PCI driver remove.
82+
///
83+
/// Called when a platform device is removed.
84+
/// Implementers should prepare the device for complete removal here.
85+
fn remove(_data: &Self::Data);
86+
}
87+
88+
/// A PCI device.
89+
///
90+
/// # Invariants
91+
///
92+
/// The field `ptr` is non-null and valid for the lifetime of the object.
93+
pub struct Device {
94+
ptr: *mut bindings::pci_dev,
95+
}
96+
97+
impl Device {
98+
unsafe fn from_ptr(ptr: *mut bindings::pci_dev) -> Self {
99+
Self { ptr }
100+
}
101+
}
102+
103+
unsafe impl device::RawDevice for Device {
104+
fn raw_device(&self) -> *mut bindings::device {
105+
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
106+
unsafe { &mut (*self.ptr).dev }
107+
}
108+
}

0 commit comments

Comments
 (0)