| // SPDX-License-Identifier: GPL-2.0 | |
| //! Abstractions for [system | |
| //! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management). | |
| //! | |
| //! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h) | |
| use core::{ | |
| ops::Deref, | |
| ptr::NonNull, // | |
| }; | |
| use crate::{ | |
| prelude::*, | |
| str::CString, | |
| types::Opaque, // | |
| }; | |
| pub use super::{ | |
| PhysAddr, | |
| ResourceSize, // | |
| }; | |
| /// A region allocated from a parent [`Resource`]. | |
| /// | |
| /// # Invariants | |
| /// | |
| /// - `self.0` points to a valid `bindings::resource` that was obtained through | |
| /// `bindings::__request_region`. | |
| pub struct Region { | |
| /// The resource returned when the region was requested. | |
| resource: NonNull<bindings::resource>, | |
| /// The name that was passed in when the region was requested. We need to | |
| /// store it for ownership reasons. | |
| _name: CString, | |
| } | |
| impl Deref for Region { | |
| type Target = Resource; | |
| fn deref(&self) -> &Self::Target { | |
| // SAFETY: Safe as per the invariant of `Region`. | |
| unsafe { Resource::from_raw(self.resource.as_ptr()) } | |
| } | |
| } | |
| impl Drop for Region { | |
| fn drop(&mut self) { | |
| let (flags, start, size) = { | |
| let res = &**self; | |
| (res.flags(), res.start(), res.size()) | |
| }; | |
| let release_fn = if flags.contains(Flags::IORESOURCE_MEM) { | |
| bindings::release_mem_region | |
| } else { | |
| bindings::release_region | |
| }; | |
| // SAFETY: Safe as per the invariant of `Region`. | |
| unsafe { release_fn(start, size) }; | |
| } | |
| } | |
| // SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from | |
| // any thread. | |
| unsafe impl Send for Region {} | |
| // SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are | |
| // safe to be used from any thread. | |
| unsafe impl Sync for Region {} | |
| /// A resource abstraction. | |
| /// | |
| /// # Invariants | |
| /// | |
| /// [`Resource`] is a transparent wrapper around a valid `bindings::resource`. | |
| #[repr(transparent)] | |
| pub struct Resource(Opaque<bindings::resource>); | |
| impl Resource { | |
| /// Creates a reference to a [`Resource`] from a valid pointer. | |
| /// | |
| /// # Safety | |
| /// | |
| /// The caller must ensure that for the duration of 'a, the pointer will | |
| /// point at a valid `bindings::resource`. | |
| /// | |
| /// The caller must also ensure that the [`Resource`] is only accessed via the | |
| /// returned reference for the duration of 'a. | |
| pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self { | |
| // SAFETY: Self is a transparent wrapper around `Opaque<bindings::resource>`. | |
| unsafe { &*ptr.cast() } | |
| } | |
| /// Requests a resource region. | |
| /// | |
| /// Exclusive access will be given and the region will be marked as busy. | |
| /// Further calls to [`Self::request_region`] will return [`None`] if | |
| /// the region, or a part of it, is already in use. | |
| pub fn request_region( | |
| &self, | |
| start: PhysAddr, | |
| size: ResourceSize, | |
| name: CString, | |
| flags: Flags, | |
| ) -> Option<Region> { | |
| // SAFETY: | |
| // - Safe as per the invariant of `Resource`. | |
| // - `__request_region` will store a reference to the name, but that is | |
| // safe as we own it and it will not be dropped until the `Region` is | |
| // dropped. | |
| let region = unsafe { | |
| bindings::__request_region( | |
| self.0.get(), | |
| start, | |
| size, | |
| name.as_char_ptr(), | |
| flags.0 as c_int, | |
| ) | |
| }; | |
| Some(Region { | |
| resource: NonNull::new(region)?, | |
| _name: name, | |
| }) | |
| } | |
| /// Returns the size of the resource. | |
| pub fn size(&self) -> ResourceSize { | |
| let inner = self.0.get(); | |
| // SAFETY: Safe as per the invariants of `Resource`. | |
| unsafe { bindings::resource_size(inner) } | |
| } | |
| /// Returns the start address of the resource. | |
| pub fn start(&self) -> PhysAddr { | |
| let inner = self.0.get(); | |
| // SAFETY: Safe as per the invariants of `Resource`. | |
| unsafe { (*inner).start } | |
| } | |
| /// Returns the name of the resource. | |
| pub fn name(&self) -> Option<&CStr> { | |
| let inner = self.0.get(); | |
| // SAFETY: Safe as per the invariants of `Resource`. | |
| let name = unsafe { (*inner).name }; | |
| if name.is_null() { | |
| return None; | |
| } | |
| // SAFETY: In the C code, `resource::name` either contains a null | |
| // pointer or points to a valid NUL-terminated C string, and at this | |
| // point we know it is not null, so we can safely convert it to a | |
| // `CStr`. | |
| Some(unsafe { CStr::from_char_ptr(name) }) | |
| } | |
| /// Returns the flags associated with the resource. | |
| pub fn flags(&self) -> Flags { | |
| let inner = self.0.get(); | |
| // SAFETY: Safe as per the invariants of `Resource`. | |
| let flags = unsafe { (*inner).flags }; | |
| Flags(flags) | |
| } | |
| } | |
| // SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is | |
| // safe to be used from any thread. | |
| unsafe impl Send for Resource {} | |
| // SAFETY: `Resource` only holds a pointer to a C `struct resource`, references | |
| // to which are safe to be used from any thread. | |
| unsafe impl Sync for Resource {} | |
| /// Resource flags as stored in the C `struct resource::flags` field. | |
| /// | |
| /// They can be combined with the operators `|`, `&`, and `!`. | |
| /// | |
| /// Values can be used from the associated constants such as | |
| /// [`Flags::IORESOURCE_IO`]. | |
| #[derive(Clone, Copy, PartialEq)] | |
| pub struct Flags(c_ulong); | |
| impl Flags { | |
| /// Check whether `flags` is contained in `self`. | |
| pub fn contains(self, flags: Flags) -> bool { | |
| (self & flags) == flags | |
| } | |
| } | |
| impl core::ops::BitOr for Flags { | |
| type Output = Self; | |
| fn bitor(self, rhs: Self) -> Self::Output { | |
| Self(self.0 | rhs.0) | |
| } | |
| } | |
| impl core::ops::BitAnd for Flags { | |
| type Output = Self; | |
| fn bitand(self, rhs: Self) -> Self::Output { | |
| Self(self.0 & rhs.0) | |
| } | |
| } | |
| impl core::ops::Not for Flags { | |
| type Output = Self; | |
| fn not(self) -> Self::Output { | |
| Self(!self.0) | |
| } | |
| } | |
| impl Flags { | |
| /// PCI/ISA I/O ports. | |
| pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO); | |
| /// Resource is software muxed. | |
| pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED); | |
| /// Resource represents a memory region. | |
| pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM); | |
| /// Resource represents a memory region that must be ioremaped using `ioremap_np`. | |
| pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); | |
| // Always inline to optimize out error path of `build_assert`. | |
| #[inline(always)] | |
| const fn new(value: u32) -> Self { | |
| build_assert!(value as u64 <= c_ulong::MAX as u64); | |
| Flags(value as c_ulong) | |
| } | |
| } |