Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH v14 0/3] rust: add basic serial device bus abstractions
@ 2026-07-15 21:00 Markus Probst
  2026-07-15 21:00 ` [PATCH v14 1/3] " Markus Probst
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Markus Probst @ 2026-07-15 21:00 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

This patch series adds the serdev device bus rust abstraction into the
kernel.

This abstraction will be used by a driver,
which targets the MCU devices in Synology devices.

Kari Argillander also messaged me, stating that he wants to write a
watchdog driver with this abstraction (needing initial device data).

This series depends on [1].

[1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v14:
- fix issues reported by Sashiko:
  - fix missing Sync
  - fix write buffers allowed with data.len() > i32::MAX
  - fix sample driver possibly stalling on full tx buffer
- Link to v13: https://patch.msgid.link/20260712-rust_serdev-v13-0-2c4060bc8e30@posteo.de

Changes in v13:
- Added Acked-by trailer from Rob. Consider this a RESEND.
- Link to v12: https://patch.msgid.link/20260603-rust_serdev-v12-0-3400ffb88b12@posteo.de

Changes in v12:
- fix CoreInternal instead of BoundInternal
- Link to v11: https://patch.msgid.link/20260531-rust_serdev-v11-0-dee8e0d830f1@posteo.de

Changes in v11:
- redo changes from v9 with the following fixes:
  - fix memory leak on probe failure
  - fix possible access of dropped drvdata from receive_buf
- Link to v10: https://patch.msgid.link/20260530-rust_serdev-v10-0-65d1d5db876c@posteo.de

Changes in v10:
- revert everything from v9, except BoundInternal
- use mutex to ensure receive_buf won't be called on a dropped drvdata.
- Link to v9: https://patch.msgid.link/20260530-rust_serdev-v9-0-f8b5fccb49c3@posteo.de

Changes in v9:
- make use of BoundInternal
- use PrivateData wrapper and drop rust_private_data field
- use non-devm version of serdev_device_open
- Link to v8: https://patch.msgid.link/20260530-rust_serdev-v8-0-2a95f1da22a7@posteo.de

Changes in v8:
- adapted to driver-lifetime v5 patch series
- add MAINTAINERS file patch
- Link to v7: https://patch.msgid.link/20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de

Changes in v7:
- adapted to driver-lifetime patch series
- Link to v6: https://patch.msgid.link/20260427-rust_serdev-v6-0-173798d5e1a3@posteo.de

Changes in v6:
- rebased onto v7.1-rc1
- Link to v5: https://patch.msgid.link/20260420-rust_serdev-v5-0-57e8ba0519f3@posteo.de

Changes in v5:
- fix typo in documentation
- Link to v4: https://lore.kernel.org/r/20260411-rust_serdev-v4-0-845e960c6627@posteo.de

Changes in v4:
- fixed not selecting rust serdev abstraction in sample
- Link to v3: https://lore.kernel.org/r/20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de

Changes in v3:
- fix vertical import style
- add Kconfig entry for the rust abstraction
- fix documentation in include/linux/serdev.h
- rename private_data to rust_private_data
- fix `complete_all` <-> `wait_for_completion` typo
- move drvdata_borrow call after the completion
- Link to v2: https://lore.kernel.org/r/20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de

Changes in v2:
- fix documentation in `serdev::Driver::write` and
  `serdev::Driver::write_all`
- remove use of `dev_info` in probe from the sample
- remove `properties_parse` from the sample
- add optional `baudrate` property to the sample
- remove 1. patch
- remove `TryFrom<&device::Device<Ctx>> for &serdev::Device<Ctx>`
  implementation
- fix import style
- add patch to return reference in `devres::register` to fix safety
  issue
- add patch to add private data to serdev_device, to fix
  `Device.drvdata()` from failing
- simplify abstraction by removing ability to receive the initial
  transmission. It may be added later in a separate patch series if
  needed.
- Link to v1: https://lore.kernel.org/r/20251220-rust_serdev-v1-0-e44645767621@posteo.de

---
Markus Probst (3):
      rust: add basic serial device bus abstractions
      samples: rust: add Rust serial device bus sample device driver
      MAINTAINERS: serdev: Add self for serdev

 MAINTAINERS                        |   5 +-
 drivers/tty/serdev/Kconfig         |   7 +
 rust/bindings/bindings_helper.h    |   1 +
 rust/helpers/helpers.c             |   1 +
 rust/helpers/serdev.c              |  22 ++
 rust/kernel/lib.rs                 |   2 +
 rust/kernel/serdev.rs              | 599 +++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig               |  11 +
 samples/rust/Makefile              |   1 +
 samples/rust/rust_driver_serdev.rs |  90 ++++++
 10 files changed, 738 insertions(+), 1 deletion(-)
---
base-commit: 1137d8b5df06137fb49513cc923b3b24d94cb809
change-id: 20251217-rust_serdev-ee5481e9085c
prerequisite-message-id: 20260530132736.3298549-1-dakr@kernel.org
prerequisite-patch-id: 310c6bee038ca3909a8e5e58ec12b74f7189b869
prerequisite-patch-id: 92812c3d42b29504838e6dc66c307ff5c035bb5e


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v14 1/3] rust: add basic serial device bus abstractions
  2026-07-15 21:00 [PATCH v14 0/3] rust: add basic serial device bus abstractions Markus Probst
@ 2026-07-15 21:00 ` Markus Probst
  2026-07-17 11:15   ` Gary Guo
  2026-07-15 21:00 ` [PATCH v14 2/3] samples: rust: add Rust serial device bus sample device driver Markus Probst
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Markus Probst @ 2026-07-15 21:00 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Implement the basic serial device bus abstractions required to write a
serial device bus device driver with or without the need for initial device
data. This includes the following data structures:

The `serdev::Driver` trait represents the interface to the driver.

The `serdev::Device` abstraction represents a `struct serdev_device`.

In order to provide the Serdev specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is
implemented by `serdev::Adapter`.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                     |   2 +
 drivers/tty/serdev/Kconfig      |   7 +
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/serdev.c           |  22 ++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/serdev.rs           | 599 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 634 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 806bd2d80d15..cd735d227892 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24568,6 +24568,8 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/serial/serial.yaml
 F:	drivers/tty/serdev/
 F:	include/linux/serdev.h
+F:	rust/helpers/serdev.c
+F:	rust/kernel/serdev.rs
 
 SERIAL IR RECEIVER
 M:	Sean Young <sean@mess.org>
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 46ae732bfc68..e6dfe949ad01 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -9,6 +9,13 @@ menuconfig SERIAL_DEV_BUS
 
 	  Note that you typically also want to enable TTY port controller support.
 
+config RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	bool "Rust Serial device bus abstractions"
+	depends on RUST
+	select SERIAL_DEV_BUS
+	help
+	  This enables the Rust abstraction for the serial device bus API.
+
 if SERIAL_DEV_BUS
 
 config SERIAL_DEV_CTRL_TTYPORT
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..fe7c505da236 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -85,6 +85,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
 #include <linux/security.h>
+#include <linux/serdev.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 #include <linux/task_work.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 998e31052e66..6921a5ecb78a 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -87,6 +87,7 @@
 #include "regulator.c"
 #include "scatterlist.c"
 #include "security.c"
+#include "serdev.c"
 #include "signal.c"
 #include "slab.c"
 #include "spinlock.c"
diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
new file mode 100644
index 000000000000..c52b78ca3fc7
--- /dev/null
+++ b/rust/helpers/serdev.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/serdev.h>
+
+__rust_helper
+void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
+{
+	serdev_device_driver_unregister(sdrv);
+}
+
+__rust_helper
+void rust_helper_serdev_device_put(struct serdev_device *serdev)
+{
+	serdev_device_put(serdev);
+}
+
+__rust_helper
+void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
+					      const struct serdev_device_ops *ops)
+{
+	serdev_device_set_client_ops(serdev, ops);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9512af7156df..d5ca17336739 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -119,6 +119,8 @@
 pub mod scatterlist;
 pub mod security;
 pub mod seq_file;
+#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
+pub mod serdev;
 pub mod sizes;
 #[cfg(CONFIG_SOC_BUS)]
 pub mod soc;
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
new file mode 100644
index 000000000000..0ffcef1849d2
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the serial device bus.
+//!
+//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
+
+use crate::{
+    acpi,
+    device,
+    driver,
+    error::{
+        from_result,
+        to_result,
+        VTABLE_DEFAULT_ERROR, //
+    },
+    new_mutex,
+    of,
+    prelude::*,
+    sync::{
+        aref::AlwaysRefCounted,
+        Mutex, //
+    },
+    time::{
+        msecs_to_jiffies,
+        Jiffies,
+        Msecs, //
+    },
+    types::{
+        Opaque,
+        ScopeGuard, //
+    }, //
+};
+
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomData,
+    mem::{offset_of, MaybeUninit},
+    num::NonZero,
+    ptr::NonNull, //
+};
+
+/// Parity bit to use with a serial device.
+#[repr(u32)]
+pub enum Parity {
+    /// No parity bit.
+    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
+    /// Even partiy.
+    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
+    /// Odd parity.
+    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
+}
+
+/// Timeout in Jiffies.
+pub enum Timeout {
+    /// Wait for a specific amount of [`Jiffies`].
+    Jiffies(NonZero<Jiffies>),
+    /// Wait for a specific amount of [`Msecs`].
+    Milliseconds(NonZero<Msecs>),
+    /// Wait as long as possible.
+    ///
+    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
+    Max,
+}
+
+impl Timeout {
+    fn into_jiffies(self) -> isize {
+        match self {
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
+            Self::Milliseconds(value) => {
+                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
+            }
+            Self::Max => 0,
+        }
+    }
+}
+
+/// An adapter for the registration of serial device bus device drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
+// - `PrivateData<'bound, T>` is the type of the driver's device private data.
+// - `struct serdev_device_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver> driver::DriverLayout for Adapter<T> {
+    type DriverType = bindings::serdev_device_driver;
+    type DriverData<'bound> = PrivateData<'bound, T>;
+    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
+    unsafe fn register(
+        sdrv: &Opaque<Self::DriverType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        let of_table = match T::OF_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let acpi_table = match T::ACPI_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
+        unsafe {
+            (*sdrv.get()).driver.name = name.as_char_ptr();
+            (*sdrv.get()).probe = Some(Self::probe_callback);
+            (*sdrv.get()).remove = Some(Self::remove_callback);
+            (*sdrv.get()).driver.of_match_table = of_table;
+            (*sdrv.get()).driver.acpi_match_table = acpi_table;
+        }
+
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
+    }
+
+    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
+    }
+}
+
+#[doc(hidden)]
+#[pin_data(PinnedDrop)]
+pub struct PrivateData<'bound, T: Driver> {
+    sdev: &'bound Device<device::Bound>,
+    #[pin]
+    driver: UnsafeCell<MaybeUninit<T::Data<'bound>>>,
+    open: UnsafeCell<bool>,
+    #[pin]
+    active: Mutex<bool>,
+}
+
+#[pinned_drop]
+impl<T: Driver> PinnedDrop for PrivateData<'_, T> {
+    fn drop(self: Pin<&mut Self>) {
+        let mut active = self.active.lock();
+        if *active {
+            // SAFETY:
+            // - We have exclusive access to `self.driver`.
+            // - `self.driver` is guaranteed to be initialized.
+            unsafe { (*self.driver.get()).assume_init_drop() };
+            *active = false;
+        }
+        drop(active);
+
+        // SAFETY: We have exclusive access to `self.open`.
+        if unsafe { *self.open.get() } {
+            // SAFETY: `self.sdev.as_raw()` is guaranteed to be a pointer to a valid
+            // `struct serdev_device`.
+            unsafe { bindings::serdev_device_close(self.sdev.as_raw()) };
+        }
+    }
+}
+
+impl<T: Driver> Adapter<T> {
+    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
+        receive_buf: if T::HAS_RECEIVE {
+            Some(Self::receive_buf_callback)
+        } else {
+            None
+        },
+        write_wakeup: Some(bindings::serdev_device_write_wakeup),
+    };
+
+    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
+        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
+        // a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
+        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
+
+        from_result(|| {
+            sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
+                sdev: &**sdev,
+                driver: MaybeUninit::<T::Data<'_>>::zeroed().into(),
+                open: false.into(),
+                active <- new_mutex!(false),
+            }))?;
+            // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
+            let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
+            let private_data = ScopeGuard::new_with_data(private_data, |_| {
+                // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
+                drop(unsafe { sdev.as_ref().drvdata_obtain::<PrivateData<'_, T>>() });
+            });
+            let mut active = private_data.active.lock();
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
+
+            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
+            // to a `serdev_device`.
+            to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?;
+
+            // SAFETY: We have exclusive access to `private_data.open`.
+            unsafe { *private_data.open.get() = true };
+
+            let data = T::probe(sdev, info);
+
+            // SAFETY: We have exclusive access to `private_data.driver`.
+            let driver = unsafe { &mut *private_data.driver.get() };
+            // SAFETY:
+            // - `driver.as_mut_ptr()` is a valid pointer to uninitialized data.
+            // - `private_data.driver` is pinned.
+            let result = unsafe { data.__pinned_init(driver.as_mut_ptr()) };
+
+            *active = result.is_ok();
+
+            drop(active);
+
+            result.map(|()| {
+                private_data.dismiss();
+                0
+            })
+        })
+    }
+
+    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
+        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
+        // to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
+
+        // SAFETY: `remove_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
+        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
+
+        // SAFETY: No one has exclusive access to `private_data.driver`.
+        let data = unsafe { &*private_data.driver.get() };
+        // SAFETY:
+        // - `private_data.driver` is pinned.
+        // - `remove_callback` is only ever called after a successful call to `probe_callback`,
+        //   hence it's guaranteed that `private_data.driver` was initialized.
+        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
+
+        T::unbind(sdev, data_pinned);
+    }
+
+    extern "C" fn receive_buf_callback(
+        sdev: *mut bindings::serdev_device,
+        buf: *const u8,
+        length: usize,
+    ) -> usize {
+        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
+        // pointer to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::BoundInternal>>() };
+
+        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
+        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
+        let active = private_data.active.lock();
+
+        if !*active {
+            return length;
+        }
+
+        // SAFETY: No one has exclusive access to `private_data.driver`.
+        let data = unsafe { &*private_data.driver.get() };
+        // SAFETY:
+        // - `private_data.driver` is pinned.
+        // - `receive_buf_callback` is only ever called after a successful call to `probe_callback`,
+        //   hence it's guaranteed that `private_data.driver` was initialized.
+        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
+
+        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
+        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
+
+        T::receive(sdev, data_pinned, buf)
+    }
+}
+
+impl<T: Driver> driver::Adapter for Adapter<T> {
+    type IdInfo = T::IdInfo;
+
+    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+        T::OF_ID_TABLE
+    }
+
+    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+        T::ACPI_ID_TABLE
+    }
+}
+
+/// Declares a kernel module that exposes a single serial device bus device driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_serdev_device_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_serdev_device_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
+    };
+}
+
+/// The serial device bus device driver trait.
+///
+/// Drivers must implement this trait in order to get a serial device bus device driver registered.
+///
+/// # Examples
+///
+///```
+/// # use kernel::{
+///     acpi,
+///     bindings,
+///     device::{
+///         Bound,
+///         Core, //
+///     },
+///     of,
+///     serdev, //
+/// };
+///
+/// struct MyDriver;
+///
+/// kernel::of_device_table!(
+///     OF_TABLE,
+///     MODULE_OF_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (of::DeviceId::new(c"test,device"), ())
+///     ]
+/// );
+///
+/// kernel::acpi_device_table!(
+///     ACPI_TABLE,
+///     MODULE_ACPI_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
+///     ]
+/// );
+///
+/// #[vtable]
+/// impl serdev::Driver for MyDriver {
+///     type IdInfo = ();
+///     type Data<'bound> = Self;
+///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+///
+///     fn probe<'bound>(
+///         sdev: &'bound serdev::Device<Core<'_>>,
+///         _id_info: Option<&'bound Self::IdInfo>,
+///     ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+///         sdev.set_baudrate(115200);
+///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
+///         Ok(MyDriver)
+///     }
+/// }
+///```
+#[vtable]
+pub trait Driver {
+    /// The type holding driver private data about each device id supported by the driver.
+    // TODO: Use associated_type_defaults once stabilized:
+    //
+    // ```
+    // type IdInfo: 'static = ();
+    // ```
+    type IdInfo: 'static;
+
+    /// The type of the driver's bus device private data.
+    type Data<'bound>: Send + Sync + 'bound;
+
+    /// The table of OF device ids supported by the driver.
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+
+    /// The table of ACPI device ids supported by the driver.
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
+
+    /// Serial device bus device driver probe.
+    ///
+    /// Called when a new serial device bus device is added or discovered.
+    /// Implementers should attempt to initialize the device here.
+    fn probe<'bound>(
+        sdev: &'bound Device<device::Core<'_>>,
+        id_info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound;
+
+    /// Serial device bus device driver unbind.
+    ///
+    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+    /// is optional.
+    ///
+    /// This callback serves as a place for drivers to perform teardown operations that require a
+    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
+    ///
+    /// Otherwise, release operations for driver resources should be performed in `Drop`.
+    fn unbind<'bound>(sdev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
+        let _ = (sdev, this);
+    }
+
+    /// Serial device bus device data receive callback.
+    ///
+    /// Called when data got received from device.
+    ///
+    /// Returns the number of bytes accepted.
+    fn receive<'bound>(
+        sdev: &'bound Device<device::Bound>,
+        this: Pin<&Self::Data<'bound>>,
+        data: &[u8],
+    ) -> usize {
+        let _ = (sdev, this, data);
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// The serial device bus device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
+/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
+/// code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::serdev_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::serdev_device {
+        self.0.get()
+    }
+}
+
+impl Device<device::Bound> {
+    /// Set the baudrate in bits per second.
+    ///
+    /// Common baudrates are 115200, 9600, 19200, 57600, 4800.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        let ret = unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed) };
+        if ret == speed {
+            Ok(())
+        } else {
+            Err(ret)
+        }
+    }
+
+    /// Set if flow control should be enabled.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_flow_control(&self, enable: bool) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), enable) };
+    }
+
+    /// Set parity to use.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_parity(&self, parity: Parity) -> Result {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        to_result(unsafe { bindings::serdev_device_set_parity(self.as_raw(), parity as u32) })
+    }
+
+    /// Write data to the serial device until the controller has accepted all the data or has
+    /// been interrupted by a timeout or signal.
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [`Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if interrupted).
+    /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSYS`] if interrupted
+    /// before any bytes were written.
+    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
+        if data.len() > i32::MAX as usize {
+            return Err(EINVAL);
+        }
+
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret = unsafe {
+            bindings::serdev_device_write(
+                self.as_raw(),
+                data.as_ptr(),
+                data.len(),
+                timeout.into_jiffies(),
+            )
+        };
+        // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` and `-1`,
+        // which always fit into a `i32`.
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Write data to the serial device.
+    ///
+    /// If you want to write until the controller has accepted all the data, use
+    /// [`Device::write_all`].
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if not enough room in the
+    /// write buffer).
+    pub fn write(&self, data: &[u8]) -> Result<u32> {
+        if data.len() > i32::MAX as usize {
+            return Err(EINVAL);
+        }
+
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret =
+            unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_ptr(), data.len()) };
+
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Send data to the serial device immediately.
+    ///
+    /// Note that this doesn't guarantee that the data has been transmitted.
+    /// Use [`Device::wait_until_sent`] for this purpose.
+    pub fn write_flush(&self) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_write_flush(self.as_raw()) };
+    }
+
+    /// Wait for the data to be sent.
+    ///
+    /// After this function, the write buffer of the controller should be empty.
+    pub fn wait_until_sent(&self, timeout: Timeout) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), timeout.into_jiffies()) };
+    }
+}
+
+// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_device`.
+// The offset is guaranteed to point to a valid device field inside `serdev::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
+    const OFFSET: usize = offset_of!(bindings::serdev_device, dev);
+}
+
+// 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 });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        self.as_ref().inc_ref();
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct serdev_device`.
+        let dev = unsafe { &raw mut (*self.as_raw()).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}
+
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct serdev_device` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v14 2/3] samples: rust: add Rust serial device bus sample device driver
  2026-07-15 21:00 [PATCH v14 0/3] rust: add basic serial device bus abstractions Markus Probst
  2026-07-15 21:00 ` [PATCH v14 1/3] " Markus Probst
@ 2026-07-15 21:00 ` Markus Probst
  2026-07-15 21:00 ` [PATCH v14 3/3] MAINTAINERS: serdev: Add self for serdev Markus Probst
  2026-07-17 10:55 ` [PATCH v14 0/3] rust: add basic serial device bus abstractions Greg Kroah-Hartman
  3 siblings, 0 replies; 17+ messages in thread
