All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Danilo Krummrich" <dakr@kernel.org>
To: "Ke Sun" <sunke@kylinos.cn>
Cc: "Alexandre Belloni" <alexandre.belloni@bootlin.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	linux-rtc@vger.kernel.org, rust-for-linux@vger.kernel.org,
	"Alvin Sun" <sk.alvin.x@gmail.com>
Subject: Re: [RFC PATCH v2 4/5] rust: add RTC core abstractions and data structures
Date: Thu, 08 Jan 2026 12:50:52 +0100	[thread overview]
Message-ID: <DFJ6P0ITWD1O.2PAYKPU63UFFC@kernel.org> (raw)
In-Reply-To: <20260107143738.3021892-5-sunke@kylinos.cn>

On Wed Jan 7, 2026 at 3:37 PM CET, Ke Sun wrote:
> +/// A Rust wrapper for the C `struct rtc_device`.
> +///
> +/// This type provides safe access to RTC device operations. The underlying `rtc_device`
> +/// is managed by the kernel and remains valid for the lifetime of the `RtcDevice`.
> +///
> +/// # Invariants
> +///
> +/// A [`RtcDevice`] instance holds a pointer to a valid [`struct rtc_device`] that is
> +/// registered and managed by the kernel.
> +///
> +/// # Examples
> +///
> +/// ```rust
> +/// # use kernel::{
> +/// #     prelude::*,
> +/// #     rtc::RtcDevice, //
> +/// # };
> +/// // Example: Set the time range for the RTC device
> +/// // rtc.set_range_min(0);
> +/// // rtc.set_range_max(u64::MAX);
> +/// //     Ok(())
> +/// // }

This example looks pretty odd, and I don't think it does compile. Did you test
with CONFIG_RUST_KERNEL_DOCTESTS=y?

> +/// ```
> +///
> +/// [`struct rtc_device`]: https://docs.kernel.org/driver-api/rtc.html
> +#[repr(transparent)]
> +pub struct RtcDevice<T: 'static = ()>(Opaque<bindings::rtc_device>, PhantomData<T>);
> +
> +impl<T: 'static> RtcDevice<T> {
> +    /// Obtain the raw [`struct rtc_device`] pointer.
> +    #[inline]
> +    pub fn as_raw(&self) -> *mut bindings::rtc_device {
> +        self.0.get()
> +    }
> +
> +    /// Set the minimum time range for the RTC device.
> +    #[inline]
> +    pub fn set_range_min(&self, min: i64) {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`, and we're only writing to the `range_min` field.
> +        unsafe {
> +            (*self.as_raw()).range_min = min;
> +        }
> +    }
> +
> +    /// Set the maximum time range for the RTC device.
> +    #[inline]
> +    pub fn set_range_max(&self, max: u64) {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`, and we're only writing to the `range_max` field.
> +        unsafe {
> +            (*self.as_raw()).range_max = max;
> +        }
> +    }
> +
> +    /// Get the minimum time range for the RTC device.
> +    #[inline]
> +    pub fn range_min(&self) -> i64 {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`, and we're only reading the `range_min` field.
> +        unsafe { (*self.as_raw()).range_min }
> +    }
> +
> +    /// Get the maximum time range for the RTC device.
> +    #[inline]
> +    pub fn range_max(&self) -> u64 {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`, and we're only reading the `range_max` field.
> +        unsafe { (*self.as_raw()).range_max }
> +    }
> +
> +    /// Notify the RTC framework that an interrupt has occurred.
> +    ///
> +    /// Should be called from interrupt handlers. Schedules work to handle the interrupt
> +    /// in process context.
> +    #[inline]
> +    pub fn update_irq(&self, num: usize, events: usize) {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`. The rtc_update_irq function handles NULL/ERR checks internally.
> +        unsafe {
> +            bindings::rtc_update_irq(self.as_raw(), num, events);
> +        }
> +    }
> +
> +    /// Clear a feature bit in the RTC device.
> +    #[inline]
> +    pub fn clear_feature(&self, feature: u32) {
> +        // SAFETY: By the type invariants, self.as_raw() is a valid pointer to a
> +        // `struct rtc_device`, and features is a valid bitmap array with RTC_FEATURE_CNT bits.
> +        let features_bitmap = unsafe {
> +            Bitmap::from_raw_mut(
> +                (*self.as_raw()).features.as_mut_ptr().cast::<usize>(),
> +                bindings::RTC_FEATURE_CNT as usize,
> +            )
> +        };
> +        features_bitmap.clear_bit(feature as usize);
> +    }
> +}
> +
> +impl<T: 'static, Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for RtcDevice<T> {

This should just be

	impl<T: 'static> AsRef<device::Device> for RtcDevice<T>

as class devices do not have a device context.

> +    fn as_ref(&self) -> &device::Device<Ctx> {
> +        let raw = self.as_raw();
> +        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
> +        // `struct rtc_device`.
> +        let dev = unsafe { &raw mut (*raw).dev };
> +
> +        // SAFETY: `dev` points to a valid `struct device`.
> +        unsafe { device::Device::from_raw(dev) }
> +    }
> +}
> +
> +// SAFETY: `RtcDevice` is a transparent wrapper of `struct rtc_device`.
> +// The offset is guaranteed to point to a valid device field inside `RtcDevice`.
> +unsafe impl<T: 'static, Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for RtcDevice<T> {
> +    const OFFSET: usize = core::mem::offset_of!(bindings::rtc_device, dev);
> +}

Please do not abuse this trait as container_of!(), as the name implies, it is
only for bus devices (hence also the device context generic). RTC devices are
class devices.

> +impl<T: RtcOps> RtcDevice<T> {
> +    /// Allocates a new RTC device managed by devres.
> +    ///
> +    /// This function allocates an RTC device and sets the driver data. The device will be
> +    /// automatically freed when the parent device is removed.
> +    pub fn new(
> +        parent_dev: &device::Device,

This must be a &Device<Bound>, otherwise you are not allowed to pass it to
devm_rtc_allocate_device().

> +        init: impl PinInit<T, Error>,
> +    ) -> Result<ARef<Self>> {
> +        // SAFETY: `Device<Bound>` and `Device<CoreInternal>` have the same layout.
> +        let dev_internal: &device::Device<device::CoreInternal> =
> +            unsafe { &*core::ptr::from_ref(parent_dev).cast() };
> +
> +        // Allocate RTC device.
> +        // SAFETY: `devm_rtc_allocate_device` returns a pointer to a devm-managed rtc_device.
> +        // We use `dev_internal.as_raw()` which is `pub(crate)`, but we can access it through
> +        // the same device pointer.
> +        let rtc: *mut bindings::rtc_device =
> +            unsafe { bindings::devm_rtc_allocate_device(dev_internal.as_raw()) };
> +        if rtc.is_null() {
> +            return Err(ENOMEM);
> +        }
> +
> +        // Set the RTC device ops.
> +        // SAFETY: We just allocated the RTC device, so it's safe to set the ops.
> +        unsafe {
> +            (*rtc).ops = Adapter::<T>::VTABLE.as_raw();
> +        }
> +
> +        // SAFETY: `rtc` is a valid pointer to a newly allocated rtc_device.
> +        // `RtcDevice` is `#[repr(transparent)]` over `Opaque<rtc_device>`, so we can safely cast.
> +        let rtc_device = unsafe { ARef::from_raw(NonNull::new_unchecked(rtc.cast::<Self>())) };
> +        rtc_device.set_drvdata(init)?;
> +        Ok(rtc_device)
> +    }
> +
> +    /// Store a pointer to the bound driver's private data.
> +    pub fn set_drvdata(&self, data: impl PinInit<T, Error>) -> Result {

This should not be public, as you should only use it in RtcDevice::new().

> +        let data = KBox::pin_init(data, GFP_KERNEL)?;
> +        let dev: &device::Device<device::Bound> = self.as_ref();
> +
> +        // SAFETY: `self.as_raw()` is a valid pointer to a `struct rtc_device`.
> +        unsafe { bindings::dev_set_drvdata(dev.as_raw(), data.into_foreign().cast()) };
> +        Ok(())
> +    }

<snip>

