From nobody Sat Feb 7 19:41:21 2026 Received: from mout01.posteo.de (mout01.posteo.de [185.67.36.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E1D28288537 for ; Sat, 20 Dec 2025 18:44:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.67.36.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256250; cv=none; b=IZzu/AVrreb7O6LdL4r4JsCZjAnb02W9DtZMHrKq/Ovex0OoFG5/232JKrx9JCqeVKysfNYtqxZ+TnQbYVoobozOREDjk5tAm4dvtrgT/I0vzxqYw3CgBuoRDa/5j8V2Zm7kOAfmrAN+KtaCa5SZUWpwQE3alZAkL9RZy0BkmHw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256250; c=relaxed/simple; bh=N5q2aEiYe7dgto/kG9UiDYXhU/zMXYlCm50QX5ilJsE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ThYWBPbKy8I5ipU5pqvG+Ks6VrYAnTIypPfALsZzJSephUIq7xDIiHOcvlyb57QSSXWQ+dFuJ6oNjwtT10GRA+O3WySE8ENd9kkTZRIyP0uvyKJg8Xx4e3sCXTAfelxzLRhKqaysOzA+IJjf+wHIPoQvWl2Jo2wsmeA3y/0ecrw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de; spf=pass smtp.mailfrom=posteo.de; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b=c4nDwN9a; arc=none smtp.client-ip=185.67.36.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=posteo.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b="c4nDwN9a" Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id 0225A240027 for ; Sat, 20 Dec 2025 19:44:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posteo.de; s=2017; t=1766256246; bh=IvztXzr5xSlHP9rpeQ5k1NFXGf6NuK8FLPWZGuMt0S0=; h=From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:To:Cc:Autocrypt:OpenPGP:From; b=c4nDwN9aebbDi6Pk0uwxKQgMcQJV2t6EgIG93DFKX4SjEezNf9LmtW1FBXhPkr401 uhysKbqHZGahaycHhyo87xBY20rcCQpHlihQfHEoytwpTu3gHZeQCd5Mrp82aYmULa eWwuvleYB+9Yc+p1vC547ilUporotuIwt9ZwY1mr/I06+754gsBaab99Jh7bdxNkU+ eqg2NPmueY2GmmTQY00EV8fzNy1xsyTqmOJSyXFcC/rrQLGWqw0gbqo4VCiX4DEcUD svOVyFNNoVjhY+XWtOdNprEUGybzjIhEf8Y56MP6/rOjuvG++vldUmvNOdsxR+0fzo rw9WJdu7QceBg== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4dYYGD2Tl8z9rxN; Sat, 20 Dec 2025 19:44:04 +0100 (CET) From: Markus Probst Date: Sat, 20 Dec 2025 18:44:05 +0000 Subject: [PATCH RFC 1/4] serdev: Export internal is_serdev_device() for drivers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251220-rust_serdev-v1-1-e44645767621@posteo.de> References: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> In-Reply-To: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> To: Rob Herring , Greg Kroah-Hartman , Jiri Slaby , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Kari Argillander Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Markus Probst X-Developer-Signature: v=1; a=openpgp-sha256; l=1853; i=markus.probst@posteo.de; h=from:subject:message-id; bh=dYFpooORdHMyhcZP4v33hyOyTm3wYSwyKARxxeg2xIc=; b=owEBiQJ2/ZANAwAIATR2H/jnrUPSAcsmYgBpRu5yl6wQmQUeaaTJM4cF/Q03svMC58ZXTxkQ8 tEFtxj7ODSJAk8EAAEIADkWIQSCdBjE9KxY53IwxHM0dh/4561D0gUCaUbuchsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMSwyLDIACgkQNHYf+OetQ9JFzA/+OsLIgvC+njN1ddsjPMl+QJCmHCg6Ibi fCuHCmGgIaAgTku4IwW9j225EBAjVAAN8sS0R07fTZpdAuYDzK7xTOtl4mPJj9Z1UvO/1THLrMy WUp3uHeByhRcsy2MxrzKJOk+GznMA4Nq3A8yKaRMf9pnLyKC7yDrcjvPNirCDjE4ebrWmLORrDJ UNay7UFMCvKWwdlySNkNPReyJC6JfZtxV+vdYZjyhG9QqgFsbsOyGpHac6YW46K5cIXxWLReOx0 ZOATSsyCqutWrG/D4zkSqEeXax1k0yow4ZR2JX2a0y32iHdc1NXZNVb4SbuYoudcs/AqhmvWp4i j+V2YQvk3+t7i+25Jc0dEAd7Y3J0i5NsrcWJEDTQmLMQiZW2jKmD1W4gdA2pJx5PFXc7u1bu+vS +Zxo3rCpf/PKwBaDXzyEOqUBxY+3juvA/rZlsy9Hjmp4Bf82NHXpKxhqL3VoZiw86iPu3wvj/AR Hbwj11GJNr3bmYW1+L7+0BqPJzi6dytZbf5geoK+YxSZdaf/9SPejKM+iE1c2WIODqg8pfBsInq nwocIfdcoAFykGZ7kc9E5gBNjnN0PNShDNXJcjbQ6QLxex6E5mtePU7bUHpoHs0Cnz4Gg5bFghY Rr/1t5L/CJ8EZpDJg5s88liGgaQaTMjTTMBsqzJE1UGQd0LuQn0c= X-Developer-Key: i=markus.probst@posteo.de; a=openpgp; fpr=827418C4F4AC58E77230C47334761FF8E7AD43D2 Autocrypt: addr=markus.probst@posteo.de; prefer-encrypt=mutual; keydata=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 aBeNN4ijKZchBXHPgVx+YtWRHfcm4l8= OpenPGP: url=https://posteo.de/keys/markus.probst@posteo.de.asc; preference=encrypt From: Kari Argillander The serdev core has an internal is_serdev_device() helper, but it was not accessible to drivers. Make it public by declaring it in serdev.h and exporting the symbol so that modular serdev drivers can rely on it instead of duplicating type checks. This allows example future Rust serdev abstraction to have TryFrom<&device::Device> for &serdev::Device That way using bus is easy for other substystems. Also some other subsystems expose similar function: - bool is_usb_device(const struct device *dev) - bool dev_is_pci(const struct device *dev) Signed-off-by: Kari Argillander --- drivers/tty/serdev/core.c | 3 ++- include/linux/serdev.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index b33e708cb245..1f6bf8e826d8 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -69,10 +69,11 @@ static const struct device_type serdev_device_type =3D { .release =3D serdev_device_release, }; =20 -static bool is_serdev_device(const struct device *dev) +bool is_serdev_device(const struct device *dev) { return dev->type =3D=3D &serdev_device_type; } +EXPORT_SYMBOL_GPL(is_serdev_device); =20 static void serdev_ctrl_release(struct device *dev) { diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 34562eb99931..0043b6cc6d01 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -116,6 +116,8 @@ static inline struct serdev_controller *to_serdev_contr= oller(struct device *d) return container_of(d, struct serdev_controller, dev); } =20 +bool is_serdev_device(const struct device *dev); + static inline void *serdev_device_get_drvdata(const struct serdev_device *= serdev) { return dev_get_drvdata(&serdev->dev); --=20 2.51.2 From nobody Sat Feb 7 19:41:21 2026 Received: from mout02.posteo.de (mout02.posteo.de [185.67.36.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA5322D7387 for ; Sat, 20 Dec 2025 18:51:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.67.36.66 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256676; cv=none; b=ItMOPlTLz7gr63ZvWMLft7K9DQCY2kqnJsap/EsTE7ZdC7bfQJKYnd86TwEovjK9gKXHd0TTSuUw4Reql7aaOVrQ8WEJDqiACAwsUaM13dBrtuo8LmMtZ/2NM+e0vVhKbJVyellvLzmDs1EIj6+T/Ml2/yteQJgsJDov/gkAZrc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256676; c=relaxed/simple; bh=9Jvsn7l5YbOt0di3XZLCVFZSASckH2ZyciBuEFUb0tg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TwORDJ4bss1LfuzYjVJaxn9LJGCA5NVC4LnMNLyOcW0ceOnlLvoDvcHbA2LYHcYOmdcDpUbz+nyillPVN4oi8K7ZTkV1UT81J6nDSk313xAYZmQl9vO/gqU21pqvmsTfx2UtBSnKMkui0udxWQnWin7f8hrMDXEDtPgeDVwNLVo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de; spf=pass smtp.mailfrom=posteo.de; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b=IognUFd2; arc=none smtp.client-ip=185.67.36.66 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=posteo.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b="IognUFd2" Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 39224240104 for ; Sat, 20 Dec 2025 19:44:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posteo.de; s=2017; t=1766256247; bh=7H3YhDYBNKq5GtrLwrm86gKeSlPYZm94Ra+gOByp63k=; h=From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:To:Cc:Autocrypt:OpenPGP:From; b=IognUFd2Q5zigq3JfAdRNPfJrEZloKWZUDiVktsebcw2rM419fud3kKnuQLJ7JWq3 gXymOgTSnqohCWRnkCuEyGaRiapRmg4p+I042OBv85lg5s87VzxXuxDzMm6x8d1kLd Tsdr86HMtYgIr8TlNAqhIwjo5QEwDmih/RNTQ2KJg6XD75VfOhMbgrbwBmmU6Me6ZS pBBaASvYYz6IuyjJ11SDZI64zU+FH3WNBGDKNLK6RAuwxLSFaOZmgY6eFXhp7OSexv 2av0OWMBB0MBHAuMjDP2Jon54HKHeWTPphFZ7IvAm+QjaLQ/wx5NKDr69zS48hR+Re 7Jpk6O0WlKWDQ== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4dYYGF3BHfz9rxM; Sat, 20 Dec 2025 19:44:05 +0100 (CET) From: Markus Probst Date: Sat, 20 Dec 2025 18:44:06 +0000 Subject: [PATCH RFC 2/4] rust: add basic serial device bus abstractions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251220-rust_serdev-v1-2-e44645767621@posteo.de> References: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> In-Reply-To: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> To: Rob Herring , Greg Kroah-Hartman , Jiri Slaby , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Kari Argillander Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Markus Probst X-Developer-Signature: v=1; a=openpgp-sha256; l=35353; i=markus.probst@posteo.de; h=from:subject:message-id; bh=9Jvsn7l5YbOt0di3XZLCVFZSASckH2ZyciBuEFUb0tg=; b=owEBiQJ2/ZANAwAIATR2H/jnrUPSAcsmYgBpRu5yTPIGIbgB6Ye9fvXJGvMF7oWiG3cpKoaV8 dtHA9fcNGyJAk8EAAEIADkWIQSCdBjE9KxY53IwxHM0dh/4561D0gUCaUbuchsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMSwyLDIACgkQNHYf+OetQ9KBgxAAqoPUIHBLv1L5d/vWXzcqce+KgHmgdGz l/KvZGTOH1/kczp8nu8Mlkg/uI9hsgBAk8Eu/TROcvg9yvJDCl9Agl3oBQNIscRqigfFG/nJWxd syeoA6rFVUDGobnnL30/ZEKPAsMqjT/1bjqwIO4WKUxjWJFuTeEKON7q8NgJt5KO70GWJloJxUZ L84HIEc76JS9RXiTYZ0b8pd9QwjdBVnnDogkdDWC7LuMcoZ9gjWHPrZ+5/EX3i1CZAMO0yoBCm/ usJjg7NwxQ4Pti5AKgyZhIaWsLujrtIn2l2z5BvCHKkLPCSSLhIEmqWqUyg1hKnHL+/3ZJjm9rF a65wpkmOw8XVFdEwIaBLsQ61ivCcasd8K6Tosu8NvEVQsG0dMZTcAbZ+AnHEHMNSH/x/wgX8bSi KrEtEEBe+JVsOJZoZrd+SL7buiqjzg+meYSYvUHfxOmjPK1dayJaz7Nts9pa9NCS0GDBXiPIOrJ F9kvd12TtbdE7zWccFOEjMonw+p/FMyuYxYg3XzErIMJcjJKdqWcmCLcwSYw/scLC/nfyowvhND j9Xl2HtmVZttigTZpOb4CVNbuQHK6se73rdplweYjBYSU5hBQPd9QEJFHKU/FZc25JydeBxjbLm uFS8plOBCPt8c2TAb+PYKEkl93jGHjYtM/eF1nYhr9IkFy/Mmh2Q= X-Developer-Key: i=markus.probst@posteo.de; a=openpgp; fpr=827418C4F4AC58E77230C47334761FF8E7AD43D2 Autocrypt: addr=markus.probst@posteo.de; prefer-encrypt=mutual; keydata=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 aBeNN4ijKZchBXHPgVx+YtWRHfcm4l8= OpenPGP: url=https://posteo.de/keys/markus.probst@posteo.de.asc; preference=encrypt 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`. Co-developed-by: Kari Argillander Signed-off-by: Kari Argillander Signed-off-by: Markus Probst --- 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 | 815 ++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 841 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index a067038b4b42..bec6c6d0913a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 79c72762ad9c..834e9fbb897d 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -50,6 +50,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 + +__rust_helper +void rust_helper_serdev_device_driver_unregister(struct serdev_device_driv= er *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 f812cf120042..cc71195466b6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -136,6 +136,8 @@ pub mod scatterlist; pub mod security; pub mod seq_file; +#[cfg(CONFIG_SERIAL_DEV_BUS)] +pub mod serdev; pub mod sizes; pub mod slice; mod static_assert; diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs new file mode 100644 index 000000000000..0f5ef325a054 --- /dev/null +++ b/rust/kernel/serdev.rs @@ -0,0 +1,815 @@ +// 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, + container_of, + device, + driver, + error::{ + from_result, + to_result, + VTABLE_DEFAULT_ERROR, // + }, + of, + prelude::*, + time::Jiffies, + types::{AlwaysRefCounted, Opaque}, // +}; + +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::offset_of, + num::NonZero, + ptr::{ + addr_of_mut, // + NonNull, + }, // +}; + +/// Parity bit to use with a serial device. +#[repr(u32)] +pub enum Parity { + /// No parity bit. + None =3D bindings::serdev_parity_SERDEV_PARITY_NONE, + /// Even partiy. + Even =3D bindings::serdev_parity_SERDEV_PARITY_EVEN, + /// Odd parity. + Odd =3D bindings::serdev_parity_SERDEV_PARITY_ODD, +} + +/// Timeout in Jiffies. +pub enum Timeout { + /// Wait for a specific amount of [`Jiffies`]. + Value(NonZero), + /// Wait as long as possible. + /// + /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`]. + MaxScheduleTimeout, +} + +impl Timeout { + fn into_jiffies(self) -> isize { + match self { + Self::Value(value) =3D> value.get().try_into().unwrap_or_defau= lt(), + Self::MaxScheduleTimeout =3D> 0, + } + } +} + +/// An adapter for the registration of serial device bus device drivers. +pub struct Adapter(T); + +// SAFETY: A call to `unregister` for a given instance of `RegType` is gua= ranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl driver::RegistrationOps for Adapter { + type RegType =3D bindings::serdev_device_driver; + + unsafe fn register( + sdrv: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let of_table =3D match T::OF_ID_TABLE { + Some(table) =3D> table.as_ptr(), + None =3D> core::ptr::null(), + }; + + let acpi_table =3D match T::ACPI_ID_TABLE { + Some(table) =3D> table.as_ptr(), + None =3D> core::ptr::null(), + }; + + // SAFETY: It's safe to set the fields of `struct serdev_device_dr= iver` on initialization. + unsafe { + (*sdrv.get()).driver.name =3D name.as_char_ptr(); + (*sdrv.get()).probe =3D Some(Self::probe_callback); + (*sdrv.get()).remove =3D Some(Self::remove_callback); + (*sdrv.get()).driver.of_match_table =3D of_table; + (*sdrv.get()).driver.acpi_match_table =3D acpi_table; + } + + // SAFETY: `sdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.= get(), module.0) }) + } + + unsafe fn unregister(sdrv: &Opaque) { + // SAFETY: `sdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) }; + } +} + +#[pin_data] +struct Drvdata { + #[pin] + driver: T, + initial_data: UnsafeCell>, + late_probe_data: UnsafeCell>>>, +} + +impl Adapter { + const OPS: &'static bindings::serdev_device_ops =3D &bindings::serdev_= device_ops { + receive_buf: if T::HAS_RECEIVE { + Some(Self::receive_buf_callback) + } else { + None + }, + write_wakeup: if T::HAS_WRITE_WAKEUP { + Some(Self::write_wakeup_callback) + } else { + Some(bindings::serdev_device_write_wakeup) + }, + }; + const INITIAL_OPS: &'static bindings::serdev_device_ops =3D &bindings:= :serdev_device_ops { + receive_buf: Some(Self::initial_receive_buf_callback), + write_wakeup: if T::HAS_WRITE_WAKEUP_INITIAL { + Some(Self::initial_write_wakeup_callback) + } else { + Some(bindings::serdev_device_write_wakeup) + }, + }; + const NO_OPS: &'static bindings::serdev_device_ops =3D &bindings::serd= ev_device_ops { + receive_buf: None, + write_wakeup: Some(bindings::serdev_device_write_wakeup), + }; + + extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> ke= rnel::ffi::c_int { + // SAFETY: The serial device bus only ever calls the probe callbac= k with a valid pointer to + // a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `probe_callback(= )`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + let info =3D ::id_info(sdev.as_ref()); + + from_result(|| { + let data =3D try_pin_init!(Drvdata { + driver <- T::probe(sdev, info), + initial_data: Some(Default::default()).into(), + late_probe_data: None.into(), + }); + + sdev.as_ref().set_drvdata(data)?; + + // SAFETY: We just set drvdata to `Drvdata`. + let data =3D unsafe { sdev.as_ref().drvdata_borrow::>() }; + + // 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::INITIAL_OPS) }; + + // SAFETY: The serial device bus only ever calls the probe cal= lback with a valid pointer + // to a `struct serdev_device`. + to_result(unsafe { + bindings::devm_serdev_device_open(sdev.as_ref().as_raw(), = sdev.as_raw()) + })?; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + T::configure(sdev, unsafe { Pin::new_unchecked(&data.driver) }= , info)?; + + if !T::HAS_RECEIVE_INITIAL { + // SAFETY: + // - It is guaranteed that we have exclusive access to `da= ta.initial_data` and + // `data.late_probe_data`. + // - We just initialized `data.initial_data` with Some. + unsafe { Self::do_late_probe(sdev, data)? }; + } + + Ok(0) + }) + } + + /// # Safety + /// + /// The caller must guarantee, that we have exclusive access to `data.= initial_data` and + /// `data.late_probe_data`. `data.initial_data` must be Some. + /// (i. e. `late_probe` has not been called yet). + unsafe fn do_late_probe(sdev: &Device, data: Pin= <&Drvdata>) -> Result { + // SAFETY: `&data.driver` is guaranteed to be pinned. + let data_driver =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: The function contract guarantees that we have exclusive= access to + // `data.initial_data`. + let initial_data =3D unsafe { &mut *data.initial_data.get() }; + + // SAFETY: The function contract guarantees that we have exclusive= access to + // `data.late_probe_data`. + let late_probe_data =3D unsafe { &mut *data.late_probe_data.get() = }; + + *late_probe_data =3D Some(KBox::pin_init( + T::late_probe( + sdev, + data_driver, + // SAFETY: The function contract guarantees that `data.ini= tial_data` is Some. + unsafe { initial_data.take().unwrap_unchecked() }, + ), + GFP_KERNEL, + )?); + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to = `serdev_device`. + unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Sel= f::OPS) }; + Ok(()) + } + + extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) { + // SAFETY: The serial device bus only ever calls the remove callba= ck with a valid pointer + // to a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `remove_callback= ()`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + + // SAFETY: `remove_callback` is only ever called after a successfu= l call to + // `probe_callback`, hence it's guaranteed that `Device::set_drvda= ta()` has been called + // and stored a `Pin>`. + let data =3D unsafe { sdev.as_ref().drvdata_obtain::>()= }; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + let data_driver =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: + // - `data.late_probe_data` is guaranteed to be pinned. + // - It is guaranteed that we have exclusive access to `data.late_= probe_data`. + let late_probe_data =3D unsafe { + (*data.late_probe_data.get()) + .as_deref() + .map(|data| Pin::new_unchecked(data)) + }; + + T::unbind(sdev, data_driver, late_probe_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 c= allback with a valid + // pointer to a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `receive_buf_cal= lback()`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + + // SAFETY: `receive_buf_callback` is only ever called after a succ= essful call to + // `probe_callback`, hence it's guaranteed that `Device::set_drvda= ta()` has been called + // and stored a `Pin>>`. + let data =3D unsafe { sdev.as_ref().drvdata_borrow::>()= }; + + // SAFETY: `buf` is guaranteed to be non-null and has the size of = `length`. + let buf =3D unsafe { core::slice::from_raw_parts(buf, length) }; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + let data_driver =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: + // - `data.late_probe_data` is guaranteed to be Some. + // - `data.late_probe_data` is guaranteed to be pinned. + let late_probe_data =3D unsafe { + Pin::new_unchecked((*data.late_probe_data.get()).as_deref().un= wrap_unchecked()) + }; + + T::receive(sdev, data_driver, late_probe_data, buf) + } + + extern "C" fn write_wakeup_callback(sdev: *mut bindings::serdev_device= ) { + // SAFETY: The serial device bus only ever calls the write wakeup = callback with a valid + // pointer to a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `write_wakeup_ca= llback()`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + + // SAFETY: `write_wakeup_callback` is only ever called after a suc= cessful call to + // `probe_callback`, hence it's guaranteed that `Device::set_drvda= ta()` has been called + // and stored a `Pin>>`. + let data =3D unsafe { sdev.as_ref().drvdata_borrow::>()= }; + + // SAFETY: `sdev.as_raw()` is guaranteed to be a pointer to a vali= d `serdev_device`. + unsafe { bindings::serdev_device_write_wakeup(sdev.as_raw()) }; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + let data_driver =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: + // - `data.late_probe_data` is guaranteed to be Some. + // - `data.late_probe_data` is guaranteed to be pinned. + let late_probe_data =3D unsafe { + Pin::new_unchecked((*data.late_probe_data.get()).as_deref().un= wrap_unchecked()) + }; + + // SAFETY: As long as the driver implementation meets the safety r= equirements, this call + // is safe. + unsafe { T::write_wakeup(sdev, data_driver, late_probe_data) }; + } + + extern "C" fn initial_receive_buf_callback( + sdev: *mut bindings::serdev_device, + buf: *const u8, + length: usize, + ) -> usize { + if !T::HAS_RECEIVE_INITIAL { + return 0; + } + + // SAFETY: The serial device bus only ever calls the receive buf c= allback with a valid + // pointer to a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `receive_buf_cal= lback()`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + + // SAFETY: `buf` is guaranteed to be non-null and has the size of = `length`. + let buf =3D unsafe { core::slice::from_raw_parts(buf, length) }; + + // SAFETY: `initial_receive_buf_callback` is only ever called afte= r a successful call to + // `probe_callback`, hence it's guaranteed that `Device::set_drvda= ta()` has been called + // and stored a `Pin>>`. + let data =3D unsafe { sdev.as_ref().drvdata_borrow::>()= }; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + let driver_data =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: + // - `data.initial_data` is always Some until `InitialReceiveResul= t::Ready` is + // returned below. + // - It is guaranteed that we have exclusive access to `data.initi= al_data`. + let initial_data =3D unsafe { (*data.initial_data.get()).as_mut().= unwrap_unchecked() }; + + match T::receive_initial(sdev, driver_data, initial_data, buf) { + Ok(InitialReceiveResult::Pending(size)) =3D> size, + Ok(InitialReceiveResult::Ready(size)) =3D> { + // SAFETY: + // - It is guaranteed that we have exclusive access to `da= ta.initial_data` and + // `data.late_probe_data`. + // - We just initialized `data.initial_data` with Some. + if let Err(err) =3D unsafe { Self::do_late_probe(sdev, dat= a) } { + dev_err!(sdev.as_ref(), "Unable to late probe device: = {err:?}\n"); + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid= pointer to + // `serdev_device`. + unsafe { bindings::serdev_device_set_client_ops(sdev.a= s_raw(), Self::NO_OPS) }; + return length; + } + size + } + Err(err) =3D> { + dev_err!( + sdev.as_ref(), + "Unable to receive initial data for probe: {err:?}.\n" + ); + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid poi= nter to `serdev_device`. + unsafe { bindings::serdev_device_set_client_ops(sdev.as_ra= w(), Self::NO_OPS) }; + length + } + } + } + + extern "C" fn initial_write_wakeup_callback(sdev: *mut bindings::serde= v_device) { + // SAFETY: The serial device bus only ever calls the write wakeup = callback with a valid + // pointer to a `struct serdev_device`. + // + // INVARIANT: `sdev` is valid for the duration of `write_wakeup_ca= llback()`. + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + + // SAFETY: `initial_write_wakeup_callback` is only ever called aft= er a successful call to + // `probe_callback`, hence it's guaranteed that `Device::set_drvda= ta()` has been called + // and stored a `Pin>>`. + let data =3D unsafe { sdev.as_ref().drvdata_borrow::>()= }; + + // SAFETY: `sdev.as_raw()` is guaranteed to be a pointer to a vali= d `serdev_device`. + unsafe { bindings::serdev_device_write_wakeup(sdev.as_raw()) }; + + // SAFETY: `&data.driver` is guaranteed to be pinned. + let data_driver =3D unsafe { Pin::new_unchecked(&data.driver) }; + + // SAFETY: As long as the driver implementation meets the safety r= equirements, this call + // is safe. + unsafe { T::write_wakeup_initial(sdev, data_driver) }; + } +} + +impl driver::Adapter for Adapter { + type IdInfo =3D T::IdInfo; + + fn of_id_table() -> Option> { + T::OF_ID_TABLE + } + + fn acpi_id_table() -> Option> { + T::ACPI_ID_TABLE + } +} + +/// Declares a kernel module that exposes a single serial device bus devic= e 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)*) =3D> { + $crate::module_driver!(, $crate::serdev::Adapter, { $($f)* }= ); + }; +} + +/// Result for `receive_initial` in [`Driver`]. +pub enum InitialReceiveResult { + /// More data is required. + /// + /// The inner data is the number of bytes accepted. + Pending(usize), + /// Ready for late probe. + /// + /// The inner data is the number of bytes accepted. + Ready(usize), +} + +/// 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, +/// ::IdInfo, +/// [ +/// (of::DeviceId::new(c"test,device"), ()) +/// ] +/// ); +/// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// ::IdInfo, +/// [ +/// (acpi::DeviceId::new(c"LNUXBEEF"), ()) +/// ] +/// ); +/// +/// #[vtable] +/// impl serdev::Driver for MyDriver { +/// type IdInfo =3D (); +/// type LateProbeData =3D (); +/// type InitialData =3D (); +/// const OF_ID_TABLE: Option> =3D Some(&OF_= TABLE); +/// const ACPI_ID_TABLE: Option> =3D Some(= &ACPI_TABLE); +/// +/// fn probe( +/// _sdev: &serdev::Device, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> impl PinInit { +/// Err(ENODEV) +/// } +/// +/// fn configure( +/// dev: &serdev::Device, +/// _this: Pin<&Self>, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> Result { +/// dev.set_baudrate(115200); +/// Ok(()) +/// } +/// +/// fn late_probe( +/// _dev: &serdev::Device, +/// _this: Pin<&Self>, +/// _initial_data: Self::InitialData, +/// ) -> impl PinInit { +/// Ok(()) +/// } +/// } +///``` +#[vtable] +pub trait Driver: Send { + /// The type holding driver private data about each device id supporte= d by the driver. + // TODO: Use associated_type_defaults once stabilized: + // + // ``` + // type IdInfo: 'static =3D (); + // type LateProbeData: Send + 'static =3D (); + // type InitialData: Default + Send + 'static =3D (); + // ``` + type IdInfo: 'static; + + /// Data returned in `late_probe` function. + type LateProbeData: Send + 'static; + /// Data used for initial data. + type InitialData: Default + Send + 'static; + + /// The table of OF device ids supported by the driver. + const OF_ID_TABLE: Option> =3D None; + + /// The table of ACPI device ids supported by the driver. + const ACPI_ID_TABLE: Option> =3D 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(sdev: &Device, id_info: Option<&Self::IdInfo>) -> impl PinIni= t; + + /// Serial device bus device driver configure. + /// + /// Called directly after the serial device bus device has been opened. + /// This should be used for setting up the communication (i. e. baudra= te, flow control, parity) + /// and sending an initial hello if required. + fn configure( + sdev: &Device, + this: Pin<&Self>, + id_info: Option<&Self::IdInfo>, + ) -> Result; + + /// Serial device bus device data receive callback (initial). + /// + /// Called when data got received from device, before `late_probe` has= been called. + /// This should be used, to get information about the device for `late= _probe`. + /// + /// Returns `InitialReceiveResult::Pending` if more data is still requ= ired for `late_probe`. + /// Otherwise `InitialReceiveResult::Ready`. The inner data is the num= ber of bytes accepted. + fn receive_initial( + sdev: &Device, + this: Pin<&Self>, + initial_data: &mut Self::InitialData, + data: &[u8], + ) -> Result { + let _ =3D (sdev, this, initial_data, data); + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Serial device bus device write wakeup callback (initial). + /// + /// Called when ready to transmit more data, before `late_probe` has b= een called. + /// + /// # Safety + /// + /// This function must not sleep. + unsafe fn write_wakeup_initial(sdev: &Device, this: Pin<= &Self>) { + let _ =3D (sdev, this); + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Serial device bus device late probe. + /// + /// Called after the initial data is available. + /// If `receive_initial` isn't implemented, this will be called direct= ly after configure. + fn late_probe( + sdev: &Device, + this: Pin<&Self>, + initial_data: Self::InitialData, + ) -> impl PinInit; + + /// Serial device bus device driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Imp= lementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown op= erations that require a + /// `&Device` or `&Device` reference. For instance. + /// + /// Otherwise, release operations for driver resources should be perfo= rmed in `Self::drop`. + fn unbind( + sdev: &Device, + this: Pin<&Self>, + late_probe_this: Option>, + ) { + let _ =3D (sdev, this, late_probe_this); + } + + /// Serial device bus device data receive callback. + /// + /// Called when data got received from device. + /// + /// Returns the number of bytes accepted. + fn receive( + sdev: &Device, + this: Pin<&Self>, + late_probe_this: Pin<&Self::LateProbeData>, + data: &[u8], + ) -> usize { + let _ =3D (sdev, this, late_probe_this, data); + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Serial device bus device write wakeup callback. + /// + /// Called when ready to transmit more data. + /// + /// # Safety + /// + /// This function must not sleep. + unsafe fn write_wakeup( + sdev: &Device, + this: Pin<&Self>, + late_probe_this: Pin<&Self::LateProbeData>, + ) { + let _ =3D (sdev, this, late_probe_this); + 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 se= rdev_device` within Rust +/// code that we get passed from the C side. +/// +/// # Invariants +/// +/// A [`Device`] instance represents a valid `struct serdev_device` create= d by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct Device( + Opaque, + PhantomData, +); + +impl Device { + fn as_raw(&self) -> *mut bindings::serdev_device { + self.0.get() + } +} + +impl Device { + /// 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 writte= n data prior to this call. + pub fn set_baudrate(&self, speed: u32) -> u32 { + // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a vali= d `serdev_device`. + unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed= ) } + } + + /// Set if flow control should be enabled. + /// + /// Use [`Device::write_flush`] before calling this if you have writte= n 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 vali= d `serdev_device`. + unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), e= nable) }; + } + + /// Set parity to use. + /// + /// Use [`Device::write_flush`] before calling this if you have writte= n 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 vali= d `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 controll= er. Use + /// [ Device::wait_until_sent`] to make sure the controller write buff= er has actually been + /// emptied. + /// + /// Returns the number of bytes written (less than data if interrupted= ). + /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::EREST= ARTSYS`] if interrupted + /// before any bytes were written. + pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result { + // SAFETY: + // - `self.as_raw()` is guaranteed to be a pointer to a valid `ser= dev_device`. + // - `data.as_ptr()` is guaranteed to be a valid array pointer wit= h the size of + // `data.len()`. + let ret =3D unsafe { + bindings::serdev_device_write( + self.as_raw(), + data.as_ptr(), + data.len(), + timeout.into_jiffies(), + ) + }; + if ret < 0 { + // CAST: negative return values are guaranteed to be between `= -MAX_ERRNO` and `-1`, + // which always fit into a `i32`. + Err(Error::from_errno(ret as i32)) + } else { + Ok(ret.unsigned_abs()) + } + } + + /// Write data to the serial device. + /// + /// If you want to write until the controller has accepted all the dat= a, use + /// [`Device::write_all`]. + /// + /// Note that any accepted data has only been buffered by the controll= er. Use + /// [ Device::wait_until_sent`] to make sure the controller write buff= er has actually been + /// emptied. + /// + /// Returns the number of bytes written (less than data if interrupted= ). + /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::EREST= ARTSYS`] if interrupted + /// before any bytes were written. + pub fn write(&self, data: &[u8]) -> Result { + // SAFETY: + // - `self.as_raw()` is guaranteed to be a pointer to a valid `ser= dev_device`. + // - `data.as_ptr()` is guaranteed to be a valid array pointer wit= h the size of + // `data.len()`. + let ret =3D + unsafe { bindings::serdev_device_write_buf(self.as_raw(), data= .as_ptr(), data.len()) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret.unsigned_abs()) + } + } + + /// Send data to the serial device immediately. + /// + /// Note that this doesn't guarantee that the data has been transmitte= d. + /// 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 vali= d `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 vali= d `serdev_device`. + unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), ti= meout.into_jiffies()) }; + } +} + +// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_dev= ice`. +// The offset is guaranteed to point to a valid device field inside `serde= v::Device`. +unsafe impl device::AsBusDevice for Devic= e { + const OFFSET: usize =3D 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) { + // SAFETY: The safety requirements guarantee that the refcount is = non-zero. + unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) } + } +} + +impl AsRef> for Device { + fn as_ref(&self) -> &device::Device { + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct serdev_device`. + let dev =3D unsafe { addr_of_mut!((*self.as_raw()).dev) }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::from_raw(dev) } + } +} + +impl TryFrom<&device::Device> for &Device= { + type Error =3D kernel::error::Error; + + fn try_from(dev: &device::Device) -> Result { + // SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a = valid pointer to a + // `struct device`. + if !unsafe { bindings::is_serdev_device(dev.as_raw()) } { + return Err(EINVAL); + } + + // SAFETY: We've just verified that the device_type of `dev` is + // `serdev_device_type`, hence `dev` must be embedded in a valid `= struct + // serdev_device_driver` as guaranteed by the corresponding C code. + let sdev =3D unsafe { container_of!(dev.as_raw(), bindings::serdev= _device, dev) }; + + // SAFETY: `sdev` is a valid pointer to a `struct serdev_device_dr= iver`. + Ok(unsafe { &*sdev.cast() }) + } +} + +// 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 `De= vice` +// (i.e. `Device) are thread safe. +unsafe impl Sync for Device {} --=20 2.51.2 From nobody Sat Feb 7 19:41:21 2026 Received: from mout02.posteo.de (mout02.posteo.de [185.67.36.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2AD3C30FF39 for ; Sat, 20 Dec 2025 18:44:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.67.36.66 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256261; cv=none; b=kO9tQTPkhKSMmgheBMASbHFt/2jORKQFR3CdUXGTd8jUrMcg5FznfU2LzsRdiqSipu1u/tjhjS90/Mpr/KFwDuTLouCKmJmE5B1LqVR+7hK1MN3lJBZDHc+cm4gTeCXqtv1aWHJ54kE5nwzUtGpx+Dem0VSuqlN4FQzdv2P/0pc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256261; c=relaxed/simple; bh=aZqeahUjQLCiJk72azPd4I5DmJ0fC6eqFPQ9m57O/Fc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lkuFZ1UsmFawicMFj+ExHTSY2OHKm74QbEOwcWHEvcNnn574zrrTNxBA1G1TEstSpSwPKhSN9M6gLBt85BPhvU5j045yUc46lxVhtlHQvu+KBRFnP+GpZXuE1FLYjYRABDztuuOoew0m5PnRGtlR4qWOx/3/6yZinpDNl10p/YQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de; spf=pass smtp.mailfrom=posteo.de; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b=p3ulSR5p; arc=none smtp.client-ip=185.67.36.66 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=posteo.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b="p3ulSR5p" Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 6F4D7240105 for ; Sat, 20 Dec 2025 19:44:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posteo.de; s=2017; t=1766256248; bh=wOcrld681LngU0MiZ8DHfy9jiZ/IlT36yUJBg6cYTFE=; h=From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:To:Cc:Autocrypt:OpenPGP:From; b=p3ulSR5p6AeuirYDy53zNUAmAUxXF3w4hKOCUoeEI3qCObtRWT0/gUNutyxWynLuE 4zaxAqAEnbGoH0Qbv7R+PNgAfjaQL8Q6xYgebvSGOgiPzfWhEtKF/DVIsZxGEJSk0A MMm0ALjEbHizkq2ZXwT/LAIkoH7LRIiR9QRJBmQVuMZXPdSvfab0pcaDV1OVXF6tc5 PbmAeXlqz+1jzuMB4XnIyYvNYc+SxKcaQvW1izSXhXshP38LvsvUe4Sxi4IbM2DSY5 AVicaeSaZvBXv+5MsOFPJSahYZLwcbWTM3IOYjoEbCcI72WEySlwxnGgnzq8oSKonz vfdExD9yq3PuQ== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4dYYGG5mCZz9rxN; Sat, 20 Dec 2025 19:44:06 +0100 (CET) From: Markus Probst Date: Sat, 20 Dec 2025 18:44:07 +0000 Subject: [PATCH RFC 3/4] samples: rust: add Rust serial device bus sample device driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251220-rust_serdev-v1-3-e44645767621@posteo.de> References: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> In-Reply-To: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> To: Rob Herring , Greg Kroah-Hartman , Jiri Slaby , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Kari Argillander Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Markus Probst X-Developer-Signature: v=1; a=openpgp-sha256; l=7285; i=markus.probst@posteo.de; h=from:subject:message-id; bh=aZqeahUjQLCiJk72azPd4I5DmJ0fC6eqFPQ9m57O/Fc=; b=owEBiQJ2/ZANAwAIATR2H/jnrUPSAcsmYgBpRu5yHydp1I6kFuWuDobSCuH260q/vBGf+f5cr O4mDgJ7vx+JAk8EAAEIADkWIQSCdBjE9KxY53IwxHM0dh/4561D0gUCaUbuchsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMSwyLDIACgkQNHYf+OetQ9Lqdw/+O9fMa5pBmvSfhAVU0AcRAR/y3ymDFiG 41sXVugZojnAYPqq1BvYvsau0mToX6F8XIPjdXpOSsSYT3462FUOS8nU6s2xWfcaf15tixLESw9 WzckrC0R7lHkjvn5CMnb8D9qpRBPnfVqMXav+7vuVjxQHoJfv0hBFiTK7Y3BjDh/A4jNnJPq0/l s4IrOk/SrmHSjisfT0S3SP45GwRkwYqP1g0WLteoByQicew0zxQM3TbLHOa2oKmvUIDvSElG1o/ MyTv5LrD7wwbZXBM+D2KYWKTdl2Yd2vCNpj0YxD5HjmvxxYyTf4sTLPg4S6NAWh7KlwC5iI7A6K HNs8/ESFTOGyYI/XjG85nQCMeJkuf+C/NzGkG5nbnuquSf12RW4bim/JmATWNJmmSEwUN92QukH 4Xi4k6qpV/XSir7SYVcv+xPicb7CceOqe+DTXtkh/Qq4dLks1LQXsGYW0SFqyNWRh6a9qGt6Y3t BwTsKiHUO3y+KZKyElE9SgTdLKbmoAjNiHxRmzI0JVGEF3igwyadOUdxGiQz3/9X8fOiB3+vE1A bVWzGyfjK8aD1msrGtTgXQdXShhDBN3L+zkfsYVESTYZVhsurN/7c3fw900EWPk0ycitj0Ao/I8 mBQuK2gUnxgTgWVeT/dI/78SEft2uKhOgh57ShjPup8vC4XXpoV8= X-Developer-Key: i=markus.probst@posteo.de; a=openpgp; fpr=827418C4F4AC58E77230C47334761FF8E7AD43D2 Autocrypt: addr=markus.probst@posteo.de; prefer-encrypt=mutual; keydata=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 aBeNN4ijKZchBXHPgVx+YtWRHfcm4l8= OpenPGP: url=https://posteo.de/keys/markus.probst@posteo.de.asc; preference=encrypt Add a sample Rust serial device bus device driver illustrating the usage of the platform bus abstractions. This drivers probes through either a match of device / driver name or a match within the OF ID table. --- samples/rust/Kconfig | 10 +++ samples/rust/Makefile | 1 + samples/rust/rust_driver_serdev.rs | 175 +++++++++++++++++++++++++++++++++= ++++ 3 files changed, 186 insertions(+) diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 3efa51bfc8ef..3b6663b4bc9b 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -161,6 +161,16 @@ config SAMPLE_RUST_DRIVER_AUXILIARY =20 If unsure, say N. =20 +config SAMPLE_RUST_DRIVER_SERDEV + tristate "Serial Device Bus Device Driver" + 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_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index f65885d1d62b..ec5cb8065fb7 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_drive= r_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB) +=3D rust_driver_usb.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) +=3D rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) +=3D rust_driver_auxiliary.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_SERDEV) +=3D rust_driver_serdev.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) +=3D rust_configfs.o =20 rust_print-y :=3D rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_= serdev.rs new file mode 100644 index 000000000000..f23b38a26c32 --- /dev/null +++ b/samples/rust/rust_driver_serdev.rs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust Serial device bus device driver sample. + +use kernel::{ + acpi, + device::{ + self, + property::{ + FwNodeReferenceArgs, + NArgs, // + }, + Bound, + Core, // + }, + of, + prelude::*, + serdev, + str::CString, + sync::aref::ARef, // +}; + +struct SampleDriver { + sdev: ARef, +} + +struct Info(u32); + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c"test,rust_driver_serdev"), Info(42))] +); + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::IdInfo, + [(acpi::DeviceId::new(c"LNUXBEEF"), Info(0))] +); + +#[vtable] +impl serdev::Driver for SampleDriver { + type IdInfo =3D Info; + type InitialData =3D (); + type LateProbeData =3D (); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + const ACPI_ID_TABLE: Option> =3D Some(&ACP= I_TABLE); + + fn probe(sdev: &serdev::Device, info: Option<&Self::IdInfo>) -> impl P= inInit { + let dev =3D sdev.as_ref(); + + dev_dbg!(dev, "Probe Rust Serial device bus device driver sample.\= n"); + + if let Some(info) =3D info { + dev_info!(dev, "Probed with info: '{}'.\n", info.0); + } + + if dev.fwnode().is_some_and(|node| node.is_of_node()) { + Self::properties_parse(dev)?; + } + + Ok(Self { sdev: sdev.into() }) + } + + fn configure( + sdev: &serdev::Device, + _this: Pin<&Self>, + _id_info: Option<&Self::IdInfo>, + ) -> Result { + dev_dbg!( + sdev.as_ref(), + "Configure Rust Serial device bus device driver sample.\n" + ); + + sdev.set_baudrate(115200); + sdev.set_flow_control(false); + sdev.set_parity(serdev::Parity::None)?; + Ok(()) + } + + fn late_probe( + sdev: &serdev::Device, + _this: Pin<&Self>, + _initial_data: Self::InitialData, + ) -> impl PinInit { + dev_dbg!( + sdev.as_ref(), + "Late Probe Rust Serial device bus device driver sample.\n" + ); + Ok(()) + } + + fn receive( + sdev: &serdev::Device, + _this: Pin<&Self>, + _late_probe_this: Pin<&Self::LateProbeData>, + data: &[u8], + ) -> usize { + let _ =3D sdev.write_all(data, serdev::Timeout::MaxScheduleTimeout= ); + data.len() + } +} + +impl SampleDriver { + fn properties_parse(dev: &device::Device) -> Result { + let fwnode =3D dev.fwnode().ok_or(ENOENT)?; + + if let Ok(idx) =3D fwnode.property_match_string(c"compatible", c"t= est,rust-device") { + dev_info!(dev, "matched compatible string idx =3D {}\n", idx); + } + + let name =3D c"compatible"; + let prop =3D fwnode.property_read::(name).required_by(dev= )?; + dev_info!(dev, "'{name}'=3D'{prop:?}'\n"); + + let name =3D c"test,bool-prop"; + let prop =3D fwnode.property_read_bool(c"test,bool-prop"); + dev_info!(dev, "'{name}'=3D'{prop}'\n"); + + if fwnode.property_present(c"test,u32-prop") { + dev_info!(dev, "'test,u32-prop' is present\n"); + } + + let name =3D c"test,u32-optional-prop"; + let prop =3D fwnode.property_read::(name).or(0x12); + dev_info!(dev, "'{name}'=3D'{prop:#x}' (default =3D 0x12)\n"); + + // A missing required property will print an error. Discard the er= ror to + // prevent properties_parse from failing in that case. + let name =3D c"test,u32-required-prop"; + let _ =3D fwnode.property_read::(name).required_by(dev); + + let name =3D c"test,u32-prop"; + let prop: u32 =3D fwnode.property_read(name).required_by(dev)?; + dev_info!(dev, "'{name}'=3D'{prop:#x}'\n"); + + let name =3D c"test,i16-array"; + let prop: [i16; 4] =3D fwnode.property_read(name).required_by(dev)= ?; + dev_info!(dev, "'{name}'=3D'{prop:?}'\n"); + let len =3D fwnode.property_count_elem::(name)?; + dev_info!(dev, "'{name}' length is {len}\n"); + + let name =3D c"test,i16-array"; + let prop: KVec =3D fwnode.property_read_array_vec(name, 4)?.r= equired_by(dev)?; + dev_info!(dev, "'{name}'=3D'{prop:?}' (KVec)\n"); + + for child in fwnode.children() { + let name =3D c"test,ref-arg"; + let nargs =3D NArgs::N(2); + let prop: FwNodeReferenceArgs =3D child.property_get_reference= _args(name, nargs, 0)?; + dev_info!(dev, "'{name}'=3D'{prop:?}'\n"); + } + + Ok(()) + } +} + +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", +} --=20 2.51.2 From nobody Sat Feb 7 19:41:21 2026 Received: from mout01.posteo.de (mout01.posteo.de [185.67.36.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C2CB330FF29 for ; Sat, 20 Dec 2025 18:44:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.67.36.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256253; cv=none; b=S7mXMaTG4NyzC0GnN9hHf/pqREvxiwh4t8WlxLUY/kkPr6C2Gr+DtwigasDNU9jwYUwhGydaSv6/z+hdPfbejfh41PyDKzW9jeucqOhQDuTIC9i3qbX3ecmUkiUBkSCUlfa3Wrsaw0IK4vNKPczDPhGYYgceH6MQBgcML8lTGuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766256253; c=relaxed/simple; bh=qxps9LuzVhP3NZF39g3++QSsoAR3m1O2TDzQHDWwPzU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gK8Gqpl0CRV6FH0euKMWebhO8ep30YuyDli3JOM2dIeRH58oUF9L00LhiqjGMmQ2QSKZe3MMGoIAfA2l70Ki3mOONCjpgaA/AW5CvqM8YUGDM7St8JYWmaVsI9VOEQRlrvOrk3dM2ggM7i0jq7LhrP8tokte8/dNLZMLp5WNwbo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de; spf=pass smtp.mailfrom=posteo.de; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b=CbqzRR1Y; arc=none smtp.client-ip=185.67.36.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=posteo.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=posteo.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=posteo.de header.i=@posteo.de header.b="CbqzRR1Y" Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id 7779F24002B for ; Sat, 20 Dec 2025 19:44:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posteo.de; s=2017; t=1766256249; bh=Mb0ZptlxWRqHBcmO8uHkQKeAT+IaQ7jXYG7pQmabGJg=; h=From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:To:Cc:Autocrypt:OpenPGP:From; b=CbqzRR1YUr8sr6v6Sf4Ne1IZzp3QSnmmdaXWeaMBOUCySNe4xCIQLpXzJ1zwO8fD/ d/qq7S6hiMMulGzoBSYuoJE3IeSqAPBU1wh99e728fTL2nsjoTicQwnCxFAw2r6RiI qhIRGu3uKF7UgFo7hKS3cL7pmfntjOWUuoifSJExKUA+caTtOykt84djZVFs4cGc/c 6a2qlA1mnPmya57kmyC+xLEFWNkRlvgaz++tAJqUp9axfwH1Al1d5LE0aUTr4URPKJ nyEy0h4mDSsxcPmnk188tBxKSkPqTQdMhyoSHlHzX04j20rTxHhPMA35go8Va7SWjm Ip2lvzp78Qjew== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4dYYGH68Hcz9rxB; Sat, 20 Dec 2025 19:44:07 +0100 (CET) From: Markus Probst Date: Sat, 20 Dec 2025 18:44:08 +0000 Subject: [PATCH RFC 4/4] rust: Add serdev rust abstractions to MAINTAINERS file Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251220-rust_serdev-v1-4-e44645767621@posteo.de> References: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> In-Reply-To: <20251220-rust_serdev-v1-0-e44645767621@posteo.de> To: Rob Herring , Greg Kroah-Hartman , Jiri Slaby , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Kari Argillander Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Markus Probst X-Developer-Signature: v=1; a=openpgp-sha256; l=644; i=markus.probst@posteo.de; h=from:subject:message-id; bh=qxps9LuzVhP3NZF39g3++QSsoAR3m1O2TDzQHDWwPzU=; b=owEBiQJ2/ZANAwAIATR2H/jnrUPSAcsmYgBpRu5yYWWsv4r3tm94HeA/rQML2zdP3U9weq9Wp B2H9L+l3SGJAk8EAAEIADkWIQSCdBjE9KxY53IwxHM0dh/4561D0gUCaUbuchsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMSwyLDIACgkQNHYf+OetQ9JTBw//aBbF+t0bjgpMyeOfm3ho1wXIXVz8v6v sHSAnsiZ3Fn+lCdEJjyeTl6E+h0GXHEs3/RGKcdaSeid5WPrhZbzMm/s/Al/SqFigKjxKX3nuJt /0M9lEqJ4mimTYFWnEf4Wx4aH7iqYf/1t38emdKXTNEKEU4QdU0YDcJOyCD3EWCxtUVUQkFneEn F6r6+ms7SEqAbHloXTg0azMs7W+EIa3mmQifnYy37Bhg46kk+GwzVtDZN/Ebjm6ICVgJRuUoq63 wKVNHeq8vOhcjMugsoslQoe/utANXJBB6xlEbGCz9bJo/BEamfdj1VnlqTJ2ntvNVnFXlyO/sbX kPPLy0aECuMi4CreZi0gP+L89DsTmE78pLARNdGeWcyEIGOQOZErY4S4u0kC2SCWDPGz4ZvGwY5 /YoPofQajql4tpeu285JGSw0ZHJ31fGEsGbVssa1fU3gIOQ+BfVjQ2wKLIC0laQsquhNYRAyhML u144sdxM0j/dJm1b8IMXVrRnufAioa/oUbGPSUmVcNwmXjf33fxKDjf7GR/6jHfuDUgQ1SUSkMW g7Y+fDqMlet0Z4Vx4fVShs0OsruaVUu6imxNY4TkobwBd8CJoKx9O18czTOewyS/tnJoa9Oc1DI +TSFLvZmhJeUGwr4cR2re4FeaKHVsQVim/Vzpq2GCpmJXunTx4Es= X-Developer-Key: i=markus.probst@posteo.de; a=openpgp; fpr=827418C4F4AC58E77230C47334761FF8E7AD43D2 Autocrypt: addr=markus.probst@posteo.de; prefer-encrypt=mutual; keydata=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 aBeNN4ijKZchBXHPgVx+YtWRHfcm4l8= OpenPGP: url=https://posteo.de/keys/markus.probst@posteo.de.asc; preference=encrypt Adds files related to the serial device bus rust abstractions to the MAINTAINERS file. Signed-off-by: Markus Probst --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..929b1c534173 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23718,6 +23718,9 @@ 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 +F: samples/rust/rust_driver_serdev.rs =20 SERIAL IR RECEIVER M: Sean Young --=20 2.51.2