* [PATCH v8 1/5] rust: devres: return reference in `devres::register`
2026-05-30 1:13 [PATCH v8 0/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
@ 2026-05-30 1:13 ` Markus Probst via B4 Relay
2026-05-30 11:54 ` Danilo Krummrich
2026-05-30 1:13 ` [PATCH v8 2/5] serdev: add rust private data to serdev_device Markus Probst via B4 Relay
` (3 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Markus Probst via B4 Relay @ 2026-05-30 1:13 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, Boqun Feng
Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel, Markus Probst
From: Markus Probst <markus.probst@posteo.de>
Return the reference to the initialized data in the `devres::register`
function.
This is needed in a following commit (rust: add basic serial device bus
abstractions).
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
rust/kernel/cpufreq.rs | 3 ++-
rust/kernel/devres.rs | 15 +++++++++++++--
rust/kernel/drm/driver.rs | 3 ++-
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index d94c6cdbc45a..bbbd4f54218a 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -1053,7 +1053,8 @@ pub fn new_foreign_owned(dev: &Device<Bound>) -> Result
where
T: 'static,
{
- devres::register(dev, Self::new()?, GFP_KERNEL)
+ devres::register(dev, Self::new()?, GFP_KERNEL)?;
+ Ok(())
}
}
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 82cbd8b969fb..e3e954478caa 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -419,15 +419,26 @@ fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
/// }
///
/// fn from_bound_context(dev: &Device<Bound>) -> Result {
-/// devres::register(dev, Registration::new(), GFP_KERNEL)
+/// devres::register(dev, Registration::new(), GFP_KERNEL)?;
+/// Ok(())
/// }
/// ```
-pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flags) -> Result
+pub fn register<'a, T, E>(
+ dev: &'a Device<Bound>,
+ data: impl PinInit<T, E>,
+ flags: Flags,
+) -> Result<&'a T>
where
T: Send + 'static,
Error: From<E>,
{
let data = KBox::pin_init(data, flags)?;
+ let data_ptr = &raw const *data;
+
register_foreign(dev, data)
+ // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+ // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+ // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+ .map(|()| unsafe { &*data_ptr })
}
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 5233bdebc9fc..1edfd7bacddb 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -147,7 +147,8 @@ pub fn new_foreign_owned(
let reg = Registration::<T>::new(drm, flags)?;
- devres::register(dev, reg, GFP_KERNEL)
+ devres::register(dev, reg, GFP_KERNEL)?;
+ Ok(())
}
/// Returns a reference to the `Device` instance for this registration.
--
2.53.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 1/5] rust: devres: return reference in `devres::register`
2026-05-30 1:13 ` [PATCH v8 1/5] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-05-30 11:54 ` Danilo Krummrich
2026-05-30 17:37 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 11:54 UTC (permalink / raw)
To: Markus Probst via B4 Relay
Cc: 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, Kari Argillander,
Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
Simona Vetter, linux-serial, linux-kernel, rust-for-linux,
linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> From: Markus Probst <markus.probst@posteo.de>
>
> Return the reference to the initialized data in the `devres::register`
> function.
>
> This is needed in a following commit (rust: add basic serial device bus
> abstractions).
>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
For driver-core and DRM,
Acked-by: Danilo Krummrich <dakr@kernel.org>
This is useful in the context of the HRT work in general, should this series not
make it this cycle, please let me know, then I'm going to pick this through the
driver-core tree.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 1/5] rust: devres: return reference in `devres::register`
2026-05-30 11:54 ` Danilo Krummrich
@ 2026-05-30 17:37 ` Markus Probst
0 siblings, 0 replies; 24+ messages in thread
From: Markus Probst @ 2026-05-30 17:37 UTC (permalink / raw)
To: Danilo Krummrich, Markus Probst via B4 Relay
Cc: Rob Herring, Greg Kroah-Hartman, 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,
linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel
[-- Attachment #1: Type: text/plain, Size: 920 bytes --]
On Sat, 2026-05-30 at 13:54 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> > From: Markus Probst <markus.probst@posteo.de>
> >
> > Return the reference to the initialized data in the `devres::register`
> > function.
> >
> > This is needed in a following commit (rust: add basic serial device bus
> > abstractions).
> >
> > Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> > Signed-off-by: Markus Probst <markus.probst@posteo.de>
>
> For driver-core and DRM,
>
> Acked-by: Danilo Krummrich <dakr@kernel.org>
>
> This is useful in the context of the HRT work in general, should this series not
> make it this cycle, please let me know, then I'm going to pick this through the
> driver-core tree.
As discussed, rust_private_data is not needed anymore thus this patch
has been dropped. Feel free to pick it.
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 2/5] serdev: add rust private data to serdev_device
2026-05-30 1:13 [PATCH v8 0/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 1/5] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-05-30 1:13 ` Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 3/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
` (2 subsequent siblings)
4 siblings, 0 replies; 24+ messages in thread
From: Markus Probst via B4 Relay @ 2026-05-30 1:13 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, Boqun Feng
Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel, Markus Probst
From: Markus Probst <markus.probst@posteo.de>
Add rust private data to `struct serdev_device`, as it is required by the
rust abstraction added in the following commit
(rust: add basic serial device bus abstractions).
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
include/linux/serdev.h | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index b6c3d957ec15..048ef5857786 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -33,12 +33,14 @@ struct serdev_device_ops {
/**
* struct serdev_device - Basic representation of an serdev device
- * @dev: Driver model representation of the device.
- * @nr: Device number on serdev bus.
- * @ctrl: serdev controller managing this device.
- * @ops: Device operations.
- * @write_comp: Completion used by serdev_device_write() internally
- * @write_lock: Lock to serialize access when writing data
+ * @dev: Driver model representation of the device.
+ * @nr: Device number on serdev bus.
+ * @ctrl: serdev controller managing this device.
+ * @ops: Device operations.
+ * @write_comp: Completion used by serdev_device_write() internally
+ * @write_lock: Lock to serialize access when writing data
+ * @rust_private_data: Private data for the rust abstraction. This should
+ * not be used by the C drivers.
*/
struct serdev_device {
struct device dev;
@@ -47,6 +49,7 @@ struct serdev_device {
const struct serdev_device_ops *ops;
struct completion write_comp;
struct mutex write_lock;
+ void *rust_private_data;
};
#define to_serdev_device(d) container_of_const(d, struct serdev_device, dev)
--
2.53.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 1:13 [PATCH v8 0/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 1/5] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 2/5] serdev: add rust private data to serdev_device Markus Probst via B4 Relay
@ 2026-05-30 1:13 ` Markus Probst via B4 Relay
2026-05-30 13:10 ` Danilo Krummrich
2026-05-30 1:13 ` [PATCH v8 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst via B4 Relay
4 siblings, 1 reply; 24+ messages in thread
From: Markus Probst via B4 Relay @ 2026-05-30 1:13 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, Boqun Feng
Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel, Markus Probst
From: Markus Probst <markus.probst@posteo.de>
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 | 548 ++++++++++++++++++++++++++++++++++++++++
7 files changed, 583 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4e118f704699..d2f608ff8ca0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24276,6 +24276,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 446dbeaf0866..4e42635b8607 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -84,6 +84,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 38b34518eff1..2fb8506a748a 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -86,6 +86,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 b72b2fbe046d..83bc2c312241 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -118,6 +118,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..e78dac480533
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,548 @@
+// 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,
+ devres,
+ driver,
+ error::{
+ from_result,
+ to_result,
+ VTABLE_DEFAULT_ERROR, //
+ },
+ of,
+ prelude::*,
+ sync::{
+ aref::AlwaysRefCounted,
+ Completion, //
+ },
+ time::{
+ msecs_to_jiffies,
+ Jiffies,
+ Msecs, //
+ },
+ types::Opaque, //
+};
+
+use core::{
+ cell::UnsafeCell,
+ marker::PhantomData,
+ mem::offset_of,
+ 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)`.
+// - `T::Data` 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> = T::Data<'bound>;
+ 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()) };
+ }
+}
+
+#[pin_data]
+struct PrivateData {
+ #[pin]
+ probe_complete: Completion,
+ error: UnsafeCell<bool>,
+}
+
+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(|| {
+ let private_data = devres::register(
+ sdev.as_ref(),
+ try_pin_init!(PrivateData {
+ probe_complete <- Completion::new(),
+ error: false.into(),
+ }),
+ GFP_KERNEL,
+ )?;
+
+ // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+ unsafe {
+ (*sdev.as_raw()).rust_private_data =
+ (&raw const *private_data).cast::<c_void>().cast_mut()
+ };
+
+ // 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::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
+ })?;
+
+ let data = T::probe(sdev, info);
+ let result = sdev.as_ref().set_drvdata(data);
+
+ // SAFETY: We have exclusive access to `private_data.error`.
+ unsafe { *private_data.error.get() = result.is_err() };
+
+ private_data.probe_complete.complete_all();
+
+ result.map(|()| 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<T::Data<'_>>>`.
+ let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
+
+ T::unbind(sdev, data);
+ }
+
+ 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::CoreInternal<'_>>>() };
+
+ // SAFETY:
+ // - The serial device bus only ever calls the receive buf callback with a valid pointer to
+ // a `struct serdev_device`.
+ // - `receive_buf_callback` is only ever called after a successful call to
+ // `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
+ // to a valid `PrivateData`.
+ let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
+
+ private_data.probe_complete.wait_for_completion();
+
+ // SAFETY: No one has exclusive access to `private_data.error`.
+ if unsafe { *private_data.error.get() } {
+ return length;
+ }
+
+ // 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<T::Data<'_>>>`.
+ let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
+
+ // 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, 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 + '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> {
+ // 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> {
+ // 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.53.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 1:13 ` [PATCH v8 3/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
@ 2026-05-30 13:10 ` Danilo Krummrich
2026-05-30 13:37 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 13:10 UTC (permalink / raw)
To: Markus Probst via B4 Relay
Cc: 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, Kari Argillander,
Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
Simona Vetter, linux-serial, linux-kernel, rust-for-linux,
linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> + 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(|| {
> + let private_data = devres::register(
> + sdev.as_ref(),
> + try_pin_init!(PrivateData {
> + probe_complete <- Completion::new(),
> + error: false.into(),
> + }),
> + GFP_KERNEL,
> + )?;
> +
> + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> + unsafe {
> + (*sdev.as_raw()).rust_private_data =
> + (&raw const *private_data).cast::<c_void>().cast_mut()
> + };
> +
> + // 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::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
> + })?;
The bus device private data drops before devres callbacks run, thus you can't
use the devm helper here. I suggest to use serdev_device_open() and call
serdev_device_close() from remove_callback() instead.
You may also want to consider whether you potentially want T::unbind() to allow
to have concurrent transmits in flight regardless. IOW, you may want to call
serdev_device_close() before T::unbind() anyway.
> +
> + let data = T::probe(sdev, info);
> + let result = sdev.as_ref().set_drvdata(data);
> +
> + // SAFETY: We have exclusive access to `private_data.error`.
> + unsafe { *private_data.error.get() = result.is_err() };
> +
> + private_data.probe_complete.complete_all();
> +
> + result.map(|()| 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<T::Data<'_>>>`.
> + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> +
> + T::unbind(sdev, data);
> + }
> +
> + 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::CoreInternal<'_>>>() };
CoreInternal dereferences to Core, which is technically unsound within this
callback.
I think you want CoreInternal for drvdata_borrow(), so we should add a
BoundInternal type state for this; I will send a patch for this.
With this and the above fixed,
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
from the driver-core side of things.
> + // SAFETY:
> + // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> + // a `struct serdev_device`.
> + // - `receive_buf_callback` is only ever called after a successful call to
> + // `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> + // to a valid `PrivateData`.
> + let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
> +
> + private_data.probe_complete.wait_for_completion();
> +
> + // SAFETY: No one has exclusive access to `private_data.error`.
> + if unsafe { *private_data.error.get() } {
> + return length;
> + }
> +
> + // 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<T::Data<'_>>>`.
> + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> +
> + // 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, buf)
> + }
> +}
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 13:10 ` Danilo Krummrich
@ 2026-05-30 13:37 ` Markus Probst
2026-05-30 13:53 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 13:37 UTC (permalink / raw)
To: Danilo Krummrich, Markus Probst via B4 Relay
Cc: Rob Herring, Greg Kroah-Hartman, 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,
linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel
[-- Attachment #1: Type: text/plain, Size: 6380 bytes --]
On Sat, 2026-05-30 at 15:10 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> > + 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(|| {
> > + let private_data = devres::register(
> > + sdev.as_ref(),
> > + try_pin_init!(PrivateData {
> > + probe_complete <- Completion::new(),
> > + error: false.into(),
> > + }),
> > + GFP_KERNEL,
> > + )?;
> > +
> > + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> > + unsafe {
> > + (*sdev.as_raw()).rust_private_data =
> > + (&raw const *private_data).cast::<c_void>().cast_mut()
> > + };
> > +
> > + // 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::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
> > + })?;
>
> The bus device private data drops before devres callbacks run, thus you can't
> use the devm helper here. I suggest to use serdev_device_open() and call
> serdev_device_close() from remove_callback() instead.
>
> You may also want to consider whether you potentially want T::unbind() to allow
> to have concurrent transmits in flight regardless. IOW, you may want to call
> serdev_device_close() before T::unbind() anyway.
`serdev::Device<Core>` will be passed as argument to unbind, i.e.
set_baudrate, set_flow_control etc. calls are possible. If we close the
serdev device before unbind, calling those would result in a null
pointer dereference. Maybe a driver even wants to send a message here,
so it should stay open.
If I think about it, I can't even close it inside remove_callback after
`T::unbind`. With the driver lifetimes, the driver could store the
`serdev::Device<Bound>` pointer in its DriverData and could still make
calls to the device.
Any suggestions?
>
> > +
> > + let data = T::probe(sdev, info);
> > + let result = sdev.as_ref().set_drvdata(data);
> > +
> > + // SAFETY: We have exclusive access to `private_data.error`.
> > + unsafe { *private_data.error.get() = result.is_err() };
> > +
> > + private_data.probe_complete.complete_all();
> > +
> > + result.map(|()| 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<T::Data<'_>>>`.
> > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > +
> > + T::unbind(sdev, data);
> > + }
> > +
> > + 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::CoreInternal<'_>>>() };
>
> CoreInternal dereferences to Core, which is technically unsound within this
> callback.
>
> I think you want CoreInternal for drvdata_borrow(), so we should add a
> BoundInternal type state for this; I will send a patch for this.
>
> With this and the above fixed,
>
> Reviewed-by: Danilo Krummrich <dakr@kernel.org>
>
> from the driver-core side of things.
>
> > + // SAFETY:
> > + // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> > + // a `struct serdev_device`.
> > + // - `receive_buf_callback` is only ever called after a successful call to
> > + // `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> > + // to a valid `PrivateData`.
> > + let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
> > +
> > + private_data.probe_complete.wait_for_completion();
> > +
> > + // SAFETY: No one has exclusive access to `private_data.error`.
> > + if unsafe { *private_data.error.get() } {
> > + return length;
> > + }
> > +
> > + // 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<T::Data<'_>>>`.
> > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > +
> > + // 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, buf)
> > + }
> > +}
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 13:37 ` Markus Probst
@ 2026-05-30 13:53 ` Markus Probst
2026-05-30 14:00 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 13:53 UTC (permalink / raw)
To: Danilo Krummrich, Markus Probst via B4 Relay
Cc: Rob Herring, Greg Kroah-Hartman, 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,
linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel
[-- Attachment #1: Type: text/plain, Size: 7051 bytes --]
On Sat, 2026-05-30 at 15:37 +0200, Markus Probst wrote:
> On Sat, 2026-05-30 at 15:10 +0200, Danilo Krummrich wrote:
> > On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> > > + 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(|| {
> > > + let private_data = devres::register(
> > > + sdev.as_ref(),
> > > + try_pin_init!(PrivateData {
> > > + probe_complete <- Completion::new(),
> > > + error: false.into(),
> > > + }),
> > > + GFP_KERNEL,
> > > + )?;
> > > +
> > > + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> > > + unsafe {
> > > + (*sdev.as_raw()).rust_private_data =
> > > + (&raw const *private_data).cast::<c_void>().cast_mut()
> > > + };
> > > +
> > > + // 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::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
> > > + })?;
> >
> > The bus device private data drops before devres callbacks run, thus you can't
> > use the devm helper here. I suggest to use serdev_device_open() and call
> > serdev_device_close() from remove_callback() instead.
> >
> > You may also want to consider whether you potentially want T::unbind() to allow
> > to have concurrent transmits in flight regardless. IOW, you may want to call
> > serdev_device_close() before T::unbind() anyway.
> `serdev::Device<Core>` will be passed as argument to unbind, i.e.
> set_baudrate, set_flow_control etc. calls are possible. If we close the
> serdev device before unbind, calling those would result in a null
> pointer dereference. Maybe a driver even wants to send a message here,
> so it should stay open.
>
> If I think about it, I can't even close it inside remove_callback after
> `T::unbind`. With the driver lifetimes, the driver could store the
> `serdev::Device<Bound>` pointer in its DriverData and could still make
> calls to the device.
>
> Any suggestions?
I could keep calling devm, but close it before unbind and reopen it
without the receive ops. The devm will close it then again.
This way it stays open until the last devres callback has run, but
doesn't call the receive_buf_callback while the drvdata has been
dropped.
I will test it and see if it works.
Thanks
- Markus Probst
>
> >
> > > +
> > > + let data = T::probe(sdev, info);
> > > + let result = sdev.as_ref().set_drvdata(data);
> > > +
> > > + // SAFETY: We have exclusive access to `private_data.error`.
> > > + unsafe { *private_data.error.get() = result.is_err() };
> > > +
> > > + private_data.probe_complete.complete_all();
> > > +
> > > + result.map(|()| 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<T::Data<'_>>>`.
> > > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > > +
> > > + T::unbind(sdev, data);
> > > + }
> > > +
> > > + 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::CoreInternal<'_>>>() };
> >
> > CoreInternal dereferences to Core, which is technically unsound within this
> > callback.
> >
> > I think you want CoreInternal for drvdata_borrow(), so we should add a
> > BoundInternal type state for this; I will send a patch for this.
> >
> > With this and the above fixed,
> >
> > Reviewed-by: Danilo Krummrich <dakr@kernel.org>
> >
> > from the driver-core side of things.
> >
> > > + // SAFETY:
> > > + // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> > > + // a `struct serdev_device`.
> > > + // - `receive_buf_callback` is only ever called after a successful call to
> > > + // `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> > > + // to a valid `PrivateData`.
> > > + let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
> > > +
> > > + private_data.probe_complete.wait_for_completion();
> > > +
> > > + // SAFETY: No one has exclusive access to `private_data.error`.
> > > + if unsafe { *private_data.error.get() } {
> > > + return length;
> > > + }
> > > +
> > > + // 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<T::Data<'_>>>`.
> > > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > > +
> > > + // 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, buf)
> > > + }
> > > +}
>
> Thanks
> - Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 13:53 ` Markus Probst
@ 2026-05-30 14:00 ` Markus Probst
2026-05-30 14:08 ` Danilo Krummrich
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 14:00 UTC (permalink / raw)
To: Danilo Krummrich, Markus Probst via B4 Relay
Cc: Rob Herring, Greg Kroah-Hartman, 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,
linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel
[-- Attachment #1: Type: text/plain, Size: 7546 bytes --]
On Sat, 2026-05-30 at 15:53 +0200, Markus Probst wrote:
> On Sat, 2026-05-30 at 15:37 +0200, Markus Probst wrote:
> > On Sat, 2026-05-30 at 15:10 +0200, Danilo Krummrich wrote:
> > > On Sat May 30, 2026 at 3:13 AM CEST, Markus Probst via B4 Relay wrote:
> > > > + 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(|| {
> > > > + let private_data = devres::register(
> > > > + sdev.as_ref(),
> > > > + try_pin_init!(PrivateData {
> > > > + probe_complete <- Completion::new(),
> > > > + error: false.into(),
> > > > + }),
> > > > + GFP_KERNEL,
> > > > + )?;
> > > > +
> > > > + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> > > > + unsafe {
> > > > + (*sdev.as_raw()).rust_private_data =
> > > > + (&raw const *private_data).cast::<c_void>().cast_mut()
> > > > + };
> > > > +
> > > > + // 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::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
> > > > + })?;
> > >
> > > The bus device private data drops before devres callbacks run, thus you can't
> > > use the devm helper here. I suggest to use serdev_device_open() and call
> > > serdev_device_close() from remove_callback() instead.
> > >
> > > You may also want to consider whether you potentially want T::unbind() to allow
> > > to have concurrent transmits in flight regardless. IOW, you may want to call
> > > serdev_device_close() before T::unbind() anyway.
> > `serdev::Device<Core>` will be passed as argument to unbind, i.e.
> > set_baudrate, set_flow_control etc. calls are possible. If we close the
> > serdev device before unbind, calling those would result in a null
> > pointer dereference. Maybe a driver even wants to send a message here,
> > so it should stay open.
> >
> > If I think about it, I can't even close it inside remove_callback after
> > `T::unbind`. With the driver lifetimes, the driver could store the
> > `serdev::Device<Bound>` pointer in its DriverData and could still make
> > calls to the device.
> >
> > Any suggestions?
> I could keep calling devm, but close it before unbind and reopen it
> without the receive ops. The devm will close it then again.
>
> This way it stays open until the last devres callback has run, but
> doesn't call the receive_buf_callback while the drvdata has been
> dropped.
>
> I will test it and see if it works.
Ok, forget it. Only works safely in theory. `serdev_device_open` could
fail and that would be unsound again.
Thanks
- Markus Probst
>
> Thanks
> - Markus Probst
>
> >
> > >
> > > > +
> > > > + let data = T::probe(sdev, info);
> > > > + let result = sdev.as_ref().set_drvdata(data);
> > > > +
> > > > + // SAFETY: We have exclusive access to `private_data.error`.
> > > > + unsafe { *private_data.error.get() = result.is_err() };
> > > > +
> > > > + private_data.probe_complete.complete_all();
> > > > +
> > > > + result.map(|()| 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<T::Data<'_>>>`.
> > > > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > > > +
> > > > + T::unbind(sdev, data);
> > > > + }
> > > > +
> > > > + 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::CoreInternal<'_>>>() };
> > >
> > > CoreInternal dereferences to Core, which is technically unsound within this
> > > callback.
> > >
> > > I think you want CoreInternal for drvdata_borrow(), so we should add a
> > > BoundInternal type state for this; I will send a patch for this.
> > >
> > > With this and the above fixed,
> > >
> > > Reviewed-by: Danilo Krummrich <dakr@kernel.org>
> > >
> > > from the driver-core side of things.
> > >
> > > > + // SAFETY:
> > > > + // - The serial device bus only ever calls the receive buf callback with a valid pointer to
> > > > + // a `struct serdev_device`.
> > > > + // - `receive_buf_callback` is only ever called after a successful call to
> > > > + // `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
> > > > + // to a valid `PrivateData`.
> > > > + let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
> > > > +
> > > > + private_data.probe_complete.wait_for_completion();
> > > > +
> > > > + // SAFETY: No one has exclusive access to `private_data.error`.
> > > > + if unsafe { *private_data.error.get() } {
> > > > + return length;
> > > > + }
> > > > +
> > > > + // 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<T::Data<'_>>>`.
> > > > + let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
> > > > +
> > > > + // 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, buf)
> > > > + }
> > > > +}
> >
> > Thanks
> > - Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 14:00 ` Markus Probst
@ 2026-05-30 14:08 ` Danilo Krummrich
2026-05-30 14:27 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 14:08 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 4:00 PM CEST, Markus Probst wrote:
> On Sat, 2026-05-30 at 15:53 +0200, Markus Probst wrote:
>> On Sat, 2026-05-30 at 15:37 +0200, Markus Probst wrote:
>> > If I think about it, I can't even close it inside remove_callback after
>> > `T::unbind`. With the driver lifetimes, the driver could store the
>> > `serdev::Device<Bound>` pointer in its DriverData and could still make
>> > calls to the device.
>> >
>> > Any suggestions?
Now that there is no public drvdata() accessor anymore, all bus device private
data accesses go through the bus abstraction.
So, you could go back to your original approach and wrap the state in a bus
specific bus device private data wrapper type, which would also get you rid of
the rust_private_data pointer you add to struct serdev_device.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 14:08 ` Danilo Krummrich
@ 2026-05-30 14:27 ` Markus Probst
2026-05-30 14:35 ` Danilo Krummrich
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 14:27 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 1228 bytes --]
On Sat, 2026-05-30 at 16:08 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 4:00 PM CEST, Markus Probst wrote:
> > On Sat, 2026-05-30 at 15:53 +0200, Markus Probst wrote:
> > > On Sat, 2026-05-30 at 15:37 +0200, Markus Probst wrote:
> > > > If I think about it, I can't even close it inside remove_callback after
> > > > `T::unbind`. With the driver lifetimes, the driver could store the
> > > > `serdev::Device<Bound>` pointer in its DriverData and could still make
> > > > calls to the device.
> > > >
> > > > Any suggestions?
>
> Now that there is no public drvdata() accessor anymore, all bus device private
> data accesses go through the bus abstraction.
>
> So, you could go back to your original approach and wrap the state in a bus
> specific bus device private data wrapper type, which would also get you rid of
> the rust_private_data pointer you add to struct serdev_device.
This would work, but I don't see how it would get rid of
rust_private_data in this way. The device private data will
automatically be dropped after the remove_callback, but the private bus
abstraction data needs to outlive the devres callbacks (which is done
by rust_private_data).
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 14:27 ` Markus Probst
@ 2026-05-30 14:35 ` Danilo Krummrich
2026-05-30 14:51 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 14:35 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
> This would work, but I don't see how it would get rid of rust_private_data in
> this way. The device private data will but the private bus abstraction data
> needs to outlive the devres callbacks (which is done by rust_private_data).
Why? You only ever use it in the receive callback, which won't be called after
device unbind anymore.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 14:35 ` Danilo Krummrich
@ 2026-05-30 14:51 ` Markus Probst
2026-05-30 16:14 ` Danilo Krummrich
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 14:51 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 967 bytes --]
On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
> > This would work, but I don't see how it would get rid of rust_private_data in
> > this way. The device private data will but the private bus abstraction data
> > needs to outlive the devres callbacks (which is done by rust_private_data).
>
> Why? You only ever use it in the receive callback, which won't be called after
> device unbind anymore.
Because the possibility exists that some classdev ops or Drop calls
e.g. sdev.set_baudrate. This is a null pointer dereference after
serdev_device_close has been called. I interpreted it to add a state to
the abstraction private data which would prevent those calls (early
return with error) after serdev_device_close has been called, but this
would only work if it outlives these calls.
But it seems you were refering to something else with the state?
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 14:51 ` Markus Probst
@ 2026-05-30 16:14 ` Danilo Krummrich
2026-05-30 16:23 ` Markus Probst
2026-05-30 18:55 ` Markus Probst
0 siblings, 2 replies; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 16:14 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 4:51 PM CEST, Markus Probst wrote:
> On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
>> On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
>> > This would work, but I don't see how it would get rid of rust_private_data in
>> > this way. The device private data will but the private bus abstraction data
>> > needs to outlive the devres callbacks (which is done by rust_private_data).
>>
>> Why? You only ever use it in the receive callback, which won't be called after
>> device unbind anymore.
> Because the possibility exists that some classdev ops or Drop calls
> e.g. sdev.set_baudrate. This is a null pointer dereference after
> serdev_device_close has been called. I interpreted it to add a state to
> the abstraction private data which would prevent those calls (early
> return with error) after serdev_device_close has been called, but this
> would only work if it outlives these calls.
>
> But it seems you were refering to something else with the state?
Yes, but I just notice that I'm a step ahead. With the lifetime model class
device registrations shouldn't be guarded with Devres anymore, but with a
lifetime.
(This also entirely eliminates the need for having different devres stages to
prevent drivers from smuggling a newly created Devres object into an existing
Devres<Registration<T>>'s data, which would be unsound for obvious reasons.)
With this there is no way there are still class device callbacks after the bus
device private data has been dropped in the first place.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 16:14 ` Danilo Krummrich
@ 2026-05-30 16:23 ` Markus Probst
2026-05-30 16:27 ` Danilo Krummrich
2026-05-30 18:55 ` Markus Probst
1 sibling, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 16:23 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 1930 bytes --]
On Sat, 2026-05-30 at 18:14 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 4:51 PM CEST, Markus Probst wrote:
> > On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
> > > On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
> > > > This would work, but I don't see how it would get rid of rust_private_data in
> > > > this way. The device private data will but the private bus abstraction data
> > > > needs to outlive the devres callbacks (which is done by rust_private_data).
> > >
> > > Why? You only ever use it in the receive callback, which won't be called after
> > > device unbind anymore.
> > Because the possibility exists that some classdev ops or Drop calls
> > e.g. sdev.set_baudrate. This is a null pointer dereference after
> > serdev_device_close has been called. I interpreted it to add a state to
> > the abstraction private data which would prevent those calls (early
> > return with error) after serdev_device_close has been called, but this
> > would only work if it outlives these calls.
> >
> > But it seems you were refering to something else with the state?
>
> Yes, but I just notice that I'm a step ahead. With the lifetime model class
> device registrations shouldn't be guarded with Devres anymore, but with a
> lifetime.
>
> (This also entirely eliminates the need for having different devres stages to
> prevent drivers from smuggling a newly created Devres object into an existing
> Devres<Registration<T>>'s data, which would be unsound for obvious reasons.)
>
> With this there is no way there are still class device callbacks after the bus
> device private data has been dropped in the first place.
There would still be Drop, which can access the device.
Drop happens after remove_callback.
Might be simpler for now: I can use a mutex to prevent calls in
receive_buf after remove_callback has been called.
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 16:23 ` Markus Probst
@ 2026-05-30 16:27 ` Danilo Krummrich
2026-05-30 16:30 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 16:27 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 6:23 PM CEST, Markus Probst wrote:
> On Sat, 2026-05-30 at 18:14 +0200, Danilo Krummrich wrote:
>> On Sat May 30, 2026 at 4:51 PM CEST, Markus Probst wrote:
>> > On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
>> > > On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
>> > > > This would work, but I don't see how it would get rid of rust_private_data in
>> > > > this way. The device private data will but the private bus abstraction data
>> > > > needs to outlive the devres callbacks (which is done by rust_private_data).
>> > >
>> > > Why? You only ever use it in the receive callback, which won't be called after
>> > > device unbind anymore.
>> > Because the possibility exists that some classdev ops or Drop calls
>> > e.g. sdev.set_baudrate. This is a null pointer dereference after
>> > serdev_device_close has been called. I interpreted it to add a state to
>> > the abstraction private data which would prevent those calls (early
>> > return with error) after serdev_device_close has been called, but this
>> > would only work if it outlives these calls.
>> >
>> > But it seems you were refering to something else with the state?
>>
>> Yes, but I just notice that I'm a step ahead. With the lifetime model class
>> device registrations shouldn't be guarded with Devres anymore, but with a
>> lifetime.
>>
>> (This also entirely eliminates the need for having different devres stages to
>> prevent drivers from smuggling a newly created Devres object into an existing
>> Devres<Registration<T>>'s data, which would be unsound for obvious reasons.)
>>
>> With this there is no way there are still class device callbacks after the bus
>> device private data has been dropped in the first place.
> There would still be Drop, which can access the device.
> Drop happens after remove_callback.
But you control drop(), so can order it such that first the driver's bus device
private data drops and after this the private data of the bus wrapping it (which
will also call serdev_device_close()).
> Might be simpler for now: I can use a mutex to prevent calls in
> receive_buf after remove_callback has been called.
>
> Thanks
> - Markus Probst
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 16:27 ` Danilo Krummrich
@ 2026-05-30 16:30 ` Markus Probst
0 siblings, 0 replies; 24+ messages in thread
From: Markus Probst @ 2026-05-30 16:30 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 2518 bytes --]
On Sat, 2026-05-30 at 18:27 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 6:23 PM CEST, Markus Probst wrote:
> > On Sat, 2026-05-30 at 18:14 +0200, Danilo Krummrich wrote:
> > > On Sat May 30, 2026 at 4:51 PM CEST, Markus Probst wrote:
> > > > On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
> > > > > On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
> > > > > > This would work, but I don't see how it would get rid of rust_private_data in
> > > > > > this way. The device private data will but the private bus abstraction data
> > > > > > needs to outlive the devres callbacks (which is done by rust_private_data).
> > > > >
> > > > > Why? You only ever use it in the receive callback, which won't be called after
> > > > > device unbind anymore.
> > > > Because the possibility exists that some classdev ops or Drop calls
> > > > e.g. sdev.set_baudrate. This is a null pointer dereference after
> > > > serdev_device_close has been called. I interpreted it to add a state to
> > > > the abstraction private data which would prevent those calls (early
> > > > return with error) after serdev_device_close has been called, but this
> > > > would only work if it outlives these calls.
> > > >
> > > > But it seems you were refering to something else with the state?
> > >
> > > Yes, but I just notice that I'm a step ahead. With the lifetime model class
> > > device registrations shouldn't be guarded with Devres anymore, but with a
> > > lifetime.
> > >
> > > (This also entirely eliminates the need for having different devres stages to
> > > prevent drivers from smuggling a newly created Devres object into an existing
> > > Devres<Registration<T>>'s data, which would be unsound for obvious reasons.)
> > >
> > > With this there is no way there are still class device callbacks after the bus
> > > device private data has been dropped in the first place.
> > There would still be Drop, which can access the device.
> > Drop happens after remove_callback.
>
> But you control drop(), so can order it such that first the driver's bus device
> private data drops and after this the private data of the bus wrapping it (which
> will also call serdev_device_close()).
Yes.
It seems I also have to rebase led then, as it currently uses Devres.
Thanks
- Markus Probst
>
> > Might be simpler for now: I can use a mutex to prevent calls in
> > receive_buf after remove_callback has been called.
> >
> > Thanks
> > - Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 16:14 ` Danilo Krummrich
2026-05-30 16:23 ` Markus Probst
@ 2026-05-30 18:55 ` Markus Probst
2026-05-30 19:45 ` Danilo Krummrich
1 sibling, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 18:55 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 1889 bytes --]
On Sat, 2026-05-30 at 18:14 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 4:51 PM CEST, Markus Probst wrote:
> > On Sat, 2026-05-30 at 16:35 +0200, Danilo Krummrich wrote:
> > > On Sat May 30, 2026 at 4:27 PM CEST, Markus Probst wrote:
> > > > This would work, but I don't see how it would get rid of rust_private_data in
> > > > this way. The device private data will but the private bus abstraction data
> > > > needs to outlive the devres callbacks (which is done by rust_private_data).
> > >
> > > Why? You only ever use it in the receive callback, which won't be called after
> > > device unbind anymore.
> > Because the possibility exists that some classdev ops or Drop calls
> > e.g. sdev.set_baudrate. This is a null pointer dereference after
> > serdev_device_close has been called. I interpreted it to add a state to
> > the abstraction private data which would prevent those calls (early
> > return with error) after serdev_device_close has been called, but this
> > would only work if it outlives these calls.
> >
> > But it seems you were refering to something else with the state?
>
> Yes, but I just notice that I'm a step ahead. With the lifetime model class
> device registrations shouldn't be guarded with Devres anymore, but with a
> lifetime.
That might be an issue. pin-init doesn't have enum support yet, so at
least in my driver devres is still required for now.
I will readd the rust_private_data field.
Thanks
- Markus Probst
>
> (This also entirely eliminates the need for having different devres stages to
> prevent drivers from smuggling a newly created Devres object into an existing
> Devres<Registration<T>>'s data, which would be unsound for obvious reasons.)
>
> With this there is no way there are still class device callbacks after the bus
> device private data has been dropped in the first place.
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 18:55 ` Markus Probst
@ 2026-05-30 19:45 ` Danilo Krummrich
2026-05-30 20:31 ` Markus Probst
0 siblings, 1 reply; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 19:45 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 8:55 PM CEST, Markus Probst wrote:
> pin-init doesn't have enum support yet, so at least in my driver devres is
> still required for now.
Hm...how is this related to having a registration type that takes a lifetime
rather than a registration type being wrapped in Devres?
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 19:45 ` Danilo Krummrich
@ 2026-05-30 20:31 ` Markus Probst
2026-05-30 20:59 ` Danilo Krummrich
0 siblings, 1 reply; 24+ messages in thread
From: Markus Probst @ 2026-05-30 20:31 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
[-- Attachment #1: Type: text/plain, Size: 534 bytes --]
On Sat, 2026-05-30 at 21:45 +0200, Danilo Krummrich wrote:
> On Sat May 30, 2026 at 8:55 PM CEST, Markus Probst wrote:
> > pin-init doesn't have enum support yet, so at least in my driver devres is
> > still required for now.
>
> Hm...how is this related to having a registration type that takes a lifetime
> rather than a registration type being wrapped in Devres?
In order to register "optional" devices, the driver currently uses
devres::register.
I just released I could use KBox for this.
Thanks
- Markus Probst
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v8 3/5] rust: add basic serial device bus abstractions
2026-05-30 20:31 ` Markus Probst
@ 2026-05-30 20:59 ` Danilo Krummrich
0 siblings, 0 replies; 24+ messages in thread
From: Danilo Krummrich @ 2026-05-30 20:59 UTC (permalink / raw)
To: Markus Probst
Cc: Markus Probst via B4 Relay, Rob Herring, Greg Kroah-Hartman,
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, linux-serial, linux-kernel,
rust-for-linux, linux-pm, driver-core, dri-devel
On Sat May 30, 2026 at 10:31 PM CEST, Markus Probst wrote:
> On Sat, 2026-05-30 at 21:45 +0200, Danilo Krummrich wrote:
>> On Sat May 30, 2026 at 8:55 PM CEST, Markus Probst wrote:
>> > pin-init doesn't have enum support yet, so at least in my driver devres is
>> > still required for now.
>>
>> Hm...how is this related to having a registration type that takes a lifetime
>> rather than a registration type being wrapped in Devres?
> In order to register "optional" devices, the driver currently uses
> devres::register.
>
> I just released I could use KBox for this.
Ah, so you basically use devres::register() to avoid a pinned Option. Let's not
absuse devres for this.
The class device registration should rather use a lifetime now that we got all
the infrastructure for this; the pinned Option problem can be tackled otherwise.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v8 4/5] samples: rust: add Rust serial device bus sample device driver
2026-05-30 1:13 [PATCH v8 0/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
` (2 preceding siblings ...)
2026-05-30 1:13 ` [PATCH v8 3/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
@ 2026-05-30 1:13 ` Markus Probst via B4 Relay
2026-05-30 1:13 ` [PATCH v8 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst via B4 Relay
4 siblings, 0 replies; 24+ messages in thread
From: Markus Probst via B4 Relay @ 2026-05-30 1:13 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, Boqun Feng
Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel, Markus Probst
From: Markus Probst <markus.probst@posteo.de>
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 | 91 ++++++++++++++++++++++++++++++++++++++
4 files changed, 104 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d2f608ff8ca0..fa3f20f0cc4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24278,6 +24278,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..824affbf6593
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,91 @@
+// 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 {
+ let _ = sdev.write_all(data, serdev::Timeout::Max);
+ data.len()
+ }
+}
+
+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.53.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [PATCH v8 5/5] MAINTAINERS: serdev: Add self for serdev
2026-05-30 1:13 [PATCH v8 0/5] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
` (3 preceding siblings ...)
2026-05-30 1:13 ` [PATCH v8 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst via B4 Relay
@ 2026-05-30 1:13 ` Markus Probst via B4 Relay
4 siblings, 0 replies; 24+ messages in thread
From: Markus Probst via B4 Relay @ 2026-05-30 1:13 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, Boqun Feng
Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
dri-devel, Markus Probst
From: Markus Probst <markus.probst@posteo.de>
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/
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 fa3f20f0cc4f..58db6aedf45a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24270,7 +24270,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.53.0
^ permalink raw reply related [flat|nested] 24+ messages in thread