From: Markus Probst @ 2026-07-15 21:00 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Add a sample Rust serial device bus device driver illustrating the usage
of the serial device bus abstractions.

This drivers probes through either a match of device / driver name or a
match within the OF ID table.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                        |  1 +
 samples/rust/Kconfig               | 11 +++++
 samples/rust/Makefile              |  1 +
 samples/rust/rust_driver_serdev.rs | 90 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 103 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index cd735d227892..7d32aa7f42a4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24570,6 +24570,7 @@ F:	drivers/tty/serdev/
 F:	include/linux/serdev.h
 F:	rust/helpers/serdev.c
 F:	rust/kernel/serdev.rs
+F:	samples/rust/rust_driver_serdev.rs
 
 SERIAL IR RECEIVER
 M:	Sean Young <sean@mess.org>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab9106345..31d62533ef25 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -161,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_SERDEV
+	tristate "Serial Device Bus Device Driver"
+	select RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  This option builds the Rust serial device bus driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_serdev.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_SOC
 	tristate "SoC Driver"
 	select SOC_BUS
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58cccc..b986b681cde5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_SERDEV)		+= rust_driver_serdev.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
 
diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
new file mode 100644
index 000000000000..a12b1412db30
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Serial device bus device driver sample.
+
+use kernel::{
+    acpi,
+    device::{
+        Bound,
+        Core, //
+    },
+    of,
+    prelude::*,
+    serdev,
+    sync::aref::ARef, //
+};
+
+struct SampleDriver {
+    sdev: ARef<serdev::Device>,
+}
+
+kernel::of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
+);
+
+kernel::acpi_device_table!(
+    ACPI_TABLE,
+    MODULE_ACPI_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
+);
+
+#[vtable]
+impl serdev::Driver for SampleDriver {
+    type IdInfo = ();
+    type Data<'bound> = Self;
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+    fn probe<'bound>(
+        sdev: &'bound serdev::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'bound {
+        let dev = sdev.as_ref();
+
+        dev_dbg!(dev, "Probe Rust Serial device bus device driver sample.\n");
+
+        if sdev
+            .set_baudrate(
+                dev.fwnode()
+                    .and_then(|fwnode| fwnode.property_read(c"baudrate").optional())
+                    .unwrap_or(115200),
+            )
+            .is_err()
+        {
+            return Err(EINVAL);
+        }
+        sdev.set_flow_control(false);
+        sdev.set_parity(serdev::Parity::None)?;
+
+        Ok(Self { sdev: sdev.into() })
+    }
+
+    fn receive<'bound>(
+        sdev: &'bound serdev::Device<Bound>,
+        _this: Pin<&Self>,
+        data: &[u8],
+    ) -> usize {
+        sdev.write(data).unwrap_or_default() as usize
+    }
+}
+
+impl Drop for SampleDriver {
+    fn drop(&mut self) {
+        dev_dbg!(
+            self.sdev.as_ref(),
+            "Remove Rust Serial device bus device driver sample.\n"
+        );
+    }
+}
+
+kernel::module_serdev_device_driver! {
+    type: SampleDriver,
+    name: "rust_driver_serdev",
+    authors: ["Markus Probst"],
+    description: "Rust Serial device bus device driver",
+    license: "GPL v2",
+}

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v14 3/3] MAINTAINERS: serdev: Add self for serdev
  2026-07-15 21:00 [PATCH v14 0/3] rust: add basic serial device bus abstractions Markus Probst
  2026-07-15 21:00 ` [PATCH v14 1/3] " Markus Probst
  2026-07-15 21:00 ` [PATCH v14 2/3] samples: rust: add Rust serial device bus sample device driver Markus Probst
@ 2026-07-15 21:00 ` Markus Probst
  2026-07-17 10:55 ` [PATCH v14 0/3] rust: add basic serial device bus abstractions Greg Kroah-Hartman
  3 siblings, 0 replies; 17+ messages in thread
From: Markus Probst @ 2026-07-15 21:00 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Rob mentioned he needs to find someone else to maintain serdev.

Link: https://lore.kernel.org/rust-for-linux/20260430195858.GA1650658-robh@kernel.org/
Link: https://lore.kernel.org/rust-for-linux/da85ceb81f51079d4a8248a1ffde6a27d2ef24ad.camel@posteo.de/
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7d32aa7f42a4..d092042fed31 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24562,7 +24562,7 @@ F:	drivers/iio/chemical/sps30_i2c.c
 F:	drivers/iio/chemical/sps30_serial.c
 
 SERIAL DEVICE BUS
-M:	Rob Herring <robh@kernel.org>
+M:	Markus Probst <markus.probst@posteo.de>
 L:	linux-serial@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/serial/serial.yaml

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-15 21:00 [PATCH v14 0/3] rust: add basic serial device bus abstractions Markus Probst
                   ` (2 preceding siblings ...)
  2026-07-15 21:00 ` [PATCH v14 3/3] MAINTAINERS: serdev: Add self for serdev Markus Probst
@ 2026-07-17 10:55 ` Greg Kroah-Hartman
  2026-07-17 11:14   ` Markus Probst
  2026-07-17 11:19   ` Danilo Krummrich
  3 siblings, 2 replies; 17+ messages in thread
From: Greg Kroah-Hartman @ 2026-07-17 10:55 UTC (permalink / raw)
  To: Markus Probst
  Cc: Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, linux-serial, linux-kernel,
	rust-for-linux, linux-pm, driver-core, dri-devel

On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
> This patch series adds the serdev device bus rust abstraction into the
> kernel.
> 
> This abstraction will be used by a driver,
> which targets the MCU devices in Synology devices.
> 
> Kari Argillander also messaged me, stating that he wants to write a
> watchdog driver with this abstraction (needing initial device data).
> 
> This series depends on [1].
> 
> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> 
> Signed-off-by: Markus Probst <markus.probst@posteo.de>

Given there are no objections, I'll go queue this up now, thanks.

greg k-h

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 10:55 ` [PATCH v14 0/3] rust: add basic serial device bus abstractions Greg Kroah-Hartman
@ 2026-07-17 11:14   ` Markus Probst
  2026-07-17 11:22     ` Danilo Krummrich
  2026-07-17 11:19   ` Danilo Krummrich
  1 sibling, 1 reply; 17+ messages in thread
From: Markus Probst @ 2026-07-17 11:14 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, linux-serial, linux-kernel,
	rust-for-linux, linux-pm, driver-core, dri-devel

[-- Attachment #1: Type: text/plain, Size: 1073 bytes --]

On Fri, 2026-07-17 at 12:55 +0200, Greg Kroah-Hartman wrote:
> On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
> > This patch series adds the serdev device bus rust abstraction into the
> > kernel.
> > 
> > This abstraction will be used by a driver,
> > which targets the MCU devices in Synology devices.
> > 
> > Kari Argillander also messaged me, stating that he wants to write a
> > watchdog driver with this abstraction (needing initial device data).
> > 
> > This series depends on [1].
> > 
> > [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> > 
> > Signed-off-by: Markus Probst <markus.probst@posteo.de>
> 
> Given there are no objections, I'll go queue this up now, thanks.
Thanks for merging, but I haven't seen the dependency

https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/

getting picked yet (its at least not in linux-next), so it might not
even compile. Not sure whats holding the patch back though.

Thanks
- Markus Probst


> 
> greg k-h

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 1/3] rust: add basic serial device bus abstractions
  2026-07-15 21:00 ` [PATCH v14 1/3] " Markus Probst
@ 2026-07-17 11:15   ` Gary Guo
  2026-07-17 11:27     ` Markus Probst
  0 siblings, 1 reply; 17+ messages in thread
From: Gary Guo @ 2026-07-17 11:15 UTC (permalink / raw)
  To: Markus Probst, Rob Herring, Greg Kroah-Hartman, Jiri Slaby,
	Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Kari Argillander, Rafael J. Wysocki, Viresh Kumar, Boqun Feng,
	David Airlie, Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Wed Jul 15, 2026 at 10:00 PM BST, Markus Probst wrote:
