|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Platform devices. |
| 4 | +//! |
| 5 | +//! Also called `platdev`, `pdev`. |
| 6 | +//! |
| 7 | +//! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h) |
| 8 | +
|
| 9 | +use crate::{ |
| 10 | + bindings, c_types, |
| 11 | + error::{Error, Result}, |
| 12 | + pr_info, CStr, |
| 13 | +}; |
| 14 | +use alloc::boxed::Box; |
| 15 | +use core::{marker::PhantomPinned, pin::Pin}; |
| 16 | + |
| 17 | +/// A registration of a platform device. |
| 18 | +#[derive(Default)] |
| 19 | +pub struct Registration { |
| 20 | + registered: bool, |
| 21 | + pdrv: bindings::platform_driver, |
| 22 | + _pin: PhantomPinned, |
| 23 | +} |
| 24 | + |
| 25 | +// SAFETY: `Registration` does not expose any of its state across threads |
| 26 | +// (it is fine for multiple threads to have a shared reference to it). |
| 27 | +unsafe impl Sync for Registration {} |
| 28 | + |
| 29 | +extern "C" fn probe_callback(_pdev: *mut bindings::platform_device) -> c_types::c_int { |
| 30 | + pr_info!("Rust platform_device probed\n"); |
| 31 | + 0 |
| 32 | +} |
| 33 | + |
| 34 | +extern "C" fn remove_callback(_pdev: *mut bindings::platform_device) -> c_types::c_int { |
| 35 | + pr_info!("Rust platform_device removed\n"); |
| 36 | + 0 |
| 37 | +} |
| 38 | + |
| 39 | +impl Registration { |
| 40 | + fn register( |
| 41 | + self: Pin<&mut Self>, |
| 42 | + name: CStr<'static>, |
| 43 | + module: &'static crate::ThisModule, |
| 44 | + ) -> Result { |
| 45 | + // SAFETY: We must ensure that we never move out of `this`. |
| 46 | + let this = unsafe { self.get_unchecked_mut() }; |
| 47 | + if this.registered { |
| 48 | + // Already registered. |
| 49 | + return Err(Error::EINVAL); |
| 50 | + } |
| 51 | + this.pdrv.driver.name = name.as_ptr() as *const c_types::c_char; |
| 52 | + this.pdrv.probe = Some(probe_callback); |
| 53 | + this.pdrv.remove = Some(remove_callback); |
| 54 | + // SAFETY: |
| 55 | + // - `this.pdrv` lives at least until the call to `platform_driver_unregister()` returns. |
| 56 | + // - `name` pointer has static lifetime. |
| 57 | + // - `module.0` lives as least as long as the module. |
| 58 | + // - `probe()` and `remove()` are static functions. |
| 59 | + // So `bindings::__platform_driver_register()` is safe to call. |
| 60 | + let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) }; |
| 61 | + if ret < 0 { |
| 62 | + return Err(Error::from_kernel_errno(ret)); |
| 63 | + } |
| 64 | + this.registered = true; |
| 65 | + Ok(()) |
| 66 | + } |
| 67 | + |
| 68 | + /// Registers a platform device. |
| 69 | + /// |
| 70 | + /// Returns a pinned heap-allocated representation of the registration. |
| 71 | + pub fn new_pinned( |
| 72 | + name: CStr<'static>, |
| 73 | + module: &'static crate::ThisModule, |
| 74 | + ) -> Result<Pin<Box<Self>>> { |
| 75 | + let mut r = Pin::from(Box::try_new(Self::default())?); |
| 76 | + r.as_mut().register(name, module)?; |
| 77 | + Ok(r) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +impl Drop for Registration { |
| 82 | + fn drop(&mut self) { |
| 83 | + if self.registered { |
| 84 | + // SAFETY: if `registered` is true, then `self.pdev` was registered |
| 85 | + // previously, which means `platform_driver_unregister` is always |
| 86 | + // safe to call. |
| 87 | + unsafe { bindings::platform_driver_unregister(&mut self.pdrv) } |
| 88 | + } |
| 89 | + } |
| 90 | +} |
0 commit comments