1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
// SPDX-License-Identifier: GPL-2.0
//! Platform devices and drivers.
//!
//! Also called `platdev`, `pdev`.
//!
//! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h)
use crate::{
bindings,
device::{self, RawDevice},
driver,
error::{from_kernel_result, Result},
of,
str::CStr,
to_result,
types::ForeignOwnable,
ThisModule,
};
/// A registration of a platform driver.
pub type Registration<T> = driver::Registration<Adapter<T>>;
/// An adapter for the registration of platform drivers.
pub struct Adapter<T: Driver>(T);
impl<T: Driver> driver::DriverOps for Adapter<T> {
type RegType = bindings::platform_driver;
unsafe fn register(
reg: *mut bindings::platform_driver,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
// SAFETY: By the safety requirements of this function (defined in the trait definition),
// `reg` is non-null and valid.
let pdrv = unsafe { &mut *reg };
pdrv.driver.name = name.as_char_ptr();
pdrv.probe = Some(Self::probe_callback);
pdrv.remove = Some(Self::remove_callback);
if let Some(t) = T::OF_DEVICE_ID_TABLE {
pdrv.driver.of_match_table = t.as_ref();
}
// SAFETY:
// - `pdrv` lives at least until the call to `platform_driver_unregister()` returns.
// - `name` pointer has static lifetime.
// - `module.0` lives at least as long as the module.
// - `probe()` and `remove()` are static functions.
// - `of_match_table` is either a raw pointer with static lifetime,
// as guaranteed by the [`driver::IdTable`] type, or null.
to_result(unsafe { bindings::__platform_driver_register(reg, module.0) })
}
unsafe fn unregister(reg: *mut bindings::platform_driver) {
// SAFETY: By the safety requirements of this function (defined in the trait definition),
// `reg` was passed (and updated) by a previous successful call to
// `platform_driver_register`.
unsafe { bindings::platform_driver_unregister(reg) };
}
}
impl<T: Driver> Adapter<T> {
fn get_id_info(dev: &Device) -> Option<&'static T::IdInfo> {
let table = T::OF_DEVICE_ID_TABLE?;
// SAFETY: `table` has static lifetime, so it is valid for read. `dev` is guaranteed to be
// valid while it's alive, so is the raw device returned by it.
let id = unsafe { bindings::of_match_device(table.as_ref(), dev.raw_device()) };
if id.is_null() {
return None;
}
// SAFETY: `id` is a pointer within the static table, so it's always valid.
let offset = unsafe { (*id).data };
if offset.is_null() {
return None;
}
// SAFETY: The offset comes from a previous call to `offset_from` in `IdArray::new`, which
// guarantees that the resulting pointer is within the table.
let ptr = unsafe {
id.cast::<u8>()
.offset(offset as _)
.cast::<Option<T::IdInfo>>()
};
// SAFETY: The id table has a static lifetime, so `ptr` is guaranteed to be valid for read.
#[allow(clippy::needless_borrow)]
unsafe {
(&*ptr).as_ref()
}
}
extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
from_kernel_result! {
// SAFETY: `pdev` is valid by the contract with the C code. `dev` is alive only for the
// duration of this call, so it is guaranteed to remain alive for the lifetime of
// `pdev`.
let mut dev = unsafe { Device::from_ptr(pdev) };
let info = Self::get_id_info(&dev);
let data = T::probe(&mut dev, info)?;
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
unsafe { bindings::platform_set_drvdata(pdev, data.into_foreign() as _) };
Ok(0)
}
}
extern "C" fn remove_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
from_kernel_result! {
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
// SAFETY:
// - we allocated this pointer using `T::Data::into_foreign`,
// so it is safe to turn back into a `T::Data`.
// - the allocation happened in `probe`, no-one freed the memory,
// `remove` is the canonical kernel location to free driver data. so OK
// to convert the pointer back to a Rust structure here.
let data = unsafe { T::Data::from_foreign(ptr) };
let ret = T::remove(&data);
<T::Data as driver::DeviceRemoval>::device_remove(&data);
ret?;
Ok(0)
}
}
}
/// A platform driver.
pub trait Driver {
/// Data stored on device by driver.
///
/// Corresponds to the data set or retrieved via the kernel's
/// `platform_{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 type holding information about each device id supported by the driver.
type IdInfo: 'static = ();
/// The table of device ids supported by the driver.
const OF_DEVICE_ID_TABLE: Option<driver::IdTable<'static, of::DeviceId, Self::IdInfo>> = None;
/// Platform 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, id_info: Option<&Self::IdInfo>) -> Result<Self::Data>;
/// Platform driver remove.
///
/// Called when a platform device is removed.
/// Implementers should prepare the device for complete removal here.
fn remove(_data: &Self::Data) -> Result {
Ok(())
}
}
/// A platform device.
///
/// # Invariants
///
/// The field `ptr` is non-null and valid for the lifetime of the object.
pub struct Device {
ptr: *mut bindings::platform_device,
}
impl Device {
/// Creates a new device from the given pointer.
///
/// # Safety
///
/// `ptr` must be non-null and valid. It must remain valid for the lifetime of the returned
/// instance.
unsafe fn from_ptr(ptr: *mut bindings::platform_device) -> Self {
// INVARIANT: The safety requirements of the function ensure the lifetime invariant.
Self { ptr }
}
/// Returns id of the platform device.
pub fn id(&self) -> i32 {
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
unsafe { (*self.ptr).id }
}
}
// SAFETY: The device returned by `raw_device` is the raw platform device.
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 }
}
}
/// Declares a kernel module that exposes a single platform driver.
///
/// # Examples
///
/// ```ignore
/// # use kernel::{platform, define_of_id_table, module_platform_driver};
/// #
/// struct MyDriver;
/// impl platform::Driver for MyDriver {
/// // [...]
/// # fn probe(_dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result {
/// # Ok(())
/// # }
/// # define_of_id_table! {(), [
/// # (of::DeviceId::Compatible(b"brcm,bcm2835-rng"), None),
/// # ]}
/// }
///
/// module_platform_driver! {
/// type: MyDriver,
/// name: "module_name",
/// author: "Author name",
/// license: "GPL",
/// }
/// ```
#[macro_export]
macro_rules! module_platform_driver {
($($f:tt)*) => {
$crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
};
}