> Implement the basic serial device bus abstractions required to write a
> serial device bus device driver with or without the need for initial device
> data. This includes the following data structures:
>
> The `serdev::Driver` trait represents the interface to the driver.
>
> The `serdev::Device` abstraction represents a `struct serdev_device`.
>
> In order to provide the Serdev specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is
> implemented by `serdev::Adapter`.
>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
>  MAINTAINERS                     |   2 +
>  drivers/tty/serdev/Kconfig      |   7 +
>  rust/bindings/bindings_helper.h |   1 +
>  rust/helpers/helpers.c          |   1 +
>  rust/helpers/serdev.c           |  22 ++
>  rust/kernel/lib.rs              |   2 +
>  rust/kernel/serdev.rs           | 599 ++++++++++++++++++++++++++++++++++++++++
>  7 files changed, 634 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 806bd2d80d15..cd735d227892 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24568,6 +24568,8 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/serial/serial.yaml
>  F:	drivers/tty/serdev/
>  F:	include/linux/serdev.h
> +F:	rust/helpers/serdev.c
> +F:	rust/kernel/serdev.rs
>  
>  SERIAL IR RECEIVER
>  M:	Sean Young <sean@mess.org>
> diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
> index 46ae732bfc68..e6dfe949ad01 100644
> --- a/drivers/tty/serdev/Kconfig
> +++ b/drivers/tty/serdev/Kconfig
> @@ -9,6 +9,13 @@ menuconfig SERIAL_DEV_BUS
>  
>  	  Note that you typically also want to enable TTY port controller support.
>  
> +config RUST_SERIAL_DEV_BUS_ABSTRACTIONS
> +	bool "Rust Serial device bus abstractions"
> +	depends on RUST
> +	select SERIAL_DEV_BUS
> +	help
> +	  This enables the Rust abstraction for the serial device bus API.
> +
>  if SERIAL_DEV_BUS
>  
>  config SERIAL_DEV_CTRL_TTYPORT
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 1124785e210b..fe7c505da236 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -85,6 +85,7 @@
>  #include <linux/regulator/consumer.h>
>  #include <linux/sched.h>
>  #include <linux/security.h>
> +#include <linux/serdev.h>
>  #include <linux/slab.h>
>  #include <linux/sys_soc.h>
>  #include <linux/task_work.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 998e31052e66..6921a5ecb78a 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -87,6 +87,7 @@
>  #include "regulator.c"
>  #include "scatterlist.c"
>  #include "security.c"
> +#include "serdev.c"
>  #include "signal.c"
>  #include "slab.c"
>  #include "spinlock.c"
> diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
> new file mode 100644
> index 000000000000..c52b78ca3fc7
> --- /dev/null
> +++ b/rust/helpers/serdev.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/serdev.h>
> +
> +__rust_helper
> +void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
> +{
> +	serdev_device_driver_unregister(sdrv);
> +}
> +
> +__rust_helper
> +void rust_helper_serdev_device_put(struct serdev_device *serdev)
> +{
> +	serdev_device_put(serdev);
> +}
> +
> +__rust_helper
> +void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
> +					      const struct serdev_device_ops *ops)
> +{
> +	serdev_device_set_client_ops(serdev, ops);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 9512af7156df..d5ca17336739 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -119,6 +119,8 @@
>  pub mod scatterlist;
>  pub mod security;
>  pub mod seq_file;
> +#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
> +pub mod serdev;
>  pub mod sizes;
>  #[cfg(CONFIG_SOC_BUS)]
>  pub mod soc;
> diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
> new file mode 100644
> index 000000000000..0ffcef1849d2
> --- /dev/null
> +++ b/rust/kernel/serdev.rs
> @@ -0,0 +1,599 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Abstractions for the serial device bus.
> +//!
> +//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
> +
> +use crate::{
> +    acpi,
> +    device,
> +    driver,
> +    error::{
> +        from_result,
> +        to_result,
> +        VTABLE_DEFAULT_ERROR, //
> +    },
> +    new_mutex,
> +    of,
> +    prelude::*,
> +    sync::{
> +        aref::AlwaysRefCounted,
> +        Mutex, //
> +    },
> +    time::{
> +        msecs_to_jiffies,
> +        Jiffies,
> +        Msecs, //
> +    },
> +    types::{
> +        Opaque,
> +        ScopeGuard, //
> +    }, //
> +};
> +
> +use core::{
> +    cell::UnsafeCell,
> +    marker::PhantomData,
> +    mem::{offset_of, MaybeUninit},
> +    num::NonZero,
> +    ptr::NonNull, //
> +};
> +
> +/// Parity bit to use with a serial device.
> +#[repr(u32)]
> +pub enum Parity {
> +    /// No parity bit.
> +    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
> +    /// Even partiy.
> +    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
> +    /// Odd parity.
> +    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
> +}
> +
> +/// Timeout in Jiffies.
> +pub enum Timeout {
> +    /// Wait for a specific amount of [`Jiffies`].
> +    Jiffies(NonZero<Jiffies>),
> +    /// Wait for a specific amount of [`Msecs`].
> +    Milliseconds(NonZero<Msecs>),
> +    /// Wait as long as possible.
> +    ///
> +    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
> +    Max,
> +}
> +
> +impl Timeout {
> +    fn into_jiffies(self) -> isize {
> +        match self {
> +            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
> +            Self::Milliseconds(value) => {
> +                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
> +            }
> +            Self::Max => 0,
> +        }
> +    }
> +}

FWIW this shouldn't be part of serdev abstraction. See
https://lore.kernel.org/rust-for-linux/20260717042247.3634961-1-tomo@flapping.org

> +
> +/// An adapter for the registration of serial device bus device drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +// SAFETY:
> +// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
> +// - `PrivateData<'bound, T>` is the type of the driver's device private data.
> +// - `struct serdev_device_driver` embeds a `struct device_driver`.
> +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
> +unsafe impl<T: Driver> driver::DriverLayout for Adapter<T> {
> +    type DriverType = bindings::serdev_device_driver;
> +    type DriverData<'bound> = PrivateData<'bound, T>;
> +    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
> +}
> +
> +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
> +// a preceding call to `register` has been successful.
> +unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
> +    unsafe fn register(
> +        sdrv: &Opaque<Self::DriverType>,
> +        name: &'static CStr,
> +        module: &'static ThisModule,
> +    ) -> Result {
> +        let of_table = match T::OF_ID_TABLE {
> +            Some(table) => table.as_ptr(),
> +            None => core::ptr::null(),
> +        };
> +
> +        let acpi_table = match T::ACPI_ID_TABLE {
> +            Some(table) => table.as_ptr(),
> +            None => core::ptr::null(),
> +        };
> +
> +        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
> +        unsafe {
> +            (*sdrv.get()).driver.name = name.as_char_ptr();
> +            (*sdrv.get()).probe = Some(Self::probe_callback);
> +            (*sdrv.get()).remove = Some(Self::remove_callback);
> +            (*sdrv.get()).driver.of_match_table = of_table;
> +            (*sdrv.get()).driver.acpi_match_table = acpi_table;
> +        }
> +
> +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> +        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
> +    }
> +
> +    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
> +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> +        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
> +    }
> +}
> +
> +#[doc(hidden)]
> +#[pin_data(PinnedDrop)]
> +pub struct PrivateData<'bound, T: Driver> {
> +    sdev: &'bound Device<device::Bound>,
> +    #[pin]
> +    driver: UnsafeCell<MaybeUninit<T::Data<'bound>>>,
> +    open: UnsafeCell<bool>,
> +    #[pin]
> +    active: Mutex<bool>,
> +}
> +
> +#[pinned_drop]
> +impl<T: Driver> PinnedDrop for PrivateData<'_, T> {
> +    fn drop(self: Pin<&mut Self>) {
> +        let mut active = self.active.lock();
> +        if *active {
> +            // SAFETY:
> +            // - We have exclusive access to `self.driver`.
> +            // - `self.driver` is guaranteed to be initialized.
> +            unsafe { (*self.driver.get()).assume_init_drop() };
> +            *active = false;
> +        }
> +        drop(active);
> +
> +        // SAFETY: We have exclusive access to `self.open`.
> +        if unsafe { *self.open.get() } {
> +            // SAFETY: `self.sdev.as_raw()` is guaranteed to be a pointer to a valid
> +            // `struct serdev_device`.
> +            unsafe { bindings::serdev_device_close(self.sdev.as_raw()) };
> +        }
> +    }
> +}
> +
> +impl<T: Driver> Adapter<T> {
> +    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
> +        receive_buf: if T::HAS_RECEIVE {
> +            Some(Self::receive_buf_callback)
> +        } else {
> +            None
> +        },
> +        write_wakeup: Some(bindings::serdev_device_write_wakeup),
> +    };
> +
> +    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
> +        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
> +        // a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
> +        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
> +
> +        from_result(|| {
> +            sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
> +                sdev: &**sdev,
> +                driver: MaybeUninit::<T::Data<'_>>::zeroed().into(),
> +                open: false.into(),
> +                active <- new_mutex!(false),
> +            }))?;
> +            // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> +            let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> +            let private_data = ScopeGuard::new_with_data(private_data, |_| {
> +                // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> +                drop(unsafe { sdev.as_ref().drvdata_obtain::<PrivateData<'_, T>>() });
> +            });
> +            let mut active = private_data.active.lock();

What's this for? This gives me a strong vibe that something is off. Either the
serdev subsystem is not having sufficient synchronization or there is an
abstraction issue.

Either way, you really should have more comments about what this active mutex is
for and why it is needed.

