mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 08:44:29 -04:00
Pull Rust updates from Miguel Ojeda:
"Toolchain and infrastructure:
- Warn when using 'bindgen' < 0.72.1 with 'libclang' >= 22, since
that combination may fail to build. It includes a probe for the bug
in case 'bindgen' happens to be patched, and tests
In parallel, Nathan updated the instructions for the kernel.org
LLVM+Rust toolchains so that the latest version of 'bindgen' is
installed, which should avoid some of these situations
- Support testing 'rust_is_available.sh' with 'bash' as '/bin/sh'
- Fix an objtool warning by adding one more 'noreturn' function for
Rust 1.99.0 (expected 2026-10-01)
- Fix build error in the 'rusttest' target due to ambiguity when the
'rustc-dev' component is installed, which was uncovered by the work
to support Rust's GCC backend ('rustc_codegen_gcc')
- Fix future Clang warnings in the upcoming powerpc support due to
macro redefinitions in the UAPI helper header by including the
arch-aware 'ioctl.h' header
'kernel' crate:
- Rework module ownership support:
- Move the module-related types into a new 'module' module and
make the 'THIS_MODULE' pointer a constant of 'ModuleMetadata'
so that modules can provide the pointer in const contexts, and
add a 'this_module' 'const fn' to retrieve it
This was enabled by upstream Rust's work on the 'const_mut_refs'
and 'const_refs_to_static' features which were stabilized back
in Rust 1.83.0
- Teach '#[vtable]' to associate implementations with their
owning module, defaulting to the local one, including fallbacks
for doctests, uses within the 'kernel' crate (like upcoming
KUnit '#[test]'s for DRM) and 'rusttest'
- Set 'fops.owner' from the module pointer for DRM and
miscdevice
- Migrate Rust Binder and configfs away from the old
'THIS_MODULE' 'static' and finally remove it from the 'module!'
macro
- 'num' module:
- Add the new 'casts' module for lossless integer conversions
Rust's 'core' library's 'From' implementations do not cover
conversions that are not portable or future-proof. However, the
kernel supports a narrower set of architectures, which makes it
helpful to provide more infallible conversions, instead of
having developers use 'as' casts, which carry the risk of
silently losing data
This goes along with previous work we did to avoid casts in
Rust kernel code since they are more powerful than needed
Thus, provide safe 'const' conversion functions (e.g.
'usize_as_u64' and 'u64_into_u8'), as well as the
'FromSafeCast' and 'IntoSafeCast' extension traits that provide
conversions that are known to be lossless in the kernel, and an
'arch' submodule defining conversions that are known to be
lossless on particular architectures (e.g. 64-bit platforms).
For instance:
// Conversion in const context.
const USIZED_CONST: usize = u8_as_usize(255u8);
// Non-const conversions.
let a = u64::from_safe_cast(4096usize);
let b: u64 = 4096usize.into_safe_cast();
- Add 'Bounded::shr_exact' method in the vein of 'try_shrink'
which shifts a bounded right only if it loses no set bits
- Fix unsoundness issue in the 'Bounded::shr' method by
rejecting, at compile-time, shifts of at least the type's bit
width
- 'fmt' module:
- Route '{:p}' raw pointer formatting through the kernel's hashed
'%p' format to prevent address leaks, including support for
width and padding. Include tests for both 'no_hash_pointers'
case and the default (hashed) one
- Fix the '{:p}' forwarding implementation, which could print the
address of a temporary stack variable
- 'time' module:
- Make 'Delta' generic over its time unit, with a default unit of
nanoseconds ('Nsec'), preserving the existing behavior. Then,
add a 'Jiffy' time unit
- Add the 'Delta::as_millis_ceil()' method
- Fix 'as_micros_ceil()' rounding near 'i64::MAX', which could
yield a result one microsecond too small
- 'sync' module:
- Implement 'ForeignOwnable' for 'ARef<T>', allowing C code to
own an 'ARef<T>'
- Add a safe abstraction for 'rcu_barrier()'
- 'error' module: add all of the remaining error codes, except the
deprecated compatibility aliases
- 'bug' module:
- Fix build error on UML in 'warn_on!' for callers from within
the 'kernel' crate
- Fix future 'dead_code' warning on arm and loongarch64 and under
'CONFIG_BUG=n' in 'warn_on!', which would trigger with the
upcoming SRCU abstractions
- Fix future build error in 'rusttest' on cross-compilation
cases, which would trigger when 'warn_on!' has callers inside
the 'kernel' crate
- 'bitfield' module: fix build error for the upcoming support for
Rust's GCC backend ('rustc_codegen_gcc') by always inlining a
couple conversions used in tests
'pin-init' crate:
- User-visible changes:
- Merge the '__pinned_init' and '__init' methods and make 'Init'
a marker trait
- Introduce public APIs 'raw_init' and 'raw_try_init' to prevent
users from needing to invoke the internal '__pinned_init' and
'__init' methods
- Emit errors for duplicate '#[pin]' attributes
- Link 'Zeroable::zeroed' and 'pin_init::zeroed' in documentation
- Other changes:
- Fix unwind safety issues
- Clean up lint 'allow' and 'expect's
- Overhaul '#[cfg]' handling to pave the way for tuple structs
and self-referential structs
- Mark many functions as '#[inline]' for better codegen with '-C
opt-level=s' ('CC_OPTIMIZE_FOR_SIZE')
'MAINTAINERS':
- Update 'MODULE SUPPORT' to cover the new 'module' module
And some other fixes, cleanups and improvements"
* tag 'rust-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux: (54 commits)
rust: add functions and traits for lossless integer conversions
rust: kernel: add `LocalModule` fallback for `#[vtable]` `impl`s
rust: fmt: route {:p} through HashedPtr to prevent address leaks
rust: fmt: fix {:p} printing stack addresses
rust: module: update MAINTAINERS to cover module.rs
rust: macros: remove `THIS_MODULE` static from `module!`
rust_binder: use `LocalModule` for `THIS_MODULE`
rust: configfs: use `LocalModule` for `THIS_MODULE`
rust: miscdevice: set fops.owner from driver module pointer
rust: drm: set fops.owner from driver module pointer
rust: macros: auto-insert OwnerModule in #[vtable]
rust: doctest: add LocalModule fallback for #[vtable] ThisModule
rust: module: add `THIS_MODULE` const to `ModuleMetadata` trait
rust: module: move module types into `module.rs`
rust: num: add Bounded::shr_exact
rust: num: reject Bounded::shr overshifts at build time
rust: num: use const_assert! in Bounded
rust: uapi: replace direct asm-generic/ioctl.h include with linux/ioctl.h
rust: time: add Delta::as_millis_ceil()
rust: time: add jiffies time unit for Delta
...
575 lines
19 KiB
Rust
575 lines
19 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Abstractions for the platform bus.
|
|
//!
|
|
//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
|
|
|
|
use crate::{
|
|
acpi,
|
|
bindings,
|
|
container_of,
|
|
device::{
|
|
self,
|
|
Bound, //
|
|
},
|
|
driver,
|
|
error::{
|
|
from_result,
|
|
to_result, //
|
|
},
|
|
io::Resource,
|
|
irq::{
|
|
self,
|
|
IrqRequest, //
|
|
},
|
|
of,
|
|
prelude::*,
|
|
types::Opaque,
|
|
ThisModule, //
|
|
};
|
|
|
|
#[cfg(CONFIG_HAS_IOMEM)]
|
|
use crate::io::mem::IoRequest;
|
|
|
|
use core::{
|
|
marker::PhantomData,
|
|
mem::offset_of,
|
|
ptr::{
|
|
addr_of_mut,
|
|
NonNull, //
|
|
},
|
|
};
|
|
|
|
/// An adapter for the registration of platform drivers.
|
|
pub struct Adapter<T: Driver>(T);
|
|
|
|
// SAFETY:
|
|
// - `bindings::platform_driver` is a C type declared as `repr(C)`.
|
|
// - `T::Data` is the type of the driver's device private data.
|
|
// - `struct platform_driver` embeds a `struct device_driver`.
|
|
// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
|
|
unsafe impl<T: Driver> driver::DriverLayout for Adapter<T> {
|
|
type DriverType = bindings::platform_driver;
|
|
type DriverData<'bound> = T::Data<'bound>;
|
|
const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
|
|
}
|
|
|
|
// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
|
|
// a preceding call to `register` has been successful.
|
|
unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
|
|
unsafe fn register(
|
|
pdrv: &Opaque<Self::DriverType>,
|
|
name: &'static CStr,
|
|
module: &'static ThisModule,
|
|
) -> Result {
|
|
let of_table = match T::OF_ID_TABLE {
|
|
Some(table) => table.as_ptr(),
|
|
None => core::ptr::null(),
|
|
};
|
|
|
|
let acpi_table = match T::ACPI_ID_TABLE {
|
|
Some(table) => table.as_ptr(),
|
|
None => core::ptr::null(),
|
|
};
|
|
|
|
// SAFETY: It's safe to set the fields of `struct platform_driver` on initialization.
|
|
unsafe {
|
|
(*pdrv.get()).driver.name = name.as_char_ptr();
|
|
(*pdrv.get()).probe = Some(Self::probe_callback);
|
|
(*pdrv.get()).remove = Some(Self::remove_callback);
|
|
(*pdrv.get()).driver.of_match_table = of_table;
|
|
(*pdrv.get()).driver.acpi_match_table = acpi_table;
|
|
}
|
|
|
|
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
|
|
to_result(unsafe {
|
|
bindings::__platform_driver_register(pdrv.get(), module.as_ptr(), name.as_char_ptr())
|
|
})
|
|
}
|
|
|
|
unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
|
|
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
|
|
unsafe { bindings::platform_driver_unregister(pdrv.get()) };
|
|
}
|
|
}
|
|
|
|
impl<T: Driver> Adapter<T> {
|
|
extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int {
|
|
// SAFETY: The platform bus only ever calls the probe callback with a valid pointer to a
|
|
// `struct platform_device`.
|
|
//
|
|
// INVARIANT: `pdev` is valid for the duration of `probe_callback()`.
|
|
let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal<'_>>>() };
|
|
let info = <Self as driver::Adapter>::id_info(pdev.as_ref());
|
|
|
|
from_result(|| {
|
|
let data = T::probe(pdev, info);
|
|
|
|
pdev.as_ref().set_drvdata(data)?;
|
|
Ok(0)
|
|
})
|
|
}
|
|
|
|
extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
|
|
// SAFETY: The platform bus only ever calls the remove callback with a valid pointer to a
|
|
// `struct platform_device`.
|
|
//
|
|
// INVARIANT: `pdev` is valid for the duration of `remove_callback()`.
|
|
let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal<'_>>>() };
|
|
|
|
// SAFETY: `remove_callback` is only ever called after a successful call to
|
|
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
|
|
// and stored a `Pin<KBox<T::Data<'_>>>`.
|
|
let data = unsafe { pdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
|
|
|
|
T::unbind(pdev, data);
|
|
}
|
|
}
|
|
|
|
impl<T: Driver> driver::Adapter for Adapter<T> {
|
|
type IdInfo = T::IdInfo;
|
|
|
|
fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
|
|
T::OF_ID_TABLE
|
|
}
|
|
|
|
fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
|
|
T::ACPI_ID_TABLE
|
|
}
|
|
}
|
|
|
|
/// Declares a kernel module that exposes a single platform driver.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```ignore
|
|
/// kernel::module_platform_driver! {
|
|
/// type: MyDriver,
|
|
/// name: "Module name",
|
|
/// authors: ["Author name"],
|
|
/// description: "Description",
|
|
/// license: "GPL v2",
|
|
/// }
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! module_platform_driver {
|
|
($($f:tt)*) => {
|
|
$crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
|
|
};
|
|
}
|
|
|
|
/// The platform driver trait.
|
|
///
|
|
/// Drivers must implement this trait in order to get a platform driver registered.
|
|
///
|
|
/// # Examples
|
|
///
|
|
///```
|
|
/// # use kernel::{
|
|
/// # acpi,
|
|
/// # bindings,
|
|
/// # device::Core,
|
|
/// # of,
|
|
/// # platform,
|
|
/// # };
|
|
/// struct MyDriver;
|
|
///
|
|
/// kernel::of_device_table!(
|
|
/// OF_TABLE,
|
|
/// MODULE_OF_TABLE,
|
|
/// <MyDriver as platform::Driver>::IdInfo,
|
|
/// [
|
|
/// (of::DeviceId::new(c"test,device"), ())
|
|
/// ]
|
|
/// );
|
|
///
|
|
/// kernel::acpi_device_table!(
|
|
/// ACPI_TABLE,
|
|
/// MODULE_ACPI_TABLE,
|
|
/// <MyDriver as platform::Driver>::IdInfo,
|
|
/// [
|
|
/// (acpi::DeviceId::new(c"LNUXBEEF"), ())
|
|
/// ]
|
|
/// );
|
|
///
|
|
/// impl platform::Driver for MyDriver {
|
|
/// type IdInfo = ();
|
|
/// type Data<'bound> = Self;
|
|
/// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
|
|
/// const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
|
|
///
|
|
/// fn probe<'bound>(
|
|
/// _pdev: &'bound platform::Device<Core<'_>>,
|
|
/// _id_info: Option<&'bound Self::IdInfo>,
|
|
/// ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
|
|
/// Err(ENODEV)
|
|
/// }
|
|
/// }
|
|
///```
|
|
pub trait Driver {
|
|
/// The type holding driver private data about each device id supported by the driver.
|
|
// TODO: Use associated_type_defaults once stabilized:
|
|
//
|
|
// ```
|
|
// type IdInfo: 'static = ();
|
|
// ```
|
|
type IdInfo: 'static;
|
|
|
|
/// The type of the driver's bus device private data.
|
|
type Data<'bound>: Send + 'bound;
|
|
|
|
/// The table of OF device ids supported by the driver.
|
|
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
|
|
|
|
/// The table of ACPI device ids supported by the driver.
|
|
const ACPI_ID_TABLE: Option<acpi::IdTable<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<'bound>(
|
|
dev: &'bound Device<device::Core<'_>>,
|
|
id_info: Option<&'bound Self::IdInfo>,
|
|
) -> impl PinInit<Self::Data<'bound>, Error> + 'bound;
|
|
|
|
/// Platform driver unbind.
|
|
///
|
|
/// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
|
|
/// is optional.
|
|
///
|
|
/// This callback serves as a place for drivers to perform teardown operations that require a
|
|
/// `&Device<Core>` or `&Device<Bound>` reference. For instance, drivers may try to perform I/O
|
|
/// operations to gracefully tear down the device.
|
|
///
|
|
/// Otherwise, release operations for driver resources should be performed in `Drop`.
|
|
fn unbind<'bound>(dev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
|
|
let _ = (dev, this);
|
|
}
|
|
}
|
|
|
|
/// The platform device representation.
|
|
///
|
|
/// This structure represents the Rust abstraction for a C `struct platform_device`. The
|
|
/// implementation abstracts the usage of an already existing C `struct platform_device` within Rust
|
|
/// code that we get passed from the C side.
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// A [`Device`] instance represents a valid `struct platform_device` created by the C portion of
|
|
/// the kernel.
|
|
#[repr(transparent)]
|
|
pub struct Device<Ctx: device::DeviceContext = device::Normal>(
|
|
Opaque<bindings::platform_device>,
|
|
PhantomData<Ctx>,
|
|
);
|
|
|
|
impl<Ctx: device::DeviceContext> Device<Ctx> {
|
|
fn as_raw(&self) -> *mut bindings::platform_device {
|
|
self.0.get()
|
|
}
|
|
|
|
/// Returns the resource at `index`, if any.
|
|
pub fn resource_by_index(&self, index: u32) -> Option<&Resource> {
|
|
// SAFETY: `self.as_raw()` returns a valid pointer to a `struct platform_device`.
|
|
let resource = unsafe {
|
|
bindings::platform_get_resource(self.as_raw(), bindings::IORESOURCE_MEM, index)
|
|
};
|
|
|
|
if resource.is_null() {
|
|
return None;
|
|
}
|
|
|
|
// SAFETY: `resource` is a valid pointer to a `struct resource` as
|
|
// returned by `platform_get_resource`.
|
|
Some(unsafe { Resource::from_raw(resource) })
|
|
}
|
|
|
|
/// Returns the resource with a given `name`, if any.
|
|
pub fn resource_by_name(&self, name: &CStr) -> Option<&Resource> {
|
|
// SAFETY: `self.as_raw()` returns a valid pointer to a `struct
|
|
// platform_device` and `name` points to a valid C string.
|
|
let resource = unsafe {
|
|
bindings::platform_get_resource_byname(
|
|
self.as_raw(),
|
|
bindings::IORESOURCE_MEM,
|
|
name.as_char_ptr(),
|
|
)
|
|
};
|
|
|
|
if resource.is_null() {
|
|
return None;
|
|
}
|
|
|
|
// SAFETY: `resource` is a valid pointer to a `struct resource` as
|
|
// returned by `platform_get_resource`.
|
|
Some(unsafe { Resource::from_raw(resource) })
|
|
}
|
|
}
|
|
|
|
#[cfg(CONFIG_HAS_IOMEM)]
|
|
impl Device<Bound> {
|
|
/// Returns an `IoRequest` for the resource at `index`, if any.
|
|
pub fn io_request_by_index(&self, index: u32) -> Option<IoRequest<'_>> {
|
|
self.resource_by_index(index)
|
|
// SAFETY: `resource` is a valid resource for `&self` during the
|
|
// lifetime of the `IoRequest`.
|
|
.map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) })
|
|
}
|
|
|
|
/// Returns an `IoRequest` for the resource with a given `name`, if any.
|
|
pub fn io_request_by_name(&self, name: &CStr) -> Option<IoRequest<'_>> {
|
|
self.resource_by_name(name)
|
|
// SAFETY: `resource` is a valid resource for `&self` during the
|
|
// lifetime of the `IoRequest`.
|
|
.map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) })
|
|
}
|
|
}
|
|
|
|
// SAFETY: `platform::Device` is a transparent wrapper of `struct platform_device`.
|
|
// The offset is guaranteed to point to a valid device field inside `platform::Device`.
|
|
unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
|
|
const OFFSET: usize = offset_of!(bindings::platform_device, dev);
|
|
}
|
|
|
|
macro_rules! define_irq_accessor_by_index {
|
|
(
|
|
$(#[$meta:meta])* $fn_name:ident,
|
|
$request_fn:ident,
|
|
$reg_type:ident,
|
|
$handler_trait:ident
|
|
) => {
|
|
$(#[$meta])*
|
|
pub fn $fn_name<'a, T: irq::$handler_trait + 'static>(
|
|
&'a self,
|
|
flags: irq::Flags,
|
|
index: u32,
|
|
name: &'static CStr,
|
|
handler: impl PinInit<T, Error> + 'a,
|
|
) -> impl PinInit<irq::$reg_type<T>, Error> + 'a {
|
|
pin_init::pin_init_scope(move || {
|
|
let request = self.$request_fn(index)?;
|
|
|
|
Ok(irq::$reg_type::<T>::new(
|
|
request,
|
|
flags,
|
|
name,
|
|
handler,
|
|
))
|
|
})
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! define_irq_accessor_by_name {
|
|
(
|
|
$(#[$meta:meta])* $fn_name:ident,
|
|
$request_fn:ident,
|
|
$reg_type:ident,
|
|
$handler_trait:ident
|
|
) => {
|
|
$(#[$meta])*
|
|
pub fn $fn_name<'a, T: irq::$handler_trait + 'static>(
|
|
&'a self,
|
|
flags: irq::Flags,
|
|
irq_name: &'a CStr,
|
|
name: &'static CStr,
|
|
handler: impl PinInit<T, Error> + 'a,
|
|
) -> impl PinInit<irq::$reg_type<T>, Error> + 'a {
|
|
pin_init::pin_init_scope(move || {
|
|
let request = self.$request_fn(irq_name)?;
|
|
|
|
Ok(irq::$reg_type::<T>::new(
|
|
request,
|
|
flags,
|
|
name,
|
|
handler,
|
|
))
|
|
})
|
|
}
|
|
};
|
|
}
|
|
|
|
impl Device<Bound> {
|
|
/// Returns an [`IrqRequest`] for the IRQ at the given index, if any.
|
|
pub fn irq_by_index(&self, index: u32) -> Result<IrqRequest<'_>> {
|
|
// SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
|
|
let irq = unsafe { bindings::platform_get_irq(self.as_raw(), index) };
|
|
|
|
if irq < 0 {
|
|
return Err(Error::from_errno(irq));
|
|
}
|
|
|
|
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
|
Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
|
|
}
|
|
|
|
/// Returns an [`IrqRequest`] for the IRQ at the given index, but does not
|
|
/// print an error if the IRQ cannot be obtained.
|
|
pub fn optional_irq_by_index(&self, index: u32) -> Result<IrqRequest<'_>> {
|
|
// SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
|
|
let irq = unsafe { bindings::platform_get_irq_optional(self.as_raw(), index) };
|
|
|
|
if irq < 0 {
|
|
return Err(Error::from_errno(irq));
|
|
}
|
|
|
|
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
|
Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
|
|
}
|
|
|
|
/// Returns an [`IrqRequest`] for the IRQ with the given name, if any.
|
|
pub fn irq_by_name(&self, name: &CStr) -> Result<IrqRequest<'_>> {
|
|
// SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
|
|
let irq = unsafe { bindings::platform_get_irq_byname(self.as_raw(), name.as_char_ptr()) };
|
|
|
|
if irq < 0 {
|
|
return Err(Error::from_errno(irq));
|
|
}
|
|
|
|
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
|
Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
|
|
}
|
|
|
|
/// Returns an [`IrqRequest`] for the IRQ with the given name, but does not
|
|
/// print an error if the IRQ cannot be obtained.
|
|
pub fn optional_irq_by_name(&self, name: &CStr) -> Result<IrqRequest<'_>> {
|
|
// SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
|
|
let irq = unsafe {
|
|
bindings::platform_get_irq_byname_optional(self.as_raw(), name.as_char_ptr())
|
|
};
|
|
|
|
if irq < 0 {
|
|
return Err(Error::from_errno(irq));
|
|
}
|
|
|
|
// SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
|
|
Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
|
|
}
|
|
|
|
define_irq_accessor_by_index!(
|
|
/// Returns a [`irq::Registration`] for the IRQ at the given index.
|
|
request_irq_by_index,
|
|
irq_by_index,
|
|
Registration,
|
|
Handler
|
|
);
|
|
define_irq_accessor_by_name!(
|
|
/// Returns a [`irq::Registration`] for the IRQ with the given name.
|
|
request_irq_by_name,
|
|
irq_by_name,
|
|
Registration,
|
|
Handler
|
|
);
|
|
define_irq_accessor_by_index!(
|
|
/// Does the same as [`Self::request_irq_by_index`], except that it does
|
|
/// not print an error message if the IRQ cannot be obtained.
|
|
request_optional_irq_by_index,
|
|
optional_irq_by_index,
|
|
Registration,
|
|
Handler
|
|
);
|
|
define_irq_accessor_by_name!(
|
|
/// Does the same as [`Self::request_irq_by_name`], except that it does
|
|
/// not print an error message if the IRQ cannot be obtained.
|
|
request_optional_irq_by_name,
|
|
optional_irq_by_name,
|
|
Registration,
|
|
Handler
|
|
);
|
|
|
|
define_irq_accessor_by_index!(
|
|
/// Returns a [`irq::ThreadedRegistration`] for the IRQ at the given index.
|
|
request_threaded_irq_by_index,
|
|
irq_by_index,
|
|
ThreadedRegistration,
|
|
ThreadedHandler
|
|
);
|
|
define_irq_accessor_by_name!(
|
|
/// Returns a [`irq::ThreadedRegistration`] for the IRQ with the given name.
|
|
request_threaded_irq_by_name,
|
|
irq_by_name,
|
|
ThreadedRegistration,
|
|
ThreadedHandler
|
|
);
|
|
define_irq_accessor_by_index!(
|
|
/// Does the same as [`Self::request_threaded_irq_by_index`], except
|
|
/// that it does not print an error message if the IRQ cannot be
|
|
/// obtained.
|
|
request_optional_threaded_irq_by_index,
|
|
optional_irq_by_index,
|
|
ThreadedRegistration,
|
|
ThreadedHandler
|
|
);
|
|
define_irq_accessor_by_name!(
|
|
/// Does the same as [`Self::request_threaded_irq_by_name`], except that
|
|
/// it does not print an error message if the IRQ cannot be obtained.
|
|
request_optional_threaded_irq_by_name,
|
|
optional_irq_by_name,
|
|
ThreadedRegistration,
|
|
ThreadedHandler
|
|
);
|
|
}
|
|
|
|
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
|
// argument.
|
|
kernel::impl_device_context_deref!(unsafe { Device });
|
|
kernel::impl_device_context_into_aref!(Device);
|
|
|
|
impl<'a> crate::dma::Device<'a> for Device<device::Core<'a>> {}
|
|
|
|
// SAFETY: Instances of `Device` are always reference-counted.
|
|
unsafe impl crate::sync::aref::AlwaysRefCounted for Device {
|
|
fn inc_ref(&self) {
|
|
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
|
unsafe { bindings::get_device(self.as_ref().as_raw()) };
|
|
}
|
|
|
|
unsafe fn dec_ref(obj: NonNull<Self>) {
|
|
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
|
unsafe { bindings::platform_device_put(obj.cast().as_ptr()) }
|
|
}
|
|
}
|
|
|
|
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
|
|
fn as_ref(&self) -> &device::Device<Ctx> {
|
|
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
|
|
// `struct platform_device`.
|
|
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
|
|
|
|
// SAFETY: `dev` points to a valid `struct device`.
|
|
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> {
|
|
// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
|
|
// `struct device`.
|
|
if !unsafe { bindings::dev_is_platform(dev.as_raw()) } {
|
|
return Err(EINVAL);
|
|
}
|
|
|
|
// SAFETY: We've just verified that the bus type of `dev` equals
|
|
// `bindings::platform_bus_type`, hence `dev` must be embedded in a valid
|
|
// `struct platform_device` as guaranteed by the corresponding C code.
|
|
let pdev = unsafe { container_of!(dev.as_raw(), bindings::platform_device, dev) };
|
|
|
|
// SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
|
|
Ok(unsafe { &*pdev.cast() })
|
|
}
|
|
}
|
|
|
|
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
|
|
unsafe impl Send for Device {}
|
|
|
|
// SAFETY: `Device` can be shared among threads because all methods of `Device`
|
|
// (i.e. `Device<Normal>) are thread safe.
|
|
unsafe impl Sync for Device {}
|
|
|
|
// SAFETY: Same as `Device<Normal>` -- the underlying `struct platform_device` is the same;
|
|
// `Bound` is a zero-sized type-state marker that does not affect thread safety.
|
|
unsafe impl Sync for Device<device::Bound> {}
|