> +/// A resource guard that ensures the RTC device is properly registered.
> +///
> +/// This struct is intended to be managed by the `devres` framework by transferring its ownership
> +/// via [`devres::register`]. This ties the lifetime of the RTC device registration
> +/// to the lifetime of the underlying device.
> +pub struct Registration<T: 'static> {
> +    #[allow(dead_code)]
> +    rtc_device: ARef<RtcDevice<T>>,
> +}
> +
> +impl<T: 'static> Registration<T> {
> +    /// Registers an RTC device with the RTC subsystem.
> +    ///
> +    /// Transfers its ownership to the `devres` framework, which ties its lifetime
> +    /// to the parent device.
> +    /// On unbind of the parent device, the `devres` entry will be dropped, automatically
> +    /// cleaning up the RTC device. This function should be called from the driver's `probe`.
> +    pub fn register(dev: &device::Device<device::Bound>, rtc_device: ARef<RtcDevice<T>>) -> Result {
> +        let rtc_dev: &device::Device = rtc_device.as_ref();
> +        let rtc_parent = rtc_dev.parent().ok_or(EINVAL)?;
> +        if dev.as_raw() != rtc_parent.as_raw() {
> +            return Err(EINVAL);
> +        }
> +
> +        // Registers an RTC device with the RTC subsystem.
> +        // SAFETY: The device will be automatically unregistered when the parent device
> +        // is removed (devm cleanup). The helper function uses `THIS_MODULE` internally.
> +        let err = unsafe { bindings::devm_rtc_register_device(rtc_device.as_raw()) };
> +        if err != 0 {
> +            return Err(Error::from_errno(err));
> +        }
> +
> +        let registration = Registration { rtc_device };
> +
> +        devres::register(dev, registration, GFP_KERNEL)

You are using devm_rtc_register_device() above already, hence you neither need
an instance of Registration, nor do you need to manage this Registration with
devres through devres::register().

> +    }
> +}
> +
> +/// Options for creating an RTC device.
> +#[derive(Copy, Clone)]
> +pub struct RtcDeviceOptions {
> +    /// The name of the RTC device.
> +    pub name: &'static CStr,
> +}
> +
> +/// Trait implemented by RTC device operations.
> +///
> +/// This trait defines the operations that an RTC device driver must implement.
> +/// Most methods are optional and have default implementations that return an error.
> +#[vtable]
> +pub trait RtcOps: Sized + 'static {

Please utilize the AsBusDevice trait to be able to provide the parent device of
the RTC device as &Device<Bound>, similarly to what is done in [1].

[1] https://lore.kernel.org/all/20260106-rust_leds-v10-1-e0a1564884f9@posteo.de/

  reply	other threads:[~2026-01-08 11:50 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-07 14:37 [RFC PATCH v2 0/5] rust: Add RTC driver support Ke Sun
2026-01-07 14:37 ` [RFC PATCH v2 1/5] rtc: migrate driver data to RTC device Ke Sun
2026-01-07 14:41   ` Ke Sun
2026-01-07 16:12   ` Greg KH
2026-01-07 23:18     ` Ke Sun
2026-01-08  0:24       ` Ke Sun
2026-01-08 11:06         ` Danilo Krummrich
2026-01-08  5:46       ` Greg KH
2026-01-08  9:02         ` Ke Sun
2026-01-08  9:10           ` Greg KH
2026-01-08 11:12   ` Danilo Krummrich
2026-01-08 13:45     ` Ke Sun
2026-01-08 13:52       ` Danilo Krummrich
2026-01-08 14:01         ` Ke Sun
2026-01-08 14:01         ` Alexandre Belloni
2026-01-08 14:06           ` Danilo Krummrich
2026-02-20 23:19             ` Alexandre Belloni
2026-01-14 23:23           ` Ke Sun
2026-01-14 23:48             ` Danilo Krummrich
2026-01-07 14:37 ` [RFC PATCH v2 2/5] rust: add AMBA bus driver support Ke Sun
2026-01-08 11:29   ` Danilo Krummrich
2026-01-07 14:37 ` [RFC PATCH v2 3/5] rust: add device wakeup capability support Ke Sun
2026-01-07 14:57   ` Greg KH
2026-01-07 23:35     ` Ke Sun
2026-01-07 14:37 ` [RFC PATCH v2 4/5] rust: add RTC core abstractions and data structures Ke Sun
2026-01-08 11:50   ` Danilo Krummrich [this message]
2026-01-08 13:17     ` Ke Sun
2026-01-08 13:49       ` Miguel Ojeda
2026-01-08 13:56         ` Ke Sun
2026-01-08 23:31   ` Kari Argillander
2026-01-07 14:37 ` [RFC PATCH v2 5/5] rust: add PL031 RTC driver Ke Sun
2026-01-08 11:57   ` Danilo Krummrich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DFJ6P0ITWD1O.2PAYKPU63UFFC@kernel.org \
    --to=dakr@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alexandre.belloni@bootlin.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=gary@garyguo.net \
    --cc=linux-rtc@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sk.alvin.x@gmail.com \
    --cc=sunke@kylinos.cn \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.