> +
> +            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> +            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
> +
> +            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
> +            // to a `serdev_device`.
> +            to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?;
> +
> +            // SAFETY: We have exclusive access to `private_data.open`.
> +            unsafe { *private_data.open.get() = true };
> +
> +            let data = T::probe(sdev, info);
> +
> +            // SAFETY: We have exclusive access to `private_data.driver`.
> +            let driver = unsafe { &mut *private_data.driver.get() };
> +            // SAFETY:
> +            // - `driver.as_mut_ptr()` is a valid pointer to uninitialized data.
> +            // - `private_data.driver` is pinned.
> +            let result = unsafe { data.__pinned_init(driver.as_mut_ptr()) };
> +
> +            *active = result.is_ok();
> +
> +            drop(active);
> +
> +            result.map(|()| {
> +                private_data.dismiss();
> +                0
> +            })
> +        })
> +    }
> +
> +    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
> +        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
> +        // to a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
> +
> +        // SAFETY: `remove_callback` is only ever called after a successful call to
> +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> +        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
> +        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> +
> +        // SAFETY: No one has exclusive access to `private_data.driver`.
> +        let data = unsafe { &*private_data.driver.get() };
> +        // SAFETY:
> +        // - `private_data.driver` is pinned.
> +        // - `remove_callback` is only ever called after a successful call to `probe_callback`,
> +        //   hence it's guaranteed that `private_data.driver` was initialized.
> +        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
> +
> +        T::unbind(sdev, data_pinned);
> +    }
> +
> +    extern "C" fn receive_buf_callback(
> +        sdev: *mut bindings::serdev_device,
> +        buf: *const u8,
> +        length: usize,
> +    ) -> usize {
> +        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
> +        // pointer to a `struct serdev_device`.
> +        //
> +        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
> +        let sdev = unsafe { &*sdev.cast::<Device<device::BoundInternal>>() };
> +
> +        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
> +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> +        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
> +        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> +        let active = private_data.active.lock();
> +
> +        if !*active {
> +            return length;
> +        }
> +
> +        // SAFETY: No one has exclusive access to `private_data.driver`.
> +        let data = unsafe { &*private_data.driver.get() };
> +        // SAFETY:
> +        // - `private_data.driver` is pinned.
> +        // - `receive_buf_callback` is only ever called after a successful call to `probe_callback`,
> +        //   hence it's guaranteed that `private_data.driver` was initialized.
> +        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
> +
> +        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
> +        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
> +
> +        T::receive(sdev, data_pinned, buf)
> +    }
> +}
> +
> +impl<T: Driver> driver::Adapter for Adapter<T> {
> +    type IdInfo = T::IdInfo;
> +
> +    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
> +        T::OF_ID_TABLE
> +    }
> +
> +    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
> +        T::ACPI_ID_TABLE
> +    }
> +}
> +
> +/// Declares a kernel module that exposes a single serial device bus device driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_serdev_device_driver! {
> +///     type: MyDriver,
> +///     name: "Module name",
> +///     authors: ["Author name"],
> +///     description: "Description",
> +///     license: "GPL v2",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_serdev_device_driver {
> +    ($($f:tt)*) => {
> +        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
> +    };
> +}
> +
> +/// The serial device bus device driver trait.
> +///
> +/// Drivers must implement this trait in order to get a serial device bus device driver registered.
> +///
> +/// # Examples
> +///
> +///```
> +/// # use kernel::{
> +///     acpi,
> +///     bindings,
> +///     device::{
> +///         Bound,
> +///         Core, //
> +///     },
> +///     of,
> +///     serdev, //
> +/// };
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +///     OF_TABLE,
> +///     MODULE_OF_TABLE,
> +///     <MyDriver as serdev::Driver>::IdInfo,
> +///     [
> +///         (of::DeviceId::new(c"test,device"), ())
> +///     ]
> +/// );
> +///
> +/// kernel::acpi_device_table!(
> +///     ACPI_TABLE,
> +///     MODULE_ACPI_TABLE,
> +///     <MyDriver as serdev::Driver>::IdInfo,
> +///     [
> +///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
> +///     ]
> +/// );
> +///
> +/// #[vtable]
> +/// impl serdev::Driver for MyDriver {
> +///     type IdInfo = ();
> +///     type Data<'bound> = Self;
> +///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
> +///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
> +///
> +///     fn probe<'bound>(
> +///         sdev: &'bound serdev::Device<Core<'_>>,
> +///         _id_info: Option<&'bound Self::IdInfo>,
> +///     ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
> +///         sdev.set_baudrate(115200);
> +///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
> +///         Ok(MyDriver)
> +///     }
> +/// }
> +///```
> +#[vtable]
> +pub trait Driver {
> +    /// The type holding driver private data about each device id supported by the driver.
> +    // TODO: Use associated_type_defaults once stabilized:
> +    //
> +    // ```
> +    // type IdInfo: 'static = ();
> +    // ```
> +    type IdInfo: 'static;
> +
> +    /// The type of the driver's bus device private data.
> +    type Data<'bound>: Send + Sync + 'bound;
> +
> +    /// The table of OF device ids supported by the driver.
> +    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
> +
> +    /// The table of ACPI device ids supported by the driver.
> +    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
> +
> +    /// Serial device bus device driver probe.
> +    ///
> +    /// Called when a new serial device bus device is added or discovered.
> +    /// Implementers should attempt to initialize the device here.
> +    fn probe<'bound>(
> +        sdev: &'bound Device<device::Core<'_>>,
> +        id_info: Option<&'bound Self::IdInfo>,
> +    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound;
> +
> +    /// Serial device bus device driver unbind.
> +    ///
> +    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
> +    /// is optional.
> +    ///
> +    /// This callback serves as a place for drivers to perform teardown operations that require a
> +    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
> +    ///
> +    /// Otherwise, release operations for driver resources should be performed in `Drop`.
> +    fn unbind<'bound>(sdev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
> +        let _ = (sdev, this);
> +    }
> +
> +    /// Serial device bus device data receive callback.
> +    ///
> +    /// Called when data got received from device.
> +    ///
> +    /// Returns the number of bytes accepted.
> +    fn receive<'bound>(
> +        sdev: &'bound Device<device::Bound>,
> +        this: Pin<&Self::Data<'bound>>,
> +        data: &[u8],
> +    ) -> usize {
> +        let _ = (sdev, this, data);
> +        build_error!(VTABLE_DEFAULT_ERROR)
> +    }
> +}
> +
> +/// The serial device bus device representation.
> +///
> +/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
> +/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
> +/// code that we get passed from the C side.
> +///
> +/// # Invariants
> +///
> +/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
> +/// the kernel.
> +#[repr(transparent)]
> +pub struct Device<Ctx: device::DeviceContext = device::Normal>(
> +    Opaque<bindings::serdev_device>,
> +    PhantomData<Ctx>,
> +);
> +
> +impl<Ctx: device::DeviceContext> Device<Ctx> {
> +    fn as_raw(&self) -> *mut bindings::serdev_device {
> +        self.0.get()
> +    }

This and many other functions should be `#[inline]`. I am surprised that Sashiko
didn't point out.

Best,
Gary

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 10:55 ` [PATCH v14 0/3] rust: add basic serial device bus abstractions Greg Kroah-Hartman
  2026-07-17 11:14   ` Markus Probst
@ 2026-07-17 11:19   ` Danilo Krummrich
  1 sibling, 0 replies; 17+ messages in thread
From: Danilo Krummrich @ 2026-07-17 11:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On 7/17/26 12:55 PM, Greg Kroah-Hartman wrote:
> On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
>> This patch series adds the serdev device bus rust abstraction into the
>> kernel.
>>
>> This abstraction will be used by a driver,
>> which targets the MCU devices in Synology devices.
>>
>> Kari Argillander also messaged me, stating that he wants to write a
>> watchdog driver with this abstraction (needing initial device data).
>>
>> This series depends on [1].
>>
>> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
>>
>> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> 
> Given there are no objections, I'll go queue this up now, thanks.
You'll need the patches from [1] as well.

Thanks,
Danilo

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 11:14   ` Markus Probst
@ 2026-07-17 11:22     ` Danilo Krummrich
  2026-07-17 12:03       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 17+ messages in thread
From: Danilo Krummrich @ 2026-07-17 11:22 UTC (permalink / raw)
  To: Markus Probst
  Cc: Greg Kroah-Hartman, Rob Herring, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Kari Argillander, Rafael J. Wysocki,
	Viresh Kumar, Boqun Feng, David Airlie, Simona Vetter,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, linux-serial, linux-kernel, rust-for-linux,
	linux-pm, driver-core, dri-devel

On 7/17/26 1:14 PM, Markus Probst wrote:
> On Fri, 2026-07-17 at 12:55 +0200, Greg Kroah-Hartman wrote:
>> On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
>>> This patch series adds the serdev device bus rust abstraction into the
>>> kernel.
>>>
>>> This abstraction will be used by a driver,
>>> which targets the MCU devices in Synology devices.
>>>
>>> Kari Argillander also messaged me, stating that he wants to write a
>>> watchdog driver with this abstraction (needing initial device data).
>>>
>>> This series depends on [1].
>>>
>>> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
>>>
>>> Signed-off-by: Markus Probst <markus.probst@posteo.de>
>>
>> Given there are no objections, I'll go queue this up now, thanks.
> Thanks for merging, but I haven't seen the dependency
> 
> https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> 
> getting picked yet (its at least not in linux-next), so it might not
> even compile. Not sure whats holding the patch back though.
(Ah, saw this just after I replied to the other thread.)

Nothing is holding them back other than requiring a user (which is your work),
so I did not pick them up.

Also note that tty and driver-core are different trees, so picking it up in
driver-core would not have helped tty.

Thanks,
Danilo

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 1/3] rust: add basic serial device bus abstractions
  2026-07-17 11:15   ` Gary Guo
@ 2026-07-17 11:27     ` Markus Probst
  0 siblings, 0 replies; 17+ messages in thread
From: Markus Probst @ 2026-07-17 11:27 UTC (permalink / raw)
  To: Gary Guo, Rob Herring, Greg Kroah-Hartman, Jiri Slaby,
	Miguel Ojeda, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Kari Argillander, Rafael J. Wysocki, Viresh Kumar, Boqun Feng,
	David Airlie, Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

[-- Attachment #1: Type: text/plain, Size: 23579 bytes --]

On Fri, 2026-07-17 at 12:15 +0100, Gary Guo wrote:
> On Wed Jul 15, 2026 at 10:00 PM BST, Markus Probst wrote:
> > Implement the basic serial device bus abstractions required to write a
> > serial device bus device driver with or without the need for initial device
> > data. This includes the following data structures:
> > 
> > The `serdev::Driver` trait represents the interface to the driver.
> > 
> > The `serdev::Device` abstraction represents a `struct serdev_device`.
> > 
> > In order to provide the Serdev specific parts to a generic
> > `driver::Registration` the `driver::RegistrationOps` trait is
> > implemented by `serdev::Adapter`.
> > 
> > Signed-off-by: Markus Probst <markus.probst@posteo.de>
> > ---
> >  MAINTAINERS                     |   2 +
> >  drivers/tty/serdev/Kconfig      |   7 +
> >  rust/bindings/bindings_helper.h |   1 +
> >  rust/helpers/helpers.c          |   1 +
> >  rust/helpers/serdev.c           |  22 ++
> >  rust/kernel/lib.rs              |   2 +
> >  rust/kernel/serdev.rs           | 599 ++++++++++++++++++++++++++++++++++++++++
> >  7 files changed, 634 insertions(+)
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 806bd2d80d15..cd735d227892 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -24568,6 +24568,8 @@ S:	Maintained
> >  F:	Documentation/devicetree/bindings/serial/serial.yaml
> >  F:	drivers/tty/serdev/
> >  F:	include/linux/serdev.h
> > +F:	rust/helpers/serdev.c
> > +F:	rust/kernel/serdev.rs
> >  
> >  SERIAL IR RECEIVER
> >  M:	Sean Young <sean@mess.org>
> > diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
> > index 46ae732bfc68..e6dfe949ad01 100644
> > --- a/drivers/tty/serdev/Kconfig
> > +++ b/drivers/tty/serdev/Kconfig
> > @@ -9,6 +9,13 @@ menuconfig SERIAL_DEV_BUS
> >  
> >  	  Note that you typically also want to enable TTY port controller support.
> >  
> > +config RUST_SERIAL_DEV_BUS_ABSTRACTIONS
> > +	bool "Rust Serial device bus abstractions"
> > +	depends on RUST
> > +	select SERIAL_DEV_BUS
> > +	help
> > +	  This enables the Rust abstraction for the serial device bus API.
> > +
> >  if SERIAL_DEV_BUS
> >  
> >  config SERIAL_DEV_CTRL_TTYPORT
> > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> > index 1124785e210b..fe7c505da236 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -85,6 +85,7 @@
> >  #include <linux/regulator/consumer.h>
> >  #include <linux/sched.h>
> >  #include <linux/security.h>
> > +#include <linux/serdev.h>
> >  #include <linux/slab.h>
> >  #include <linux/sys_soc.h>
> >  #include <linux/task_work.h>
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index 998e31052e66..6921a5ecb78a 100644
> > --- a/rust/helpers/helpers.c
> > +++ b/rust/helpers/helpers.c
> > @@ -87,6 +87,7 @@
> >  #include "regulator.c"
> >  #include "scatterlist.c"
> >  #include "security.c"
> > +#include "serdev.c"
> >  #include "signal.c"
> >  #include "slab.c"
> >  #include "spinlock.c"
> > diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
> > new file mode 100644
> > index 000000000000..c52b78ca3fc7
> > --- /dev/null
> > +++ b/rust/helpers/serdev.c
> > @@ -0,0 +1,22 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <linux/serdev.h>
> > +
> > +__rust_helper
> > +void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
> > +{
> > +	serdev_device_driver_unregister(sdrv);
> > +}
> > +
> > +__rust_helper
> > +void rust_helper_serdev_device_put(struct serdev_device *serdev)
> > +{
> > +	serdev_device_put(serdev);
> > +}
> > +
> > +__rust_helper
> > +void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
> > +					      const struct serdev_device_ops *ops)
> > +{
> > +	serdev_device_set_client_ops(serdev, ops);
> > +}
> > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > index 9512af7156df..d5ca17336739 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -119,6 +119,8 @@
> >  pub mod scatterlist;
> >  pub mod security;
> >  pub mod seq_file;
> > +#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
> > +pub mod serdev;
> >  pub mod sizes;
> >  #[cfg(CONFIG_SOC_BUS)]
> >  pub mod soc;
> > diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
> > new file mode 100644
> > index 000000000000..0ffcef1849d2
> > --- /dev/null
> > +++ b/rust/kernel/serdev.rs
> > @@ -0,0 +1,599 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Abstractions for the serial device bus.
> > +//!
> > +//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
> > +
> > +use crate::{
> > +    acpi,
> > +    device,
> > +    driver,
> > +    error::{
> > +        from_result,
> > +        to_result,
> > +        VTABLE_DEFAULT_ERROR, //
> > +    },
> > +    new_mutex,
> > +    of,
> > +    prelude::*,
> > +    sync::{
> > +        aref::AlwaysRefCounted,
> > +        Mutex, //
> > +    },
> > +    time::{
> > +        msecs_to_jiffies,
> > +        Jiffies,
> > +        Msecs, //
> > +    },
> > +    types::{
> > +        Opaque,
> > +        ScopeGuard, //
> > +    }, //
> > +};
> > +
> > +use core::{
> > +    cell::UnsafeCell,
> > +    marker::PhantomData,
> > +    mem::{offset_of, MaybeUninit},
> > +    num::NonZero,
> > +    ptr::NonNull, //
> > +};
> > +
> > +/// Parity bit to use with a serial device.
> > +#[repr(u32)]
> > +pub enum Parity {
> > +    /// No parity bit.
> > +    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
> > +    /// Even partiy.
> > +    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
> > +    /// Odd parity.
> > +    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
> > +}
> > +
> > +/// Timeout in Jiffies.
> > +pub enum Timeout {
> > +    /// Wait for a specific amount of [`Jiffies`].
> > +    Jiffies(NonZero<Jiffies>),
> > +    /// Wait for a specific amount of [`Msecs`].
> > +    Milliseconds(NonZero<Msecs>),
> > +    /// Wait as long as possible.
> > +    ///
> > +    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
> > +    Max,
> > +}
> > +
> > +impl Timeout {
> > +    fn into_jiffies(self) -> isize {
> > +        match self {
> > +            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
> > +            Self::Milliseconds(value) => {
> > +                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
> > +            }
> > +            Self::Max => 0,
> > +        }
> > +    }
> > +}
> 
> FWIW this shouldn't be part of serdev abstraction. See
> https://lore.kernel.org/rust-for-linux/20260717042247.3634961-1-tomo@flapping.org
> 
> > +
> > +/// An adapter for the registration of serial device bus device drivers.
> > +pub struct Adapter<T: Driver>(T);
> > +
> > +// SAFETY:
> > +// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
> > +// - `PrivateData<'bound, T>` is the type of the driver's device private data.
> > +// - `struct serdev_device_driver` embeds a `struct device_driver`.
> > +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
> > +unsafe impl<T: Driver> driver::DriverLayout for Adapter<T> {
> > +    type DriverType = bindings::serdev_device_driver;
> > +    type DriverData<'bound> = PrivateData<'bound, T>;
> > +    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
> > +}
> > +
> > +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
> > +// a preceding call to `register` has been successful.
> > +unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
> > +    unsafe fn register(
> > +        sdrv: &Opaque<Self::DriverType>,
> > +        name: &'static CStr,
> > +        module: &'static ThisModule,
> > +    ) -> Result {
> > +        let of_table = match T::OF_ID_TABLE {
> > +            Some(table) => table.as_ptr(),
> > +            None => core::ptr::null(),
> > +        };
> > +
> > +        let acpi_table = match T::ACPI_ID_TABLE {
> > +            Some(table) => table.as_ptr(),
> > +            None => core::ptr::null(),
> > +        };
> > +
> > +        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
> > +        unsafe {
> > +            (*sdrv.get()).driver.name = name.as_char_ptr();
> > +            (*sdrv.get()).probe = Some(Self::probe_callback);
> > +            (*sdrv.get()).remove = Some(Self::remove_callback);
> > +            (*sdrv.get()).driver.of_match_table = of_table;
> > +            (*sdrv.get()).driver.acpi_match_table = acpi_table;
> > +        }
> > +
> > +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> > +        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
> > +    }
> > +
> > +    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
> > +        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
> > +        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
> > +    }
> > +}
> > +
> > +#[doc(hidden)]
> > +#[pin_data(PinnedDrop)]
> > +pub struct PrivateData<'bound, T: Driver> {
> > +    sdev: &'bound Device<device::Bound>,
> > +    #[pin]
> > +    driver: UnsafeCell<MaybeUninit<T::Data<'bound>>>,
> > +    open: UnsafeCell<bool>,
> > +    #[pin]
> > +    active: Mutex<bool>,
> > +}
> > +
> > +#[pinned_drop]
> > +impl<T: Driver> PinnedDrop for PrivateData<'_, T> {
> > +    fn drop(self: Pin<&mut Self>) {
> > +        let mut active = self.active.lock();
> > +        if *active {
> > +            // SAFETY:
> > +            // - We have exclusive access to `self.driver`.
> > +            // - `self.driver` is guaranteed to be initialized.
> > +            unsafe { (*self.driver.get()).assume_init_drop() };
> > +            *active = false;
> > +        }
> > +        drop(active);
> > +
> > +        // SAFETY: We have exclusive access to `self.open`.
> > +        if unsafe { *self.open.get() } {
> > +            // SAFETY: `self.sdev.as_raw()` is guaranteed to be a pointer to a valid
> > +            // `struct serdev_device`.
> > +            unsafe { bindings::serdev_device_close(self.sdev.as_raw()) };
> > +        }
> > +    }
> > +}
> > +
> > +impl<T: Driver> Adapter<T> {
> > +    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
> > +        receive_buf: if T::HAS_RECEIVE {
> > +            Some(Self::receive_buf_callback)
> > +        } else {
> > +            None
> > +        },
> > +        write_wakeup: Some(bindings::serdev_device_write_wakeup),
> > +    };
> > +
> > +    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
> > +        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
> > +        // a `struct serdev_device`.
> > +        //
> > +        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
> > +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
> > +        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
> > +
> > +        from_result(|| {
> > +            sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
> > +                sdev: &**sdev,
> > +                driver: MaybeUninit::<T::Data<'_>>::zeroed().into(),
> > +                open: false.into(),
> > +                active <- new_mutex!(false),
> > +            }))?;
> > +            // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> > +            let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> > +            let private_data = ScopeGuard::new_with_data(private_data, |_| {
> > +                // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> > +                drop(unsafe { sdev.as_ref().drvdata_obtain::<PrivateData<'_, T>>() });
> > +            });
> > +            let mut active = private_data.active.lock();
> 
> What's this for? This gives me a strong vibe that something is off. Either the
> serdev subsystem is not having sufficient synchronization or there is an
> abstraction issue.
There is the following issue it trys to solve:

In the rust abstraction, the driver data is only initialized after the
probe. `serdev_device_open` should be called inside or before probe,
because otherwise the driver can't configure the serdev device. But as
a sideeffect, once the serdev device is open, it is possible for
receive_buf_callback to be called, which this mutex prevents while the
driver data is not initialized yet.
> 
> Either way, you really should have more comments about what this active mutex is
> for and why it is needed.
> 
> > +
> > +            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> > +            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
> > +
> > +            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
> > +            // to a `serdev_device`.
> > +            to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?;
> > +
> > +            // SAFETY: We have exclusive access to `private_data.open`.
> > +            unsafe { *private_data.open.get() = true };
> > +
> > +            let data = T::probe(sdev, info);
> > +
> > +            // SAFETY: We have exclusive access to `private_data.driver`.
> > +            let driver = unsafe { &mut *private_data.driver.get() };
> > +            // SAFETY:
> > +            // - `driver.as_mut_ptr()` is a valid pointer to uninitialized data.
> > +            // - `private_data.driver` is pinned.
> > +            let result = unsafe { data.__pinned_init(driver.as_mut_ptr()) };
> > +
> > +            *active = result.is_ok();
> > +
> > +            drop(active);
> > +
> > +            result.map(|()| {
> > +                private_data.dismiss();
> > +                0
> > +            })
> > +        })
> > +    }
> > +
> > +    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
> > +        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
> > +        // to a `struct serdev_device`.
> > +        //
> > +        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
> > +        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
> > +
> > +        // SAFETY: `remove_callback` is only ever called after a successful call to
> > +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> > +        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
> > +        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> > +
> > +        // SAFETY: No one has exclusive access to `private_data.driver`.
> > +        let data = unsafe { &*private_data.driver.get() };
> > +        // SAFETY:
> > +        // - `private_data.driver` is pinned.
> > +        // - `remove_callback` is only ever called after a successful call to `probe_callback`,
> > +        //   hence it's guaranteed that `private_data.driver` was initialized.
> > +        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
> > +
> > +        T::unbind(sdev, data_pinned);
> > +    }
> > +
> > +    extern "C" fn receive_buf_callback(
> > +        sdev: *mut bindings::serdev_device,
> > +        buf: *const u8,
> > +        length: usize,
> > +    ) -> usize {
> > +        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
> > +        // pointer to a `struct serdev_device`.
> > +        //
> > +        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
> > +        let sdev = unsafe { &*sdev.cast::<Device<device::BoundInternal>>() };
> > +
> > +        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
> > +        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
> > +        // and stored a `Pin<KBox<PrivateData<'_, T>>>`.
> > +        let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> > +        let active = private_data.active.lock();
> > +
> > +        if !*active {
> > +            return length;
> > +        }
> > +
> > +        // SAFETY: No one has exclusive access to `private_data.driver`.
> > +        let data = unsafe { &*private_data.driver.get() };
> > +        // SAFETY:
> > +        // - `private_data.driver` is pinned.
> > +        // - `receive_buf_callback` is only ever called after a successful call to `probe_callback`,
> > +        //   hence it's guaranteed that `private_data.driver` was initialized.
> > +        let data_pinned = unsafe { Pin::new_unchecked(data.assume_init_ref()) };
> > +
> > +        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
> > +        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
> > +
> > +        T::receive(sdev, data_pinned, buf)
> > +    }
> > +}
> > +
> > +impl<T: Driver> driver::Adapter for Adapter<T> {
> > +    type IdInfo = T::IdInfo;
> > +
> > +    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
> > +        T::OF_ID_TABLE
> > +    }
> > +
> > +    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
> > +        T::ACPI_ID_TABLE
> > +    }
> > +}
> > +
> > +/// Declares a kernel module that exposes a single serial device bus device driver.
> > +///
> > +/// # Examples
> > +///
> > +/// ```ignore
> > +/// kernel::module_serdev_device_driver! {
> > +///     type: MyDriver,
> > +///     name: "Module name",
> > +///     authors: ["Author name"],
> > +///     description: "Description",
> > +///     license: "GPL v2",
> > +/// }
> > +/// ```
> > +#[macro_export]
> > +macro_rules! module_serdev_device_driver {
> > +    ($($f:tt)*) => {
> > +        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
> > +    };
> > +}
> > +
> > +/// The serial device bus device driver trait.
> > +///
> > +/// Drivers must implement this trait in order to get a serial device bus device driver registered.
> > +///
> > +/// # Examples
> > +///
> > +///```
> > +/// # use kernel::{
> > +///     acpi,
> > +///     bindings,
> > +///     device::{
> > +///         Bound,
> > +///         Core, //
> > +///     },
> > +///     of,
> > +///     serdev, //
> > +/// };
> > +///
> > +/// struct MyDriver;
> > +///
> > +/// kernel::of_device_table!(
> > +///     OF_TABLE,
> > +///     MODULE_OF_TABLE,
> > +///     <MyDriver as serdev::Driver>::IdInfo,
> > +///     [
> > +///         (of::DeviceId::new(c"test,device"), ())
> > +///     ]
> > +/// );
> > +///
> > +/// kernel::acpi_device_table!(
> > +///     ACPI_TABLE,
> > +///     MODULE_ACPI_TABLE,
> > +///     <MyDriver as serdev::Driver>::IdInfo,
> > +///     [
> > +///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
> > +///     ]
> > +/// );
> > +///
> > +/// #[vtable]
> > +/// impl serdev::Driver for MyDriver {
> > +///     type IdInfo = ();
> > +///     type Data<'bound> = Self;
> > +///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
> > +///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
> > +///
> > +///     fn probe<'bound>(
> > +///         sdev: &'bound serdev::Device<Core<'_>>,
> > +///         _id_info: Option<&'bound Self::IdInfo>,
> > +///     ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
> > +///         sdev.set_baudrate(115200);
> > +///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
> > +///         Ok(MyDriver)
> > +///     }
> > +/// }
> > +///```
> > +#[vtable]
> > +pub trait Driver {
> > +    /// The type holding driver private data about each device id supported by the driver.
> > +    // TODO: Use associated_type_defaults once stabilized:
> > +    //
> > +    // ```
> > +    // type IdInfo: 'static = ();
> > +    // ```
> > +    type IdInfo: 'static;
> > +
> > +    /// The type of the driver's bus device private data.
> > +    type Data<'bound>: Send + Sync + 'bound;
> > +
> > +    /// The table of OF device ids supported by the driver.
> > +    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
> > +
> > +    /// The table of ACPI device ids supported by the driver.
> > +    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
> > +
> > +    /// Serial device bus device driver probe.
> > +    ///
> > +    /// Called when a new serial device bus device is added or discovered.
> > +    /// Implementers should attempt to initialize the device here.
> > +    fn probe<'bound>(
> > +        sdev: &'bound Device<device::Core<'_>>,
> > +        id_info: Option<&'bound Self::IdInfo>,
> > +    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound;
> > +
> > +    /// Serial device bus device driver unbind.
> > +    ///
> > +    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
> > +    /// is optional.
> > +    ///
> > +    /// This callback serves as a place for drivers to perform teardown operations that require a
> > +    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
> > +    ///
> > +    /// Otherwise, release operations for driver resources should be performed in `Drop`.
> > +    fn unbind<'bound>(sdev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
> > +        let _ = (sdev, this);
> > +    }
> > +
> > +    /// Serial device bus device data receive callback.
> > +    ///
> > +    /// Called when data got received from device.
> > +    ///
> > +    /// Returns the number of bytes accepted.
> > +    fn receive<'bound>(
> > +        sdev: &'bound Device<device::Bound>,
> > +        this: Pin<&Self::Data<'bound>>,
> > +        data: &[u8],
> > +    ) -> usize {
> > +        let _ = (sdev, this, data);
> > +        build_error!(VTABLE_DEFAULT_ERROR)
> > +    }
> > +}
> > +
> > +/// The serial device bus device representation.
> > +///
> > +/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
> > +/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
> > +/// code that we get passed from the C side.
> > +///
> > +/// # Invariants
> > +///
> > +/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
> > +/// the kernel.
> > +#[repr(transparent)]
> > +pub struct Device<Ctx: device::DeviceContext = device::Normal>(
> > +    Opaque<bindings::serdev_device>,
> > +    PhantomData<Ctx>,
> > +);
> > +
> > +impl<Ctx: device::DeviceContext> Device<Ctx> {
> > +    fn as_raw(&self) -> *mut bindings::serdev_device {
> > +        self.0.get()
> > +    }
> 
> This and many other functions should be `#[inline]`. I am surprised that Sashiko
> didn't point out.
Yes, inline would make sense here. In this particular case, I used
platform.rs as base, so the missing inline got inherited.

Thanks
- Markus Probst

> 
> Best,
> Gary

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 11:22     ` Danilo Krummrich
@ 2026-07-17 12:03       ` Greg Kroah-Hartman
  2026-07-17 12:07         ` Gary Guo
  2026-07-17 12:07         ` Danilo Krummrich
  0 siblings, 2 replies; 17+ messages in thread
From: Greg Kroah-Hartman @ 2026-07-17 12:03 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Fri, Jul 17, 2026 at 01:22:40PM +0200, Danilo Krummrich wrote:
> On 7/17/26 1:14 PM, Markus Probst wrote:
> > On Fri, 2026-07-17 at 12:55 +0200, Greg Kroah-Hartman wrote:
> >> On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
> >>> This patch series adds the serdev device bus rust abstraction into the
> >>> kernel.
> >>>
> >>> This abstraction will be used by a driver,
> >>> which targets the MCU devices in Synology devices.
> >>>
> >>> Kari Argillander also messaged me, stating that he wants to write a
> >>> watchdog driver with this abstraction (needing initial device data).
> >>>
> >>> This series depends on [1].
> >>>
> >>> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> >>>
> >>> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> >>
> >> Given there are no objections, I'll go queue this up now, thanks.
> > Thanks for merging, but I haven't seen the dependency
> > 
> > https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> > 
> > getting picked yet (its at least not in linux-next), so it might not
> > even compile. Not sure whats holding the patch back though.
> (Ah, saw this just after I replied to the other thread.)
> 
> Nothing is holding them back other than requiring a user (which is your work),
> so I did not pick them up.
> 
> Also note that tty and driver-core are different trees, so picking it up in
> driver-core would not have helped tty.

Should I drop this from the tty tree and take it through driver-core
instead?  If so, no problem for me, just let me know.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 12:03       ` Greg Kroah-Hartman
@ 2026-07-17 12:07         ` Gary Guo
  2026-07-17 12:20           ` Danilo Krummrich
  2026-07-17 12:07         ` Danilo Krummrich
  1 sibling, 1 reply; 17+ messages in thread
From: Gary Guo @ 2026-07-17 12:07 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Danilo Krummrich
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Fri Jul 17, 2026 at 1:03 PM BST, Greg Kroah-Hartman wrote:
> On Fri, Jul 17, 2026 at 01:22:40PM +0200, Danilo Krummrich wrote:
>> On 7/17/26 1:14 PM, Markus Probst wrote:
>> > On Fri, 2026-07-17 at 12:55 +0200, Greg Kroah-Hartman wrote:
>> >> On Wed, Jul 15, 2026 at 09:00:14PM +0000, Markus Probst wrote:
>> >>> This patch series adds the serdev device bus rust abstraction into the
>> >>> kernel.
>> >>>
>> >>> This abstraction will be used by a driver,
>> >>> which targets the MCU devices in Synology devices.
>> >>>
>> >>> Kari Argillander also messaged me, stating that he wants to write a
>> >>> watchdog driver with this abstraction (needing initial device data).
>> >>>
>> >>> This series depends on [1].
>> >>>
>> >>> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
>> >>>
>> >>> Signed-off-by: Markus Probst <markus.probst@posteo.de>
>> >>
>> >> Given there are no objections, I'll go queue this up now, thanks.
>> > Thanks for merging, but I haven't seen the dependency
>> > 
>> > https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
>> > 
>> > getting picked yet (its at least not in linux-next), so it might not
>> > even compile. Not sure whats holding the patch back though.
>> (Ah, saw this just after I replied to the other thread.)
>> 
>> Nothing is holding them back other than requiring a user (which is your work),
>> so I did not pick them up.
>> 
>> Also note that tty and driver-core are different trees, so picking it up in
>> driver-core would not have helped tty.
>
> Should I drop this from the tty tree and take it through driver-core
> instead?  If so, no problem for me, just let me know.

FWIW this series will also conflict quite significantly with
https://lore.kernel.org/driver-core/20260629-id_info-v2-0-56fccbe9c5ef@garyguo.net/
which is routed via driver-core tree.

Best,
GAry

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 12:03       ` Greg Kroah-Hartman
  2026-07-17 12:07         ` Gary Guo
@ 2026-07-17 12:07         ` Danilo Krummrich
  2026-07-17 13:12           ` Greg Kroah-Hartman
  1 sibling, 1 reply; 17+ messages in thread
From: Danilo Krummrich @ 2026-07-17 12:07 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Fri Jul 17, 2026 at 2:03 PM CEST, Greg Kroah-Hartman wrote:
> Should I drop this from the tty tree and take it through driver-core
> instead?  If so, no problem for me, just let me know.

Either should work, you can also take [1] through the tty tree, I don't think
there will be any conflicts.

[1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 12:07         ` Gary Guo
@ 2026-07-17 12:20           ` Danilo Krummrich
  2026-07-17 12:44             ` Markus Probst
  0 siblings, 1 reply; 17+ messages in thread
From: Danilo Krummrich @ 2026-07-17 12:20 UTC (permalink / raw)
  To: Gary Guo
  Cc: Greg Kroah-Hartman, Markus Probst, Rob Herring, Jiri Slaby,
	Miguel Ojeda, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, linux-serial, linux-kernel,
	rust-for-linux, linux-pm, driver-core, dri-devel

On Fri Jul 17, 2026 at 2:07 PM CEST, Gary Guo wrote:
> FWIW this series will also conflict quite significantly with
> https://lore.kernel.org/driver-core/20260629-id_info-v2-0-56fccbe9c5ef@garyguo.net/
> which is routed via driver-core tree.

Yeah, but I think it should be trivial (see below)? However, routing serdev
through driver-core does indeed avoid this.

Thanks,
Danilo

diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
index 0ffcef1849d2..a1161dd9f37f 100644
--- a/rust/kernel/serdev.rs
+++ b/rust/kernel/serdev.rs
@@ -174,7 +174,8 @@ extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi:
         //
         // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
         let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
-        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
+        // SAFETY: `sdev` has been matched by the serial device bus via its device ID table.
+        let info = unsafe { <Self as driver::Adapter>::id_info(sdev.as_ref()) };

         from_result(|| {
             sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
@@ -334,7 +335,6 @@ macro_rules! module_serdev_device_driver {
 ///
 /// kernel::of_device_table!(
 ///     OF_TABLE,
-///     MODULE_OF_TABLE,
 ///     <MyDriver as serdev::Driver>::IdInfo,
 ///     [
 ///         (of::DeviceId::new(c"test,device"), ())
@@ -343,7 +343,6 @@ macro_rules! module_serdev_device_driver {
 ///
 /// kernel::acpi_device_table!(
 ///     ACPI_TABLE,
-///     MODULE_ACPI_TABLE,
 ///     <MyDriver as serdev::Driver>::IdInfo,
 ///     [
 ///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
index a12b1412db30..51b4898cd855 100644
--- a/samples/rust/rust_driver_serdev.rs
+++ b/samples/rust/rust_driver_serdev.rs
@@ -20,14 +20,12 @@ struct SampleDriver {

 kernel::of_device_table!(
     OF_TABLE,
-    MODULE_OF_TABLE,
     <SampleDriver as serdev::Driver>::IdInfo,
     [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
 );

 kernel::acpi_device_table!(
     ACPI_TABLE,
-    MODULE_ACPI_TABLE,
     <SampleDriver as serdev::Driver>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
 );

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 12:20           ` Danilo Krummrich
@ 2026-07-17 12:44             ` Markus Probst
  0 siblings, 0 replies; 17+ messages in thread
From: Markus Probst @ 2026-07-17 12:44 UTC (permalink / raw)
  To: Danilo Krummrich, Gary Guo
  Cc: Greg Kroah-Hartman, Rob Herring, Jiri Slaby, Miguel Ojeda,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

[-- Attachment #1: Type: text/plain, Size: 2387 bytes --]

On Fri, 2026-07-17 at 14:20 +0200, Danilo Krummrich wrote:
> diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
> index 0ffcef1849d2..a1161dd9f37f 100644
> --- a/rust/kernel/serdev.rs
> +++ b/rust/kernel/serdev.rs
> @@ -174,7 +174,8 @@ extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi:
>          //
>          // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
>          let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
> -        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
> +        // SAFETY: `sdev` has been matched by the serial device bus via its device ID table.
> +        let info = unsafe { <Self as driver::Adapter>::id_info(sdev.as_ref()) };
> 
>          from_result(|| {
>              sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
> @@ -334,7 +335,6 @@ macro_rules! module_serdev_device_driver {
>  ///
>  /// kernel::of_device_table!(
>  ///     OF_TABLE,
> -///     MODULE_OF_TABLE,
>  ///     <MyDriver as serdev::Driver>::IdInfo,
>  ///     [
>  ///         (of::DeviceId::new(c"test,device"), ())
> @@ -343,7 +343,6 @@ macro_rules! module_serdev_device_driver {
>  ///
>  /// kernel::acpi_device_table!(
>  ///     ACPI_TABLE,
> -///     MODULE_ACPI_TABLE,
>  ///     <MyDriver as serdev::Driver>::IdInfo,
>  ///     [
>  ///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
> diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
> index a12b1412db30..51b4898cd855 100644
> --- a/samples/rust/rust_driver_serdev.rs
> +++ b/samples/rust/rust_driver_serdev.rs
> @@ -20,14 +20,12 @@ struct SampleDriver {
> 
>  kernel::of_device_table!(
>      OF_TABLE,
> -    MODULE_OF_TABLE,
>      <SampleDriver as serdev::Driver>::IdInfo,
>      [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
>  );
> 
>  kernel::acpi_device_table!(
>      ACPI_TABLE,
> -    MODULE_ACPI_TABLE,
>      <SampleDriver as serdev::Driver>::IdInfo,
>      [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
>  );
Not sure if necessary for a conflict resolution, but

Reviewed-by: Markus Probst <markus.probst@posteo.de>

Thanks
- Markus Probst


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 12:07         ` Danilo Krummrich
@ 2026-07-17 13:12           ` Greg Kroah-Hartman
  2026-07-17 13:18             ` Danilo Krummrich
  0 siblings, 1 reply; 17+ messages in thread
From: Greg Kroah-Hartman @ 2026-07-17 13:12 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Fri, Jul 17, 2026 at 02:07:48PM +0200, Danilo Krummrich wrote:
> On Fri Jul 17, 2026 at 2:03 PM CEST, Greg Kroah-Hartman wrote:
> > Should I drop this from the tty tree and take it through driver-core
> > instead?  If so, no problem for me, just let me know.
> 
> Either should work, you can also take [1] through the tty tree, I don't think
> there will be any conflicts.
> 
> [1] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/
> 

Hm, that gets messy.  I'll just add these patches to the driver core
tree, along with the above linked ones, to keep things simple.

thanks,
greg k-h

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v14 0/3] rust: add basic serial device bus abstractions
  2026-07-17 13:12           ` Greg Kroah-Hartman
@ 2026-07-17 13:18             ` Danilo Krummrich
  0 siblings, 0 replies; 17+ messages in thread
From: Danilo Krummrich @ 2026-07-17 13:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Markus Probst, Rob Herring, Jiri Slaby, Miguel Ojeda, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Kari Argillander, Rafael J. Wysocki, Viresh Kumar,
	Boqun Feng, David Airlie, Simona Vetter, Daniel Almeida,
	Tamir Duberstein, Alexandre Courbot, Onur Özkan,
	linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel

On Fri Jul 17, 2026 at 3:12 PM CEST, Greg Kroah-Hartman wrote:
> Hm, that gets messy.  I'll just add these patches to the driver core
> tree, along with the above linked ones, to keep things simple.

Yeah, that's cleaner, thanks!

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-07-17 13:18 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 21:00 [PATCH v14 0/3] rust: add basic serial device bus abstractions Markus Probst
2026-07-15 21:00 ` [PATCH v14 1/3] " Markus Probst
2026-07-17 11:15   ` Gary Guo
2026-07-17 11:27     ` Markus Probst
2026-07-15 21:00 ` [PATCH v14 2/3] samples: rust: add Rust serial device bus sample device driver Markus Probst
2026-07-15 21:00 ` [PATCH v14 3/3] MAINTAINERS: serdev: Add self for serdev Markus Probst
2026-07-17 10:55 ` [PATCH v14 0/3] rust: add basic serial device bus abstractions Greg Kroah-Hartman
2026-07-17 11:14   ` Markus Probst
2026-07-17 11:22     ` Danilo Krummrich
2026-07-17 12:03       ` Greg Kroah-Hartman
2026-07-17 12:07         ` Gary Guo
2026-07-17 12:20           ` Danilo Krummrich
2026-07-17 12:44             ` Markus Probst
2026-07-17 12:07         ` Danilo Krummrich
2026-07-17 13:12           ` Greg Kroah-Hartman
2026-07-17 13:18             ` Danilo Krummrich
2026-07-17 11:19   ` Danilo Krummrich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox