These accessors can be used to retrieve a irq::Registration and irq::ThreadedRegistration from a platform device by index or name. Alternatively, drivers can retrieve an IrqRequest from a bound platform device for later use. These accessors ensure that only valid IRQ lines can ever be registered. Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx> --- rust/kernel/platform.rs | 143 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 5b21fa517e55348582622ec10471918919502959..bfcb4962dda7ec420dc86dfa04311be0462ad9f2 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,8 +5,11 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) use crate::{ - bindings, container_of, device, driver, + bindings, container_of, + device::{self, Bound}, + driver, error::{to_result, Result}, + irq::{self, request::IrqRequest}, of, prelude::*, str::CStr, @@ -190,6 +193,144 @@ fn as_raw(&self) -> *mut bindings::platform_device { } } +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<T: irq::$handler_trait + 'static>( + &self, + flags: irq::flags::Flags, + index: u32, + name: &'static CStr, + handler: T, + ) -> Result<impl PinInit<irq::$reg_type<T>, Error> + '_> { + 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<T: irq::$handler_trait + 'static>( + &self, + flags: irq::flags::Flags, + irq_name: &'static CStr, + name: &'static CStr, + handler: T, + ) -> Result<impl PinInit<irq::$reg_type<T>, Error> + '_> { + 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 request_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 request_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 request_irq_by_name(&self, name: &'static 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 request_optional_irq_by_name(&self, name: &'static 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. + irq_by_index, request_irq_by_index, Registration, Handler + ); + define_irq_accessor_by_name!( + /// Returns a [`irq::Registration`] for the IRQ with the given name. + irq_by_name, request_irq_by_name, Registration, Handler + ); + define_irq_accessor_by_index!( + /// Does the same as [`Self::irq_by_index`], except that it does not + /// print an error message if the IRQ cannot be obtained. + optional_irq_by_index, request_optional_irq_by_index, Registration, Handler + ); + define_irq_accessor_by_name!( + /// Does the same as [`Self::irq_by_name`], except that it does not + /// print an error message if the IRQ cannot be obtained. + optional_irq_by_name, request_optional_irq_by_name, Registration, Handler + ); + + define_irq_accessor_by_index!( + /// Returns a [`irq::ThreadedRegistration`] for the IRQ at the given index. + threaded_irq_by_index, request_irq_by_index, ThreadedRegistration, ThreadedHandler + ); + define_irq_accessor_by_name!( + /// Returns a [`irq::ThreadedRegistration`] for the IRQ with the given name. + threaded_irq_by_name, request_irq_by_name, ThreadedRegistration, ThreadedHandler + ); + define_irq_accessor_by_index!( + /// Does the same as [`Self::threaded_irq_by_index`], except that it + /// does not print an error message if the IRQ cannot be obtained. + optional_threaded_irq_by_index, request_optional_irq_by_index, ThreadedRegistration, ThreadedHandler + ); + define_irq_accessor_by_name!( + /// Does the same as [`Self::threaded_irq_by_name`], except that it + /// does not print an error message if the IRQ cannot be obtained. + optional_threaded_irq_by_name, request_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 }); -- 2.50.0