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/
next prev parent 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox