* [PATCH v7 0/4] rust: add basic serial device bus abstractions
From: Markus Probst via B4 Relay @ 2026-04-29 18:21 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
This patch series adds the serdev device bus rust abstraction into the
kernel.
This abstraction will be used by a driver,
which targets the MCU devices in Synology devices.
Kari Argillander also messaged me, stating that he wants to write a
watchdog driver with this abstraction (needing initial device data).
@Rob: Are you willing to maintain these rust abstractions yourself,
as you are the expert on this subsystem, otherwise I would take care of
it with a "SERIAL DEVICE BUS [RUST]" section in the MAINTAINERS file. In
the second case, I assume you are going to pick those patches as-is into
your tree, after they have been reviewed?
This series depends on [1].
[1]
https://lore.kernel.org/driver-core/20260427221155.2144848-1-dakr@kernel.org/
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v7:
- adapted to driver-lifetime patch series
- Link to v6: https://patch.msgid.link/20260427-rust_serdev-v6-0-173798d5e1a3@posteo.de
Changes in v6:
- rebased onto v7.1-rc1
- Link to v5: https://patch.msgid.link/20260420-rust_serdev-v5-0-57e8ba0519f3@posteo.de
Changes in v5:
- fix typo in documentation
- Link to v4: https://lore.kernel.org/r/20260411-rust_serdev-v4-0-845e960c6627@posteo.de
Changes in v4:
- fixed not selecting rust serdev abstraction in sample
- Link to v3: https://lore.kernel.org/r/20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de
Changes in v3:
- fix vertical import style
- add Kconfig entry for the rust abstraction
- fix documentation in include/linux/serdev.h
- rename private_data to rust_private_data
- fix `complete_all` <-> `wait_for_completion` typo
- move drvdata_borrow call after the completion
- Link to v2: https://lore.kernel.org/r/20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de
Changes in v2:
- fix documentation in `serdev::Driver::write` and
`serdev::Driver::write_all`
- remove use of `dev_info` in probe from the sample
- remove `properties_parse` from the sample
- add optional `baudrate` property to the sample
- remove 1. patch
- remove `TryFrom<&device::Device<Ctx>> for &serdev::Device<Ctx>`
implementation
- fix import style
- add patch to return reference in `devres::register` to fix safety
issue
- add patch to add private data to serdev_device, to fix
`Device.drvdata()` from failing
- simplify abstraction by removing ability to receive the initial
transmission. It may be added later in a separate patch series if
needed.
- Link to v1: https://lore.kernel.org/r/20251220-rust_serdev-v1-0-e44645767621@posteo.de
---
Markus Probst (4):
rust: devres: return reference in `devres::register`
serdev: add rust private data to serdev_device
rust: add basic serial device bus abstractions
samples: rust: add Rust serial device bus sample device driver
drivers/tty/serdev/Kconfig | 7 +
include/linux/serdev.h | 15 +-
rust/bindings/bindings_helper.h | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/serdev.c | 22 ++
rust/kernel/cpufreq.rs | 3 +-
rust/kernel/devres.rs | 15 +-
rust/kernel/drm/driver.rs | 3 +-
rust/kernel/lib.rs | 2 +
rust/kernel/serdev.rs | 571 +++++++++++++++++++++++++++++++++++++
samples/rust/Kconfig | 11 +
samples/rust/Makefile | 1 +
samples/rust/rust_driver_serdev.rs | 86 ++++++
13 files changed, 728 insertions(+), 10 deletions(-)
---
base-commit: 5e9b7d093f3f77cb0af4409559e3d139babfb443
change-id: 20251217-rust_serdev-ee5481e9085c
prerequisite-message-id: 20260427221002.2143861-1-dakr@kernel.org
prerequisite-patch-id: 925690ac15ddd64777b2da34b343d2547bbf79e8
prerequisite-patch-id: 67318671a5eed5fb4ad23a450f1cf0e442bf8ca2
prerequisite-message-id: 20260427221155.2144848-1-dakr@kernel.org
prerequisite-patch-id: 87127047dbf7d948ae98f1ff0f4a782214a516e2
prerequisite-patch-id: 7664165039fb510368a721edfe60f5b67449b7b6
prerequisite-patch-id: 1e8c97720bab512d8646409c129edd6047164c4c
prerequisite-patch-id: f0fdd1e9912f31a3945f1c0f227dcb64cf71885d
prerequisite-patch-id: be515f7e13f7800eaadb3ed85dec6a9ddb733828
prerequisite-patch-id: 3a3c7749e017d9335f58497404d1350e96caf471
prerequisite-patch-id: 3526c9154f581497a11465b936d83ef61a875454
prerequisite-patch-id: 65d8c757b52475c2acc7d22ddc92cd3f0152b55d
prerequisite-patch-id: 4bd31f1414d5248dc080884caadf5f21684a8427
prerequisite-patch-id: 7beadbb0da3e589ed86d12f512d1c83427dd82b4
prerequisite-patch-id: f04f5427c592cb078c08ee071b965ef6fd4a9a48
prerequisite-patch-id: 64c0098c3d2420f82c89f44fccd3eed459557bfc
prerequisite-patch-id: 3294340ecd964b8e3e87e5a787ff1ffa28e0b698
prerequisite-patch-id: b2c14d06f068dc6a52814fc55d1188737dc29861
prerequisite-patch-id: c7fd96983b606bc42cc5b003022b8fa2bb1c0c34
prerequisite-patch-id: 8e9f4c2d5521fb6cdc5bed2d6661f4bbed5eb63d
prerequisite-patch-id: d868f7925ee9dfe44c5ddf50f4deb50263ff203f
prerequisite-patch-id: 5677248690560ec45e512a8cdbbf8aecfad62d77
prerequisite-patch-id: 5d9674da330ee9d0f4c91edb8acc20727ba1cd9c
prerequisite-patch-id: d5f29453bfd2e3354fb96b57d7a55521bedb2b0b
prerequisite-patch-id: 9d6f96e26fe651304e950766d6bc2006dd33e86a
prerequisite-patch-id: c7a73c58c6d4ca8556d29ce651ad78cc647f6f31
prerequisite-patch-id: c5f35030637e78ab6f5fa489172f1506493fbae1
prerequisite-patch-id: aad19e48d4fa45734050bba927a5b72d6def9673
^ permalink raw reply
* [PATCH v7 2/4] serdev: add rust private data to serdev_device
From: Markus Probst via B4 Relay @ 2026-04-29 18:21 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
In-Reply-To: <20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de>
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
* [PATCH v7 4/4] samples: rust: add Rust serial device bus sample device driver
From: Markus Probst via B4 Relay @ 2026-04-29 18:21 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
In-Reply-To: <20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de>
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>
---
samples/rust/Kconfig | 11 +++++
samples/rust/Makefile | 1 +
samples/rust/rust_driver_serdev.rs | 86 ++++++++++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+)
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..956ca62831fe
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,86 @@
+// 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<'a> serdev::Driver<'a> for SampleDriver {
+ type IdInfo = ();
+ 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(
+ sdev: &'a serdev::Device<Core>,
+ _info: Option<&'a Self::IdInfo>,
+ ) -> impl PinInit<Self, Error> + 'a {
+ 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(sdev: &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
* [PATCH v7 3/4] rust: add basic serial device bus abstractions
From: Markus Probst via B4 Relay @ 2026-04-29 18:21 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
In-Reply-To: <20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de>
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>
---
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 | 571 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 604 insertions(+)
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..28c6cf16a3a7
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,571 @@
+// 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::{
+ ForLt,
+ 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.
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_serdev_device_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
+
+// SAFETY:
+// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
+// - `F::Of<'static>` is the stored 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<F> driver::DriverLayout for Adapter<F>
+where
+ F: ForLt + 'static,
+ for<'a> F::Of<'a>: Driver<'a>,
+{
+ type DriverType = bindings::serdev_device_driver;
+ type DriverData = F;
+ 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<F> driver::RegistrationOps for Adapter<F>
+where
+ F: ForLt + 'static,
+ for<'a> F::Of<'a>: Driver<'a>,
+{
+ unsafe fn register(
+ sdrv: &Opaque<Self::DriverType>,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ let of_table = match <F::Of<'static> as Driver<'static>>::OF_ID_TABLE {
+ Some(table) => table.as_ptr(),
+ None => core::ptr::null(),
+ };
+
+ let acpi_table = match <F::Of<'static> as Driver<'static>>::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<F> Adapter<F>
+where
+ F: ForLt + 'static,
+ for<'a> F::Of<'a>: Driver<'a>,
+{
+ const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
+ receive_buf: if <F::Of<'static> as Driver<'static>>::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>>() };
+
+ 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 info = <Self as driver::Adapter<'_>>::id_info(sdev.as_ref());
+ let data = <F::Of<'_> as Driver<'_>>::probe(sdev, info);
+ let result = sdev.as_ref().set_drvdata::<F>(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 drvdata has been set.
+ let data = unsafe { sdev.as_ref().drvdata_borrow::<F>() };
+
+ <F::Of<'_> as Driver<'_>>::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>>`.
+ let data = unsafe { sdev.as_ref().drvdata_borrow::<F>() };
+
+ // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
+ let buf = unsafe { core::slice::from_raw_parts(buf, length) };
+
+ <F::Of<'_> as Driver<'_>>::receive(sdev, data, buf)
+ }
+}
+
+impl<'a, F> driver::Adapter<'a> for Adapter<F>
+where
+ F: ForLt + 'static,
+ for<'b> F::Of<'b>: Driver<'b>,
+{
+ type IdInfo = <F::Of<'a> as Driver<'a>>::IdInfo;
+
+ fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+ <F::Of<'a> as Driver<'a>>::OF_ID_TABLE
+ }
+
+ fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+ <F::Of<'a> as Driver<'a>>::ACPI_ID_TABLE
+ }
+}
+
+/// Declares a kernel module that exposes a single serial device bus device driver.
+///
+/// The `type` field accepts a driver type, optionally with a lifetime placeholder `'_` for
+/// lifetime-parameterized drivers. The macro wraps it in [`ForLt!`] automatically.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_serdev_device_driver! {
+/// type: MyDriver,
+/// name: "Module name",
+/// authors: ["Author name"],
+/// description: "Description",
+/// license: "GPL v2",
+/// }
+/// ```
+///
+/// [`ForLt!`]: macro@ForLt
+/// [`ForLt`]: trait@ForLt
+#[macro_export]
+macro_rules! module_serdev_device_driver {
+ (type: $type:ty, $($rest:tt)*) => {
+ $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, {
+ type: $crate::types::ForLt!($type),
+ $($rest)*
+ });
+ };
+}
+
+/// 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<'a> serdev::Driver<'a> for MyDriver {
+/// type IdInfo = ();
+/// 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(
+/// sdev: &'a serdev::Device<Core>,
+/// _id_info: Option<&'a Self::IdInfo>,
+/// ) -> impl PinInit<Self, Error> + 'a {
+/// sdev.set_baudrate(115200);
+/// sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
+/// Ok(MyDriver)
+/// }
+/// }
+///```
+#[vtable]
+pub trait Driver<'a>: Send {
+ /// 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 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(
+ sdev: &'a Device<device::Core>,
+ id_info: Option<&'a Self::IdInfo>,
+ ) -> impl PinInit<Self, Error> + 'a;
+
+ /// 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 `Self::drop`.
+ fn unbind(sdev: &'a Device<device::Core>, this: Pin<&'a Self>) {
+ 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(sdev: &'a Device<device::Bound>, this: Pin<&'a Self>, 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
* [PATCH v7 1/4] rust: devres: return reference in `devres::register`
From: Markus Probst via B4 Relay @ 2026-04-29 18:21 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
In-Reply-To: <20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de>
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 ac59cdfd633c..1da80dc266dd 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -1052,7 +1052,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 6f3c58355d10..d01e186be381 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -438,15 +438,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
* [PATCH v7 0/6] Add support for ZTE zx297520v3
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger, Krzysztof Kozlowski
Hi,
This is a follow-up on my RFC patches from January [0] for ZTE's
zx297520v3 chipset. This chipset is popular in cheap LTE-to-wifi routers
sold in developing countries. My goal is to run OpenWRT on them. I made
more progress in more work on this SoC and it is time to get serious
about code review and upstreaming.
Since my version in January I managed to get more hardware running: SPI,
I2C, PMIC with real time clock and voltage regulators, Watchdog. LTE is
not working yet, but I am able to start the coprocessor that handles it
and talk to it via mailbox + shared memory. Wifi is working on a few
more devices. Since WiFi, USB and Ethernet are working, the devices can
have actual use with OpenWRT even without LTE.
Another hacker created a free software program to talk to the USB loader
[1] and boot U-Boot and Linux without modifying the on disk files. At
the moment it needs a proprietary blob, so my documentation is
emphasising booting with the on-device U-Boot.
This patchset here is mostly unmodified from the version I sent in
January. It is the bare minimum to get an interactive shell working on
the UART. Future patches can be found on my git repository [2] for those
curious to peek ahead. The first 30 patches are in reasonable shape, but
the further you go the more cleanup is necessary. I expect all of the
patches go require a few rounds of feedback though.
My plan for upstreaming is largly this:
1) This bare minimum boot patchset
2) Add clock and pinctrl drivers
3) Add standard hardware to the device tree
4) Add zx29 specific drivers one by one: Watchdog, spi, i2c, DMA, PMIC,
battery
5) SDIO backend for rtl8xxxu
6) rproc, mailbox and rpmsg
I am willing to maintain support for the SoC within reason. My patches
add myself as maintainer. This is a hobby project for me though, keep
that in mind if you want to ship a commercial product with these SoCs
and upstreaming Linux.
Cheers,
Stefan
0: https://lists.infradead.org/pipermail/linux-arm-kernel/2026-January/1099306.html
1: https://github.com/zx297520v3-mainline/zx297520v3-loader
2: https://gitlab.com/stefandoesinger/zx297520-kernel/
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Changes in v7:
Fix line order in mach-zte/Kconfig
Use "zx297520v3 SoC" as the option name for CONFIG_SOC_ZX297520V3
Changes in v6:
Squashed DT commits into one
Removed for-now unused board DT bindings
Add "zte" to DT patch subject
Regenerate the defconfig with make savedefconfig
- Link to v5: https://lore.kernel.org/r/20260421-send-v5-0-ace038e63515@gmail.com
v5:
Spelling fixes
Renamed dlink-dwr-932m.dts to zx297520v3-dlink-dwr932m.dts
DT binding indentation fixes
Use a manufacturer 0x8b for the UART, fix patch prefix
Declare all UARTs, remove uart aliases for now
Consistent license declarations. I made every new file except the DT
binding GPL-2.0-only but I don't particularly mind GPL-2.0-or-later
either.
- Link to v4: https://lore.kernel.org/r/20260416-send-v4-0-e19d02b944ec@gmail.com
v4: rename zx29.yaml to zte.yaml and add board enums
v3: Remove [RFC] tag, add defconfig
v2: checkpatch.pl fixes
---
Stefan Dösinger (6):
dt-bindings: arm: zte: Add D-Link DWR932M board based on zx297520v3 SoC
ARM: zte: Add zx297520v3 platform support
ARM: zte: Add support for zx29 low level debug
amba/serial: amba-pl011: Bring back zx29 UART support
ARM: dts: zte: Add D-Link DWR-932M support
ARM: zte: defconfig: Add a zx29 defconfig file
Documentation/arch/arm/zte/zx297520v3.rst | 158 +++++++++++++++++++++
Documentation/devicetree/bindings/arm/zte.yaml | 26 ++++
MAINTAINERS | 7 +
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 12 ++
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/zte/Makefile | 3 +
arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dts | 22 +++
arch/arm/boot/dts/zte/zx297520v3.dtsi | 103 ++++++++++++++
arch/arm/configs/zx29_defconfig | 54 +++++++
arch/arm/include/debug/pl01x.S | 7 +
arch/arm/mach-zte/Kconfig | 26 ++++
arch/arm/mach-zte/Makefile | 2 +
arch/arm/mach-zte/zx297520v3.c | 19 +++
drivers/tty/serial/amba-pl011.c | 42 ++++++
16 files changed, 485 insertions(+)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260416-send-5c08e095e5c9
Best regards,
--
Stefan Dösinger <stefandoesinger@gmail.com>
^ permalink raw reply
* [PATCH v7 1/6] dt-bindings: arm: zte: Add D-Link DWR932M board based on zx297520v3 SoC
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger, Krzysztof Kozlowski
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This adds a new binding file for ZTE, containing their zx297520v3 SoC
and one board (D-Link DWR-932M) based on it.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Changelog:
v6:
Removed extra boards, I'll add them when submitting their individual
DTS files. Rephrase the subject to add "zte" and remove the redundant
use of "binding".
Moved the devicetree bindings patch ahead of the implementation patches.
Moved the MAINTAINERS section from "ZX29" to "ARM/ZTE".
---
Documentation/devicetree/bindings/arm/zte.yaml | 26 ++++++++++++++++++++++++++
MAINTAINERS | 4 ++++
2 files changed, 30 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/zte.yaml b/Documentation/devicetree/bindings/arm/zte.yaml
new file mode 100644
index 000000000000..f028d2cec7ab
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/zte.yaml
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/zte.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ZTE zx platforms
+
+maintainers:
+ - Stefan Dösinger <stefandoesinger@gmail.com>
+
+description: |
+ ARM platforms using SoCs designed by ZTE. Currently this supports devices
+ based on the zx297520v3 SoC which is found in LTE routers.
+
+properties:
+ $nodename:
+ const: "/"
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - dlink,dwr932m
+ - const: zte,zx297520v3
+
+additionalProperties: true
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..b768b9da37a4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3772,6 +3772,10 @@ F: drivers/video/fbdev/vt8500lcdfb.*
F: drivers/video/fbdev/wm8505fb*
F: drivers/video/fbdev/wmt_ge_rops.*
+ARM/ZTE ZX29 SOC SUPPORT
+M: Stefan Dösinger <stefandoesinger@gmail.com>
+F: Documentation/devicetree/bindings/arm/zte.yaml
+
ARM/ZYNQ ARCHITECTURE
M: Michal Simek <michal.simek@amd.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
--
2.53.0
^ permalink raw reply related
* [PATCH v7 2/6] ARM: zte: Add zx297520v3 platform support
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger, Krzysztof Kozlowski
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This SoC is used in low end LTE-to-WiFi routers, for example some D-Link
DWR 932 revisions, ZTE K10, ZLT S10 4G, but also models that are branded
and sold by ISPs themselves. They are widespread in Africa, China,
Russia and Eastern Europe.
This SoC is a relative of the zx296702 and zx296718 that had some
upstream support until commit 89d4f98ae90d ("ARM: remove zte zx
platform"). My eventual goal is to enable OpenWRT to run on these
devices.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Documentation/arch/arm/zte/zx297520v3.rst | 158 ++++++++++++++++++++++++++++++
MAINTAINERS | 1 +
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/mach-zte/Kconfig | 26 +++++
arch/arm/mach-zte/Makefile | 2 +
arch/arm/mach-zte/zx297520v3.c | 19 ++++
7 files changed, 209 insertions(+)
diff --git a/Documentation/arch/arm/zte/zx297520v3.rst b/Documentation/arch/arm/zte/zx297520v3.rst
new file mode 100644
index 000000000000..6621ea72769f
--- /dev/null
+++ b/Documentation/arch/arm/zte/zx297520v3.rst
@@ -0,0 +1,158 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+====================================
+Booting Linux on ZTE zx297520v3 SoCs
+====================================
+
+...............................................................................
+
+Author: Stefan Dösinger
+
+Date : 27 Jan 2026
+
+1. Hardware description
+---------------------------
+Zx297520v3 SoCs use a 64 bit capable Cortex-A53 CPU and GICv3, although they
+run in arm32 mode only. The CPU has support EL3, but no hypervisor (EL2) and
+it seems to lack VFP and NEON.
+
+The SoC is used in a number of cheap LTE to WiFi routers, both battery powered
+MiFis and stationary CPEs. In addition to the CPU these devices usually have
+64 MB Ram (although some is shared with the LTE chip), 128 MB NAND flash, an
+SDIO connected RTL8192-type Wifi chip limited to 2.4 ghz operation, USB 2,
+and buttons. Devices with as low as 32 MB or as high as 128 MB ram exist, as
+do devices with 8 or 16 MB of NOR flash.
+
+Some devices, especially the stationary ones, have 100 mbit Ethernet and an
+Ethernet switch.
+
+Usually the devices have LEDs for status indication, although some have SPI or
+I2C connected displays
+
+Some have an SD card slot. If it exists, it is a better choice for the root
+file system because it easily outperforms the built-in NAND.
+
+The LTE interface runs on a separate DSP called ZSP880. It is probably derived
+from LSI ZSPs and has an undocumented instruction set. The ZSP communicates
+with the main CPU via SRAM and DRAM and a mailbox hardware that can generate
+IRQs on either ends.
+
+There is also a Cortex M0 CPU, which is responsible for early HW initialization
+and starting the Cortex A53 CPU. It does not have any essential purpose once
+U-Boot is started. A SRAM-Based handover protocol exists to run custom code on
+this CPU.
+
+2. Booting via USB
+---------------------------
+
+The Boot ROM has support for booting custom code via USB. This mode can be
+entered by connecting a Boot PIN to GND or by modifying the third byte on NAND
+(set it to anything other than 0x5A aka 'Z'). A free software tool to start
+custom U-Boot and kernels can be found here:
+
+https://github.com/zx297520v3-mainline/zx297520v3-loader
+
+If USB download mode is entered but no boot commands are sent through USB, the
+device will proceed to boot normally after a few seconds. It is therefore
+possible to enable USB boot permanently and still leave the default boot files
+in place.
+
+3. Building for built-in U-Boot
+---------------------------
+The devices come with an ancient U-Boot that loads legacy uImages from NAND and
+boots them without a chance for the user to interrupt. The images are stored in
+files ap_cpuap.bin and ap_recovery.bin on a jffs2 partition named imagefs,
+usually mtd4. A file named "fotaflag" switches between the two modes.
+
+In addition to the uImage header, those files have a 384 byte signature header,
+which is used for authenticating the images on some devices. Most devices have
+this authentication disabled and it is enough to pad the uImage files with 384
+zero bytes.
+
+Builtin U-Boot also poorly sets up the CPU. Read the next section for details
+on this. It has no support for loading DTBs, so CONFIG_ARM_APPENDED_DTB is
+needed.
+
+So to build an image that boots from NAND the following steps are necessary:
+
+1) Patch the assembly code from section 3 into arch/arm/kernel/head.S.
+2) make zx29_defconfig
+3) make [-j x]
+4) cat arch/arm/boot/zImage arch/arm/boot/dts/zte/[device].dtb > kernel+dtb
+5) mkimage -A arm -O linux -T kernel -C none -a 0x20008000 -d kernel+dtb uimg
+6) dd if=/dev/zero bs=1 count=384 of=ap_recovery.bin
+7) cat uimg >> ap_recovery.bin
+8) Place this file onto imagefs on the device. Delete ap_cpuap.bin if the
+free space is not enough.
+9) Create the file fotaflag: echo -n FOTA-RECOVERY > fotaflag
+
+For development, booting ap_recovery.bin is recommended because the normal boot
+mode arms the watchdog before starting the kernel.
+
+4. CPU and GIC Setup
+---------------------------
+
+Generally CPU and GICv3 need to be set up according to the requirements spelled
+out in Documentation/arch/arm64/booting.rst. For zx297520v3 this means:
+
+1. GICD_CTLR.DS=1 to disable GIC security
+2. Enable access to ICC_SRE
+3. Disable trapping IRQs into monitor mode
+4. Configure EL2 and below to run in insecure mode.
+5. Configure timer PPIs to active-low.
+
+The kernel sources provided by ZTE do not boot either (interrupts do not work
+at all). They are incomplete in other aspects too, so it is assumed that there
+is some workaround similar to the one described in this document somewhere in
+the binary blobs.
+
+The assembly code below is given as an example of how to achieve this:
+
+```
+#include <linux/irqchip/arm-gic-v3.h>
+#include <asm/assembler.h>
+#include <asm/cp15.h>
+
+@ This allows EL1 to handle ints hat are normally handled by EL2/3.
+ldr r3, =0xf2000000
+ldr r4, =(GICD_CTLR_ARE_NS | GICD_CTLR_DS)
+str r4, [r3]
+
+cps #MON_MODE
+
+@ Work in non-secure physical address space: SCR_EL3.NS = 1. At least the UART
+@ seems to respond only to non-secure addresses. I have taken insipiration from
+@ Raspberry pi's armstub7.S here.
+@
+@ ARM docs say modify this bit in monitor mode only...
+mov r3, #0x131 @ non-secure, Make F, A bits in CPSR writeable
+ @ Allow hypervisor call.
+mcr p15, 0, r3, c1, c1, 0
+
+@ AP_PPI_MODE_REG: Configure timer PPIs (10, 11, 13, 14) to active-low.
+ldr r3, =0xF22020a8
+ldr r4, =0x50
+str r4, [r3]
+ldr r3, =0xF22020ac
+ldr r4, =0x14
+str r4, [r3]
+
+@ Enable EL2 access to ICC_SRE (bit 3, ICC_SRE_EL3.Enable). Enable system reg
+@ access to GICv3 registers (bit 0, ICC_SRE_EL3.SRE) for EL1 and EL3.
+mrc p15, 6, r3, c12, c12, 5 @ ICC_SRE_EL3
+orr r3, #0x9 @ FIXME: No defines for SRE_EL3 values?
+mcr p15, 6, r3, c12, c12, 5
+mrc p15, 0, r3, c12, c12, 5 @ ICC_SRE_EL1
+orr r3, #(ICC_SRE_EL1_SRE)
+mcr p15, 0, r3, c12, c12, 5
+
+@ Like ICC_SRE_EL3, enable EL1 access to ICC_SRE and system register access
+@ for EL2.
+mrc p15, 4, r3, c12, c9, 5 @ ICC_SRE_EL2 aka ICC_HSRE
+orr r3, r3, #(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE)
+mcr p15, 4, r3, c12, c9, 5
+isb
+
+@ Back to SVC mode. TODO: Doesn't safe_svcmode_maskall do this for us anyway?
+cps #SVC_MODE
+```
diff --git a/MAINTAINERS b/MAINTAINERS
index b768b9da37a4..e707176c2114 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3775,6 +3775,7 @@ F: drivers/video/fbdev/wmt_ge_rops.*
ARM/ZTE ZX29 SOC SUPPORT
M: Stefan Dösinger <stefandoesinger@gmail.com>
F: Documentation/devicetree/bindings/arm/zte.yaml
+F: arch/arm/mach-zte/
ARM/ZYNQ ARCHITECTURE
M: Michal Simek <michal.simek@amd.com>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ec33376f8e2b..4217ed704e48 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -464,6 +464,8 @@ source "arch/arm/mach-versatile/Kconfig"
source "arch/arm/mach-vt8500/Kconfig"
+source "arch/arm/mach-zte/Kconfig"
+
source "arch/arm/mach-zynq/Kconfig"
# ARMv7-M architecture
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b7de4b6b284c..573813ef5e77 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -223,6 +223,7 @@ machine-$(CONFIG_ARCH_SUNXI) += sunxi
machine-$(CONFIG_ARCH_TEGRA) += tegra
machine-$(CONFIG_ARCH_U8500) += ux500
machine-$(CONFIG_ARCH_VT8500) += vt8500
+machine-$(CONFIG_ARCH_ZTE) += zte
machine-$(CONFIG_ARCH_ZYNQ) += zynq
machine-$(CONFIG_PLAT_VERSATILE) += versatile
machine-$(CONFIG_PLAT_SPEAR) += spear
diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig
new file mode 100644
index 000000000000..4effbe3f8215
--- /dev/null
+++ b/arch/arm/mach-zte/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig ARCH_ZTE
+ bool "ZTE zx family"
+ depends on ARCH_MULTI_V7
+ help
+ Support for ZTE zx-based family of processors.
+
+if ARCH_ZTE
+
+config SOC_ZX297520V3
+ bool "zx297520v3 SoC"
+ default y if ARCH_ZTE
+ select ARM_GIC_V3
+ select ARM_AMBA
+ select HAVE_ARM_ARCH_TIMER
+ select PM_GENERIC_DOMAINS if PM
+ help
+ Support for ZTE zx297520v3 SoC. It is a single core SoC used in cheap
+ LTE to WiFi routers. These devices can be identified by the occurrence
+ of the string "zx297520v3" in the boot output and /proc/cpuinfo of
+ their stock firmware.
+
+ Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot
+ the kernel.
+
+endif
diff --git a/arch/arm/mach-zte/Makefile b/arch/arm/mach-zte/Makefile
new file mode 100644
index 000000000000..1bfe4fddd6af
--- /dev/null
+++ b/arch/arm/mach-zte/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SOC_ZX297520V3) += zx297520v3.o
diff --git a/arch/arm/mach-zte/zx297520v3.c b/arch/arm/mach-zte/zx297520v3.c
new file mode 100644
index 000000000000..c11c7e836f91
--- /dev/null
+++ b/arch/arm/mach-zte/zx297520v3.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2026 Stefan Dösinger
+ */
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+static const char *const zx297520v3_dt_compat[] __initconst = {
+ "zte,zx297520v3",
+ NULL,
+};
+
+DT_MACHINE_START(ZX, "ZTE zx297520v3 (Device Tree)")
+ .dt_compat = zx297520v3_dt_compat,
+MACHINE_END
--
2.53.0
^ permalink raw reply related
* [PATCH v7 3/6] ARM: zte: Add support for zx29 low level debug
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This is based on the removed zx29 code. A separate (more complicated)
patch will re-add the register map to the pl011 serial driver.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
I am unsure about the virtual address. It doesn't seem to matter, as
long as it is a valid address. This address is based on the old removed
code. Is there a rule-of-thumb physical to virtual mapping I can use to
give a sensible default value?
---
arch/arm/Kconfig.debug | 12 ++++++++++++
arch/arm/include/debug/pl01x.S | 7 +++++++
2 files changed, 19 insertions(+)
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 366f162e147d..98d8a5a60048 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1331,6 +1331,16 @@ choice
This option selects UART0 on VIA/Wondermedia System-on-a-chip
devices, including VT8500, WM8505, WM8650 and WM8850.
+ config DEBUG_ZTE_ZX
+ bool "Kernel low-level debugging via zx29 UART"
+ select DEBUG_UART_PL01X
+ depends on ARCH_ZTE
+ help
+ Say Y here if you are enabling ZTE zx297520v3 SOC and need
+ debug UART support. This UART is a PL011 with different
+ register addresses. The UART for boot messages on zx29 boards
+ is usually UART1 and is operating at 921600 8N1.
+
config DEBUG_ZYNQ_UART0
bool "Kernel low-level debugging on Xilinx Zynq using UART0"
depends on ARCH_ZYNQ
@@ -1545,6 +1555,7 @@ config DEBUG_UART_8250
config DEBUG_UART_PHYS
hex "Physical base address of debug UART"
+ default 0x01408000 if DEBUG_ZTE_ZX
default 0x01c28000 if DEBUG_SUNXI_UART0
default 0x01c28400 if DEBUG_SUNXI_UART1
default 0x01d0c000 if DEBUG_DAVINCI_DA8XX_UART1
@@ -1701,6 +1712,7 @@ config DEBUG_UART_VIRT
default 0xf31004c0 if DEBUG_MESON_UARTAO
default 0xf4090000 if DEBUG_LPC32XX
default 0xf4200000 if DEBUG_GEMINI
+ default 0xf4708000 if DEBUG_ZTE_ZX
default 0xf6200000 if DEBUG_PXA_UART1
default 0xf7000000 if DEBUG_SUN9I_UART0
default 0xf7000000 if DEBUG_S3C64XX_UART && DEBUG_S3C_UART0
diff --git a/arch/arm/include/debug/pl01x.S b/arch/arm/include/debug/pl01x.S
index c7e02d0628bf..0c7bfa4c10db 100644
--- a/arch/arm/include/debug/pl01x.S
+++ b/arch/arm/include/debug/pl01x.S
@@ -8,6 +8,13 @@
*/
#include <linux/amba/serial.h>
+#ifdef CONFIG_DEBUG_ZTE_ZX
+#undef UART01x_DR
+#undef UART01x_FR
+#define UART01x_DR 0x04
+#define UART01x_FR 0x14
+#endif
+
#ifdef CONFIG_DEBUG_UART_PHYS
.macro addruart, rp, rv, tmp
ldr \rp, =CONFIG_DEBUG_UART_PHYS
--
2.53.0
^ permalink raw reply related
* [PATCH v7 4/6] amba/serial: amba-pl011: Bring back zx29 UART support
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This is based on code removed in commit 89d4f98ae90d ("ARM: remove zte
zx platform"). I did not bring back the zx29-uart .compatible as the
arm,primecell-periphid does the job.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Changes since v4:
Use ZTE's JEDEC ID instead of 0xfe for the DT-Provided AMBA ID.
---
drivers/tty/serial/amba-pl011.c | 42 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 7f17d288c807..f24cc403d9e0 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -216,6 +216,38 @@ static struct vendor_data vendor_st = {
.get_fifosize = get_fifosize_st,
};
+static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = ZX_UART011_DR,
+ [REG_FR] = ZX_UART011_FR,
+ [REG_LCRH_RX] = ZX_UART011_LCRH,
+ [REG_LCRH_TX] = ZX_UART011_LCRH,
+ [REG_IBRD] = ZX_UART011_IBRD,
+ [REG_FBRD] = ZX_UART011_FBRD,
+ [REG_CR] = ZX_UART011_CR,
+ [REG_IFLS] = ZX_UART011_IFLS,
+ [REG_IMSC] = ZX_UART011_IMSC,
+ [REG_RIS] = ZX_UART011_RIS,
+ [REG_MIS] = ZX_UART011_MIS,
+ [REG_ICR] = ZX_UART011_ICR,
+ [REG_DMACR] = ZX_UART011_DMACR,
+};
+
+static unsigned int get_fifosize_zte(struct amba_device *dev)
+{
+ return 16;
+}
+
+static struct vendor_data vendor_zte = {
+ .reg_offset = pl011_zte_offsets,
+ .access_32b = true,
+ .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
+ .fr_busy = ZX_UART01x_FR_BUSY,
+ .fr_dsr = ZX_UART01x_FR_DSR,
+ .fr_cts = ZX_UART01x_FR_CTS,
+ .fr_ri = ZX_UART011_FR_RI,
+ .get_fifosize = get_fifosize_zte,
+};
+
/* Deals with DMA transactions */
struct pl011_dmabuf {
@@ -3081,6 +3113,16 @@ static const struct amba_id pl011_ids[] = {
.mask = 0x00ffffff,
.data = &vendor_st,
},
+ {
+ /* This is an invented ID. The actual hardware that contains
+ * these ZTE UARTs (zx29 boards) has no AMBA PIDs stored. ZTE
+ * JEDEC ID (ignoring banks) and the "011" part number as used
+ * by ARM.
+ */
+ .id = 0x0008c011,
+ .mask = 0x000fffff,
+ .data = &vendor_zte,
+ },
{ 0, 0 },
};
--
2.53.0
^ permalink raw reply related
* [PATCH v7 5/6] ARM: dts: zte: Add D-Link DWR-932M support
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This adds base DT definition for zx297520v3 and one board that consumes it.
The stock kernel does not use the armv7 timer, but it seems to work
fine. The board has other board-specific timers that would need a driver
and I see no reason to bother with them since the arm standard timer
works.
The caveat is the non-standard GIC setup needed to handle the timer's
level-low PPI. This is the responsibility of the boot loader and
documented in Documentation/arch/arm/zte/zx297520v3.rst.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Changes in
v6: Squash board + timer + uart patches into one
v5: Prepend the SoC name in the device specific DTS filename.
v4:
Declare all uarts
Remove the UART aliases for now. I can revisit this when I get my
hands on a board that exposes two UARTs.
---
MAINTAINERS | 1 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/zte/Makefile | 3 +
arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dts | 22 +++++
arch/arm/boot/dts/zte/zx297520v3.dtsi | 103 +++++++++++++++++++++
5 files changed, 130 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e707176c2114..6f51ba1c5ada 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3775,6 +3775,7 @@ F: drivers/video/fbdev/wmt_ge_rops.*
ARM/ZTE ZX29 SOC SUPPORT
M: Stefan Dösinger <stefandoesinger@gmail.com>
F: Documentation/devicetree/bindings/arm/zte.yaml
+F: arch/arm/boot/dts/zte/
F: arch/arm/mach-zte/
ARM/ZYNQ ARCHITECTURE
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index efe38eb25301..28fba538d552 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -39,3 +39,4 @@ subdir-y += unisoc
subdir-y += vt8500
subdir-y += xen
subdir-y += xilinx
+subdir-y += zte
diff --git a/arch/arm/boot/dts/zte/Makefile b/arch/arm/boot/dts/zte/Makefile
new file mode 100644
index 000000000000..f052cfbd636c
--- /dev/null
+++ b/arch/arm/boot/dts/zte/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+dtb-$(CONFIG_SOC_ZX297520V3) += \
+ zx297520v3-dlink-dwr932m.dtb
diff --git a/arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dts b/arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dts
new file mode 100644
index 000000000000..1700f46aba86
--- /dev/null
+++ b/arch/arm/boot/dts/zte/zx297520v3-dlink-dwr932m.dts
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Stefan Dösinger <stefandoesinger@gmail.com>
+ */
+
+/dts-v1/;
+
+#include "zx297520v3.dtsi"
+
+/ {
+ model = "D-Link DWR-932M";
+ compatible = "dlink,dwr932m", "zte,zx297520v3";
+
+ memory@20000000 {
+ device_type = "memory";
+ reg = <0x20000000 0x04000000>;
+ };
+};
+
+&uart1 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
new file mode 100644
index 000000000000..ca65797ed926
--- /dev/null
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Stefan Dösinger <stefandoesinger@gmail.com>
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ reg = <0>;
+ };
+ };
+
+ /* Base bus clock and default for the UART. It will be replaced once a clock driver has
+ * been added.
+ */
+ uartclk: uartclk: uartclk-26000000 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <26000000>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ clock-frequency = <26000000>;
+ interrupt-parent = <&gic>;
+ /* I don't think uboot sets CNTVOFF and the stock kernel doesn't use the
+ * arm timer at all. Since this is a single CPU system I don't think it
+ * really matters that the offset is random though.
+ */
+ arm,cpu-registers-not-fw-configured;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&gic>;
+ ranges;
+
+ /* The GIC has a non-standard way of configuring ints between level-low/level
+ * high or rising edge/falling edge at 0xf2202070 and onwards. See AP_INT_MODE_BASE
+ * and AP_PPI_MODE_REG in the ZTE kernel, although the offsets in the kernel source
+ * seem wrong.
+ *
+ * Everything defaults to active-high/rising edge, but the timer is active-low. We
+ * currently rely on the boot loader to change timer IRQs to active-low for us for
+ * now.
+ */
+ gic: interrupt-controller@f2000000 {
+ compatible = "arm,gic-v3";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf2000000 0x10000>,
+ <0xf2040000 0x20000>;
+ };
+
+ uart0: serial@131000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0018c011>;
+ reg = <0x00131000 0x1000>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&uartclk>, <&uartclk>;
+ clock-names = "uartclk", "apb_pclk";
+ status = "disabled";
+ };
+
+ uart1: serial@1408000 {
+ compatible = "arm,pl011", "arm,primecell";
+ arm,primecell-periphid = <0x0018c011>;
+ reg = <0x01408000 0x1000>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&uartclk>, <&uartclk>;
+ clock-names = "uartclk", "apb_pclk";
+ status = "disabled";
+ };
+
+ uart2: serial@140d000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0018c011>;
+ reg = <0x0140d000 0x1000>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&uartclk>, <&uartclk>;
+ clock-names = "uartclk", "apb_pclk";
+ status = "disabled";
+ };
+ };
+};
--
2.53.0
^ permalink raw reply related
* [PATCH v7 6/6] ARM: zte: defconfig: Add a zx29 defconfig file
From: Stefan Dösinger @ 2026-04-29 19:13 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260429-send-v7-0-b432e00d2db8@gmail.com>
This enables existing drivers for hardware that is present on this board
even if it is not present in the DT yet.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
Changes: v5 to v6: Regenerate the file with make savedefconfig.
An open question: What's the appropriate name? zx29_defconfig?
zte_defconfig? zte_zx29_defconfig? There's e.g. stm32_defconfig without
an extra mention of STMicro in the name.
---
MAINTAINERS | 1 +
arch/arm/configs/zx29_defconfig | 54 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6f51ba1c5ada..5dc52b84cc09 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3776,6 +3776,7 @@ ARM/ZTE ZX29 SOC SUPPORT
M: Stefan Dösinger <stefandoesinger@gmail.com>
F: Documentation/devicetree/bindings/arm/zte.yaml
F: arch/arm/boot/dts/zte/
+F: arch/arm/configs/zx29_defconfig
F: arch/arm/mach-zte/
ARM/ZYNQ ARCHITECTURE
diff --git a/arch/arm/configs/zx29_defconfig b/arch/arm/configs/zx29_defconfig
new file mode 100644
index 000000000000..54fa62ed56e7
--- /dev/null
+++ b/arch/arm/configs/zx29_defconfig
@@ -0,0 +1,54 @@
+CONFIG_SYSVIPC=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_EXPERT=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_ARCH_ZTE=y
+CONFIG_ARM_PSCI=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_CMDLINE="console=ttyAMA0 earlyprintk root=/dev/ram rw"
+CONFIG_CPU_FREQ=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_BINFMT_FLAT=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=4
+CONFIG_SRAM=y
+CONFIG_KEYBOARD_GPIO_POLLED=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_DEV_BUS=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_PINCTRL=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_POWER_RESET=y
+CONFIG_MFD_SYSCON=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_HID is not set
+CONFIG_USB_DWC2=y
+CONFIG_USB_GADGET=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
+CONFIG_JFFS2_FS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v1] serial: qcom-geni: Avoid probing debug console UART without console support
From: kernel test robot @ 2026-04-30 3:42 UTC (permalink / raw)
To: Aniket Randive, gregkh, jirislaby, linux-arm-msm, linux-kernel,
linux-serial, praveen.talari, anup.kulkarni, dmitry.baryshkov,
viken.dadhaniya
Cc: llvm, oe-kbuild-all, Aniket Randive
In-Reply-To: <20260413072501.263871-1-aniket.randive@oss.qualcomm.com>
Hi Aniket,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus usb/usb-testing usb/usb-next usb/usb-linus linus/master v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Aniket-Randive/serial-qcom-geni-Avoid-probing-debug-console-UART-without-console-support/20260423-150710
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20260413072501.263871-1-aniket.randive%40oss.qualcomm.com
patch subject: [PATCH v1] serial: qcom-geni: Avoid probing debug console UART without console support
config: s390-randconfig-002-20260430 (https://download.01.org/0day-ci/archive/20260430/202604301151.rPBXnWLg-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 5bac06718f502014fade905512f1d26d578a18f3)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301151.rPBXnWLg-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604301151.rPBXnWLg-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/tty/serial/qcom_geni_serial.c:1995:43: warning: unused variable 'qcom_geni_console_data' [-Wunused-const-variable]
1995 | static const struct qcom_geni_device_data qcom_geni_console_data = {
| ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/tty/serial/qcom_geni_serial.c:2011:43: warning: unused variable 'sa8255p_qcom_geni_console_data' [-Wunused-const-variable]
2011 | static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
vim +/qcom_geni_console_data +1995 drivers/tty/serial/qcom_geni_serial.c
c4f528795d1add Karthikeyan Ramasubramanian 2018-03-14 1994
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 @1995 static const struct qcom_geni_device_data qcom_geni_console_data = {
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 1996 .console = true,
2aaa43c7077833 Bartosz Golaszewski 2022-12-29 1997 .mode = GENI_SE_FIFO,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 1998 .resources_init = geni_serial_resource_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 1999 .set_rate = geni_serial_set_rate,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2000 .power_state = geni_serial_resource_state,
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2001 };
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2002
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2003 static const struct qcom_geni_device_data qcom_geni_uart_data = {
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2004 .console = false,
2aaa43c7077833 Bartosz Golaszewski 2022-12-29 2005 .mode = GENI_SE_DMA,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2006 .resources_init = geni_serial_resource_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2007 .set_rate = geni_serial_set_rate,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2008 .power_state = geni_serial_resource_state,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2009 };
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2010
abffd1e6c4f1c9 Praveen Talari 2025-11-10 @2011 static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2012 .console = true,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2013 .mode = GENI_SE_FIFO,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2014 .pd_data = {
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2015 .pd_flags = PD_FLAG_DEV_LINK_ON,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2016 .pd_names = (const char*[]) { "power", "perf" },
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2017 .num_pd_names = 2,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2018 },
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2019 .resources_init = geni_serial_pwr_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2020 .set_rate = geni_serial_set_level,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2021 };
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2022
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2] dt-bindings: qcom: geni-se-qup: Add compatible for SA8797P SoC
From: Krzysztof Kozlowski @ 2026-04-30 8:05 UTC (permalink / raw)
To: Shawn Guo
Cc: Bjorn Andersson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Praveen Talari, Konrad Dybcio, Dmitry Baryshkov,
Bartosz Golaszewski, Deepti Jaggi, linux-serial, devicetree,
linux-arm-msm, linux-kernel
In-Reply-To: <20260427005901.230237-1-shengchao.guo@oss.qualcomm.com>
On Mon, Apr 27, 2026 at 08:59:01AM +0800, Shawn Guo wrote:
> From: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
>
> Document GENI Serial Engine QUP Wrapper Controller on Nord SA8797P SoC
> which is compatible with SA8255P one.
>
> Signed-off-by: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
> Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> ---
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH net-next 2/3] ppp: unify two channel structs
From: Qingfang Deng @ 2026-04-30 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Jiri Kosina, David Sterba, Greg Kroah-Hartman,
Jiri Slaby, Mitchell Blank Jr, Chas Williams, Simon Horman,
James Chapman, Qingfang Deng, Kees Cook,
Sebastian Andrzej Siewior, Taegu Ha, Guillaume Nault,
Eric Woudstra, Arnd Bergmann, Dawid Osuchowski, Breno Leitao,
linux-ppp, netdev, linux-kernel, linux-serial, linux-atm-general
In-Reply-To: <20260430090532.244758-1-qingfang.deng@linux.dev>
Historically, PPP maintained two separate structures for a channel:
'struct channel' was internal to ppp_generic.c, while 'struct ppp_channel'
was the public interface that drivers were required to embed. This
duplication was redundant and forced drivers to manage the lifecycle of
the public structure.
Unify these two structures into a single 'struct ppp_channel', which is
now internal to ppp_generic.c. Drivers now use a 'ppp_channel_conf'
structure to specify registration parameters and receive an opaque
pointer to the allocated channel.
Key changes:
- ppp_register_channel() and ppp_register_net_channel() now return
a 'struct ppp_channel *' instead of taking a pointer to a driver-
embedded structure.
- 'struct ppp_channel_ops' methods now take the driver's 'private'
pointer directly as their first argument, simplifying driver logic.
- ppp_unregister_channel() now takes the opaque pointer.
- Multilink-specific fields are unified and handled via the new
configuration structure.
This cleanup simplifies the driver interface and makes the channel
lifecycle management more robust by centralizing allocation in the PPP
generic layer.
Assisted-by: Gemini:gemini-3-flash
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
drivers/net/ppp/ppp_async.c | 51 +++++-----
drivers/net/ppp/ppp_generic.c | 161 +++++++++++++++----------------
drivers/net/ppp/ppp_synctty.c | 51 +++++-----
drivers/net/ppp/pppoe.c | 34 ++++---
drivers/net/ppp/pppox.c | 4 +-
drivers/net/ppp/pptp.c | 40 ++++----
drivers/tty/ipwireless/network.c | 30 +++---
include/linux/if_pppox.h | 2 +-
include/linux/ppp_channel.h | 49 ++++++----
net/atm/pppoatm.c | 61 ++++++------
net/l2tp/l2tp_ppp.c | 34 ++++---
11 files changed, 271 insertions(+), 246 deletions(-)
diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c
index 93a7b0f6c4e7..faa299cc3db9 100644
--- a/drivers/net/ppp/ppp_async.c
+++ b/drivers/net/ppp/ppp_async.c
@@ -67,7 +67,7 @@ struct asyncppp {
refcount_t refcnt;
struct completion dead;
- struct ppp_channel chan; /* interface to generic ppp layer */
+ struct ppp_channel *chan; /* interface to generic ppp layer */
unsigned char obuf[OBUFSIZE];
};
@@ -95,12 +95,12 @@ MODULE_ALIAS_LDISC(N_PPP);
* Prototypes.
*/
static int ppp_async_encode(struct asyncppp *ap);
-static int ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb);
+static int ppp_async_send(void *private, struct sk_buff *skb);
static int ppp_async_push(struct asyncppp *ap);
static void ppp_async_flush_output(struct asyncppp *ap);
static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
const u8 *flags, int count);
-static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+static int ppp_async_ioctl(void *private, unsigned int cmd,
unsigned long arg);
static void ppp_async_process(struct tasklet_struct *t);
@@ -155,9 +155,10 @@ static void ap_put(struct asyncppp *ap)
static int
ppp_asynctty_open(struct tty_struct *tty)
{
+ struct ppp_channel_conf conf = {};
+ struct ppp_channel *chan;
struct asyncppp *ap;
int err;
- int speed;
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
@@ -185,14 +186,18 @@ ppp_asynctty_open(struct tty_struct *tty)
refcount_set(&ap->refcnt, 1);
init_completion(&ap->dead);
- ap->chan.private = ap;
- ap->chan.ops = &async_ops;
- ap->chan.mtu = PPP_MRU;
- speed = tty_get_baud_rate(tty);
- ap->chan.speed = speed;
- err = ppp_register_channel(&ap->chan);
- if (err)
+ conf.private = ap;
+ conf.ops = &async_ops;
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = PPP_MRU;
+ conf.speed = tty_get_baud_rate(tty);
+#endif
+ chan = ppp_register_channel(&conf);
+ if (!chan) {
+ err = -ENOMEM;
goto out_free;
+ }
+ ap->chan = chan;
tty->disc_data = ap;
tty->receive_room = 65536;
@@ -235,7 +240,7 @@ ppp_asynctty_close(struct tty_struct *tty)
wait_for_completion(&ap->dead);
tasklet_kill(&ap->tsk);
- ppp_unregister_channel(&ap->chan);
+ ppp_unregister_channel(ap->chan);
kfree_skb(ap->rpkt);
skb_queue_purge(&ap->rqueue);
kfree_skb(ap->tpkt);
@@ -293,14 +298,14 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
switch (cmd) {
case PPPIOCGCHAN:
err = -EFAULT;
- if (put_user(ppp_channel_index(&ap->chan), p))
+ if (put_user(ppp_channel_index(ap->chan), p))
break;
err = 0;
break;
case PPPIOCGUNIT:
err = -EFAULT;
- if (put_user(ppp_unit_number(&ap->chan), p))
+ if (put_user(ppp_unit_number(ap->chan), p))
break;
err = 0;
break;
@@ -391,9 +396,9 @@ ppp_async_init(void)
* The following routines provide the PPP channel interface.
*/
static int
-ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+ppp_async_ioctl(void *private, unsigned int cmd, unsigned long arg)
{
- struct asyncppp *ap = chan->private;
+ struct asyncppp *ap = private;
void __user *argp = (void __user *)arg;
int __user *p = argp;
int err, val;
@@ -491,13 +496,13 @@ static void ppp_async_process(struct tasklet_struct *t)
/* process received packets */
while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
if (skb->cb[0])
- ppp_input_error(&ap->chan);
- ppp_input(&ap->chan, skb);
+ ppp_input_error(ap->chan);
+ ppp_input(ap->chan, skb);
}
/* try to push more stuff out */
if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_async_push(ap))
- ppp_output_wakeup(&ap->chan);
+ ppp_output_wakeup(ap->chan);
}
/*
@@ -620,9 +625,9 @@ ppp_async_encode(struct asyncppp *ap)
* at some later time.
*/
static int
-ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
+ppp_async_send(void *private, struct sk_buff *skb)
{
- struct asyncppp *ap = chan->private;
+ struct asyncppp *ap = private;
ppp_async_push(ap);
@@ -733,7 +738,7 @@ ppp_async_flush_output(struct asyncppp *ap)
}
spin_unlock_bh(&ap->xmit_lock);
if (done)
- ppp_output_wakeup(&ap->chan);
+ ppp_output_wakeup(ap->chan);
}
/*
@@ -992,7 +997,7 @@ static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
if (inbound)
ap->mru = val;
else
- ap->chan.mtu = val;
+ ppp_channel_update_mtu(ap->chan, val);
break;
case LCP_ASYNCMAP:
val = get_unaligned_be32(data + 2);
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index aef42a69b63c..de59a2c44b77 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -106,7 +106,7 @@ struct ppp_file {
#define PF_TO_X(pf, X) container_of(pf, X, file)
#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
-#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
+#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct ppp_channel)
struct ppp_xmit_recursion {
struct task_struct *owner;
@@ -172,10 +172,11 @@ struct ppp {
* Private data structure for each channel.
* This includes the data structure used for multilink.
*/
-struct channel {
+struct ppp_channel {
struct ppp_file file; /* stuff for read/write/poll */
struct list_head list; /* link in all/new_channels list */
- struct ppp_channel *chan; /* public channel data structure */
+ const struct ppp_channel_ops *ops; /* operations for this channel */
+ void *private; /* channel private data */
struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */
spinlock_t downl; /* protects `chan', file.xq dequeue */
struct ppp __rcu *ppp; /* ppp unit we're connected to */
@@ -183,11 +184,13 @@ struct channel {
netns_tracker ns_tracker;
struct list_head clist; /* link in list of channels per unit */
spinlock_t upl; /* protects `ppp' and 'bridge' */
- struct channel __rcu *bridge; /* "bridged" ppp channel */
+ struct ppp_channel __rcu *bridge; /* "bridged" ppp channel */
+ bool direct_xmit; /* no qdisc, xmit directly */
#ifdef CONFIG_PPP_MULTILINK
u8 avail; /* flag used in multilink stuff */
u8 had_frag; /* >= 1 fragments have been sent */
u32 lastseq; /* MP: last sequence # received */
+ int mtu; /* max transmit packet size */
int speed; /* speed of the corresponding ppp channel*/
#endif /* CONFIG_PPP_MULTILINK */
};
@@ -265,16 +268,16 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb);
static int ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb);
static int ppp_push(struct ppp *ppp, struct sk_buff *skb);
-static void ppp_channel_push(struct channel *pch);
+static void ppp_channel_push(struct ppp_channel *pch);
static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
- struct channel *pch);
+ struct ppp_channel *pch);
static void ppp_receive_error(struct ppp *ppp);
static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
struct sk_buff *skb);
#ifdef CONFIG_PPP_MULTILINK
static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
- struct channel *pch);
+ struct ppp_channel *pch);
static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
@@ -288,10 +291,10 @@ static int ppp_create_interface(struct net *net, struct file *file, int *unit);
static void init_ppp_file(struct ppp_file *pf, int kind);
static void ppp_release_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
-static struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
-static int ppp_connect_channel(struct channel *pch, int unit);
-static int ppp_disconnect_channel(struct channel *pch);
-static void ppp_release_channel(struct channel *pch);
+static struct ppp_channel *ppp_find_channel(struct ppp_net *pn, int unit);
+static int ppp_connect_channel(struct ppp_channel *pch, int unit);
+static int ppp_disconnect_channel(struct ppp_channel *pch);
+static void ppp_release_channel(struct ppp_channel *pch);
static int unit_get(struct idr *p, void *ptr, int min);
static int unit_set(struct idr *p, void *ptr, int n);
static void unit_put(struct idr *p, int n);
@@ -638,7 +641,7 @@ static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p)
* Once successfully bridged, each channel holds a reference on the other
* to prevent it being freed while the bridge is extant.
*/
-static int ppp_bridge_channels(struct channel *pch, struct channel *pchb)
+static int ppp_bridge_channels(struct ppp_channel *pch, struct ppp_channel *pchb)
{
spin_lock(&pch->upl);
if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) ||
@@ -676,9 +679,9 @@ static int ppp_bridge_channels(struct channel *pch, struct channel *pchb)
return -EALREADY;
}
-static int ppp_unbridge_channels(struct channel *pch)
+static int ppp_unbridge_channels(struct ppp_channel *pch)
{
- struct channel *pchb, *pchbb;
+ struct ppp_channel *pchb, *pchbb;
spin_lock(&pch->upl);
pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl));
@@ -745,8 +748,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
if (pf->kind == CHANNEL) {
- struct channel *pch, *pchb;
- struct ppp_channel *chan;
+ struct ppp_channel *pch, *pchb;
struct ppp_net *pn;
pch = PF_TO_CHANNEL(pf);
@@ -788,10 +790,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
default:
down_read(&pch->chan_sem);
- chan = pch->chan;
err = -ENOTTY;
- if (!pch->file.dead && chan->ops->ioctl)
- err = chan->ops->ioctl(chan, cmd, arg);
+ if (!pch->file.dead && pch->ops->ioctl)
+ err = pch->ops->ioctl(pch->private, cmd, arg);
up_read(&pch->chan_sem);
}
goto out;
@@ -1044,7 +1045,7 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
{
int unit, err = -EFAULT;
struct ppp *ppp;
- struct channel *chan;
+ struct ppp_channel *chan;
struct ppp_net *pn;
int __user *p = (int __user *)arg;
@@ -1586,21 +1587,19 @@ static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
struct ppp *ppp = netdev_priv(ctx->dev);
- struct ppp_channel *chan;
- struct channel *pch;
+ struct ppp_channel *pch;
if (ppp->flags & SC_MULTILINK)
return -EOPNOTSUPP;
- pch = list_first_or_null_rcu(&ppp->channels, struct channel, clist);
+ pch = list_first_or_null_rcu(&ppp->channels, struct ppp_channel, clist);
if (!pch)
return -ENODEV;
- chan = pch->chan;
- if (!chan->ops->fill_forward_path)
+ if (!pch->ops->fill_forward_path)
return -EOPNOTSUPP;
- return chan->ops->fill_forward_path(ctx, path, chan);
+ return pch->ops->fill_forward_path(ctx, path, pch->private);
}
static const struct net_device_ops ppp_netdev_ops = {
@@ -1901,7 +1900,6 @@ static int
ppp_push(struct ppp *ppp, struct sk_buff *skb)
{
struct list_head *list;
- struct channel *pch;
list = &ppp->channels;
if (list_empty(list)) {
@@ -1911,15 +1909,14 @@ ppp_push(struct ppp *ppp, struct sk_buff *skb)
}
if ((ppp->flags & SC_MULTILINK) == 0) {
- struct ppp_channel *chan;
+ struct ppp_channel *pch;
int ret;
/* not doing multilink: send it down the first channel */
list = list->next;
- pch = list_entry(list, struct channel, clist);
+ pch = list_entry(list, struct ppp_channel, clist);
spin_lock(&pch->downl);
- chan = pch->chan;
- if (unlikely(!chan->direct_xmit && skb_linearize(skb))) {
+ if (unlikely(!pch->direct_xmit && skb_linearize(skb))) {
/* channel requires a linear skb but linearization
* failed
*/
@@ -1928,7 +1925,7 @@ ppp_push(struct ppp *ppp, struct sk_buff *skb)
goto out;
}
- ret = chan->ops->start_xmit(chan, skb);
+ ret = pch->ops->start_xmit(pch->private, skb);
out:
spin_unlock(&pch->downl);
@@ -1967,9 +1964,8 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
int totfree;
unsigned char *p, *q;
struct list_head *list;
- struct channel *pch;
+ struct ppp_channel *pch;
struct sk_buff *frag;
- struct ppp_channel *chan;
totspeed = 0; /*total bitrate of the bundle*/
nfree = 0; /* # channels which have no packet already queued */
@@ -1984,8 +1980,6 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
list_for_each_entry(pch, &ppp->channels, clist) {
pch->avail = 1;
navail++;
- pch->speed = pch->chan->speed;
-
if (skb_queue_empty(&pch->file.xq) || !pch->had_frag) {
if (pch->speed == 0)
nzero++;
@@ -2041,7 +2035,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
i = 0;
continue;
}
- pch = list_entry(list, struct channel, clist);
+ pch = list_entry(list, struct ppp_channel, clist);
++i;
if (!pch->avail)
continue;
@@ -2108,7 +2102,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
* MTU counts only the payload excluding the protocol field.
* (RFC1661 Section 2)
*/
- mtu = pch->chan->mtu - (hdrlen - 2);
+ mtu = pch->mtu - (hdrlen - 2);
if (mtu < 4)
mtu = 4;
if (flen > mtu)
@@ -2135,9 +2129,8 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
memcpy(q + hdrlen, p, flen);
/* try to send it down the channel */
- chan = pch->chan;
if (!skb_queue_empty(&pch->file.xq) ||
- !chan->ops->start_xmit(chan, frag))
+ !pch->ops->start_xmit(pch->private, frag))
skb_queue_tail(&pch->file.xq, frag);
pch->had_frag = 1;
p += flen;
@@ -2162,7 +2155,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
#endif /* CONFIG_PPP_MULTILINK */
/* Try to send data out on a channel */
-static void __ppp_channel_push(struct channel *pch, struct ppp *ppp)
+static void __ppp_channel_push(struct ppp_channel *pch, struct ppp *ppp)
{
struct sk_buff *skb;
@@ -2170,7 +2163,7 @@ static void __ppp_channel_push(struct channel *pch, struct ppp *ppp)
if (!pch->file.dead) {
while (!skb_queue_empty(&pch->file.xq)) {
skb = skb_dequeue(&pch->file.xq);
- if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+ if (!pch->ops->start_xmit(pch->private, skb)) {
/* put the packet back and try again later */
skb_queue_head(&pch->file.xq, skb);
break;
@@ -2192,7 +2185,7 @@ static void __ppp_channel_push(struct channel *pch, struct ppp *ppp)
}
}
-static void ppp_channel_push(struct channel *pch)
+static void ppp_channel_push(struct ppp_channel *pch)
{
struct ppp_xmit_recursion *xmit_recursion;
struct ppp *ppp;
@@ -2223,7 +2216,7 @@ struct ppp_mp_skb_parm {
#define PPP_MP_CB(skb) ((struct ppp_mp_skb_parm *)((skb)->cb))
static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct ppp_channel *pch)
{
ppp_recv_lock(ppp);
if (!ppp->closing)
@@ -2278,9 +2271,9 @@ static bool ppp_decompress_proto(struct sk_buff *skb)
* If not, the caller must handle the frame by normal recv mechanisms.
* Returns true if the frame is consumed, false otherwise.
*/
-static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
+static bool ppp_channel_bridge_input(struct ppp_channel *pch, struct sk_buff *skb)
{
- struct channel *pchb;
+ struct ppp_channel *pchb;
rcu_read_lock();
pchb = rcu_dereference(pch->bridge);
@@ -2295,7 +2288,7 @@ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
}
skb_scrub_packet(skb, !net_eq(pch->chan_net, pchb->chan_net));
- if (!pchb->chan->ops->start_xmit(pchb->chan, skb))
+ if (!pchb->ops->start_xmit(pchb->private, skb))
kfree_skb(skb);
outl:
@@ -2308,9 +2301,8 @@ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
}
void
-ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
+ppp_input(struct ppp_channel *pch, struct sk_buff *skb)
{
- struct channel *pch = chan->ppp;
struct ppp *ppp;
int proto;
@@ -2352,9 +2344,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
}
void
-ppp_input_error(struct ppp_channel *chan)
+ppp_input_error(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
struct ppp *ppp;
if (!pch)
@@ -2375,7 +2366,7 @@ ppp_input_error(struct ppp_channel *chan)
* The receive side of the ppp unit is locked.
*/
static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct ppp_channel *pch)
{
skb_checksum_complete_unset(skb);
#ifdef CONFIG_PPP_MULTILINK
@@ -2611,10 +2602,10 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
* as many completed frames as we can.
*/
static void
-ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct ppp_channel *pch)
{
u32 mask, seq;
- struct channel *ch;
+ struct ppp_channel *ch;
int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
@@ -2885,6 +2876,13 @@ ppp_mp_reconstruct(struct ppp *ppp)
return skb;
}
+
+/* Update the MTU of a multilink channel */
+void ppp_channel_update_mtu(struct ppp_channel *pch, int mtu)
+{
+ pch->mtu = mtu;
+}
+EXPORT_SYMBOL(ppp_channel_update_mtu);
#endif /* CONFIG_PPP_MULTILINK */
/*
@@ -2892,29 +2890,33 @@ ppp_mp_reconstruct(struct ppp *ppp)
*/
/* Create a new, unattached ppp channel. */
-int ppp_register_channel(struct ppp_channel *chan)
+struct ppp_channel *ppp_register_channel(const struct ppp_channel_conf *conf)
{
- return ppp_register_net_channel(current->nsproxy->net_ns, chan);
+ return ppp_register_net_channel(current->nsproxy->net_ns, conf);
}
/* Create a new, unattached ppp channel for specified net. */
-int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
+struct ppp_channel *ppp_register_net_channel(struct net *net,
+ const struct ppp_channel_conf *conf)
{
- struct channel *pch;
+ struct ppp_channel *pch;
struct ppp_net *pn;
- pch = kzalloc_obj(struct channel);
+ pch = kzalloc_obj(struct ppp_channel);
if (!pch)
- return -ENOMEM;
+ return NULL;
pn = ppp_pernet(net);
- pch->chan = chan;
pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL);
- chan->ppp = pch;
init_ppp_file(&pch->file, CHANNEL);
- pch->file.hdrlen = chan->hdrlen;
+ pch->file.hdrlen = conf->hdrlen;
+ pch->ops = conf->ops;
+ pch->private = conf->private;
+ pch->direct_xmit = conf->direct_xmit;
#ifdef CONFIG_PPP_MULTILINK
+ pch->speed = conf->speed;
+ pch->mtu = conf->mtu;
pch->lastseq = -1;
#endif /* CONFIG_PPP_MULTILINK */
init_rwsem(&pch->chan_sem);
@@ -2927,16 +2929,14 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
atomic_inc(&channel_count);
spin_unlock_bh(&pn->all_channels_lock);
- return 0;
+ return pch;
}
/*
* Return the index of a channel.
*/
-int ppp_channel_index(struct ppp_channel *chan)
+int ppp_channel_index(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
-
if (pch)
return pch->file.index;
return -1;
@@ -2945,9 +2945,8 @@ int ppp_channel_index(struct ppp_channel *chan)
/*
* Return the PPP unit number to which a channel is connected.
*/
-int ppp_unit_number(struct ppp_channel *chan)
+int ppp_unit_number(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
struct ppp *ppp;
int unit = -1;
@@ -2965,9 +2964,8 @@ int ppp_unit_number(struct ppp_channel *chan)
* Return the PPP device interface name of a channel.
* Caller must hold RCU read lock.
*/
-char *ppp_dev_name(struct ppp_channel *chan)
+char *ppp_dev_name(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
char *name = NULL;
struct ppp *ppp;
@@ -2985,16 +2983,13 @@ char *ppp_dev_name(struct ppp_channel *chan)
* This must be called in process context.
*/
void
-ppp_unregister_channel(struct ppp_channel *chan)
+ppp_unregister_channel(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
struct ppp_net *pn;
if (!pch)
return; /* should never happen */
- chan->ppp = NULL;
-
/*
* This ensures that we have returned from any calls into
* the channel's start_xmit or ioctl routine before we proceed.
@@ -3023,10 +3018,8 @@ ppp_unregister_channel(struct ppp_channel *chan)
* This should be called at BH/softirq level, not interrupt level.
*/
void
-ppp_output_wakeup(struct ppp_channel *chan)
+ppp_output_wakeup(struct ppp_channel *pch)
{
- struct channel *pch = chan->ppp;
-
if (!pch)
return;
ppp_channel_push(pch);
@@ -3459,10 +3452,10 @@ ppp_find_unit(struct ppp_net *pn, int unit)
* we move it to the all_channels list. This is for speed
* when we have a lot of channels in use.
*/
-static struct channel *
+static struct ppp_channel *
ppp_find_channel(struct ppp_net *pn, int unit)
{
- struct channel *pch;
+ struct ppp_channel *pch;
list_for_each_entry(pch, &pn->new_channels, list) {
if (pch->file.index == unit) {
@@ -3483,7 +3476,7 @@ ppp_find_channel(struct ppp_net *pn, int unit)
* Connect a PPP channel to a PPP interface unit.
*/
static int
-ppp_connect_channel(struct channel *pch, int unit)
+ppp_connect_channel(struct ppp_channel *pch, int unit)
{
struct ppp *ppp;
struct ppp_net *pn;
@@ -3511,7 +3504,7 @@ ppp_connect_channel(struct channel *pch, int unit)
ret = -ENOTCONN;
goto outl;
}
- if (pch->chan->direct_xmit)
+ if (pch->direct_xmit)
ppp->dev->priv_flags |= IFF_NO_QUEUE;
else
ppp->dev->priv_flags &= ~IFF_NO_QUEUE;
@@ -3539,7 +3532,7 @@ ppp_connect_channel(struct channel *pch, int unit)
* Disconnect a channel from its ppp unit.
*/
static int
-ppp_disconnect_channel(struct channel *pch)
+ppp_disconnect_channel(struct ppp_channel *pch)
{
struct ppp *ppp;
int err = -EINVAL;
@@ -3565,7 +3558,7 @@ ppp_disconnect_channel(struct channel *pch)
* Drop a reference to a ppp channel and free its memory if the refcount reaches
* zero.
*/
-static void ppp_release_channel(struct channel *pch)
+static void ppp_release_channel(struct ppp_channel *pch)
{
if (!refcount_dec_and_test(&pch->file.refcnt))
return;
diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c
index b7f243b416f8..d84c267f4da1 100644
--- a/drivers/net/ppp/ppp_synctty.c
+++ b/drivers/net/ppp/ppp_synctty.c
@@ -71,7 +71,7 @@ struct syncppp {
refcount_t refcnt;
struct completion dead_cmp;
- struct ppp_channel chan; /* interface to generic ppp layer */
+ struct ppp_channel *chan; /* interface to generic ppp layer */
};
/* Bit numbers in xmit_flags */
@@ -87,8 +87,8 @@ struct syncppp {
* Prototypes.
*/
static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
-static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
-static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
+static int ppp_sync_send(void *private, struct sk_buff *skb);
+static int ppp_sync_ioctl(void *private, unsigned int cmd,
unsigned long arg);
static void ppp_sync_process(struct tasklet_struct *t);
static int ppp_sync_push(struct syncppp *ap);
@@ -155,9 +155,10 @@ static void sp_put(struct syncppp *ap)
static int
ppp_sync_open(struct tty_struct *tty)
{
+ struct ppp_channel_conf conf = {};
+ struct ppp_channel *chan;
struct syncppp *ap;
int err;
- int speed;
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
@@ -182,15 +183,19 @@ ppp_sync_open(struct tty_struct *tty)
refcount_set(&ap->refcnt, 1);
init_completion(&ap->dead_cmp);
- ap->chan.private = ap;
- ap->chan.ops = &sync_ops;
- ap->chan.mtu = PPP_MRU;
- ap->chan.hdrlen = 2; /* for A/C bytes */
- speed = tty_get_baud_rate(tty);
- ap->chan.speed = speed;
- err = ppp_register_channel(&ap->chan);
- if (err)
+ conf.private = ap;
+ conf.ops = &sync_ops;
+ conf.hdrlen = 2; /* for A/C bytes */
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = PPP_MRU;
+ conf.speed = tty_get_baud_rate(tty);
+#endif
+ chan = ppp_register_channel(&conf);
+ if (!chan) {
+ err = -ENOMEM;
goto out_free;
+ }
+ ap->chan = chan;
tty->disc_data = ap;
tty->receive_room = 65536;
@@ -233,7 +238,7 @@ ppp_sync_close(struct tty_struct *tty)
wait_for_completion(&ap->dead_cmp);
tasklet_kill(&ap->tsk);
- ppp_unregister_channel(&ap->chan);
+ ppp_unregister_channel(ap->chan);
skb_queue_purge(&ap->rqueue);
kfree_skb(ap->tpkt);
kfree(ap);
@@ -285,14 +290,14 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
switch (cmd) {
case PPPIOCGCHAN:
err = -EFAULT;
- if (put_user(ppp_channel_index(&ap->chan), p))
+ if (put_user(ppp_channel_index(ap->chan), p))
break;
err = 0;
break;
case PPPIOCGUNIT:
err = -EFAULT;
- if (put_user(ppp_unit_number(&ap->chan), p))
+ if (put_user(ppp_unit_number(ap->chan), p))
break;
err = 0;
break;
@@ -383,9 +388,9 @@ ppp_sync_init(void)
* The following routines provide the PPP channel interface.
*/
static int
-ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+ppp_sync_ioctl(void *private, unsigned int cmd, unsigned long arg)
{
- struct syncppp *ap = chan->private;
+ struct syncppp *ap = private;
int err, val;
u32 accm[8];
void __user *argp = (void __user *)arg;
@@ -483,16 +488,16 @@ static void ppp_sync_process(struct tasklet_struct *t)
while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
if (skb->len == 0) {
/* zero length buffers indicate error */
- ppp_input_error(&ap->chan);
+ ppp_input_error(ap->chan);
kfree_skb(skb);
}
else
- ppp_input(&ap->chan, skb);
+ ppp_input(ap->chan, skb);
}
/* try to push more stuff out */
if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
- ppp_output_wakeup(&ap->chan);
+ ppp_output_wakeup(ap->chan);
}
/*
@@ -562,9 +567,9 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
* at some later time.
*/
static int
-ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb)
+ppp_sync_send(void *private, struct sk_buff *skb)
{
- struct syncppp *ap = chan->private;
+ struct syncppp *ap = private;
ppp_sync_push(ap);
@@ -649,7 +654,7 @@ ppp_sync_flush_output(struct syncppp *ap)
}
spin_unlock_bh(&ap->xmit_lock);
if (done)
- ppp_output_wakeup(&ap->chan);
+ ppp_output_wakeup(ap->chan);
}
/*
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index bdd61c504a1c..3c08cf6de705 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -357,7 +357,7 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
*/
if (sk->sk_state & PPPOX_BOUND) {
- ppp_input(&po->chan, skb);
+ ppp_input(po->chan, skb);
} else {
if (sock_queue_rcv_skb(sk, skb))
goto abort_kfree;
@@ -631,7 +631,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr
po->pppoe_ifindex = 0;
memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa));
- memset(&po->chan, 0, sizeof(po->chan));
+ po->chan = NULL;
po->next = NULL;
po->num = 0;
@@ -640,6 +640,9 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr
/* Re-bind in session stage only */
if (stage_session(sp->sa_addr.pppoe.sid)) {
+ struct ppp_channel_conf conf = {};
+ struct ppp_channel *chan;
+
error = -ENODEV;
net = sock_net(sk);
dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
@@ -663,20 +666,23 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr
if (error < 0)
goto err_put;
- po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
+ conf.hdrlen = (sizeof(struct pppoe_hdr) +
dev->hard_header_len);
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2;
+#endif
+ conf.private = sk;
+ conf.ops = &pppoe_chan_ops;
+ conf.direct_xmit = true;
- po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2;
- po->chan.private = sk;
- po->chan.ops = &pppoe_chan_ops;
- po->chan.direct_xmit = true;
-
- error = ppp_register_net_channel(dev_net(dev), &po->chan);
- if (error) {
+ chan = ppp_register_net_channel(dev_net(dev), &conf);
+ if (!chan) {
+ error = -ENOMEM;
delete_item(pn, po->pppoe_pa.sid,
po->pppoe_pa.remote, po->pppoe_ifindex);
goto err_put;
}
+ po->chan = chan;
sk->sk_state = PPPOX_CONNECTED;
}
@@ -897,17 +903,17 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
* sends PPP frame over PPPoE socket
*
***********************************************************************/
-static int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+static int pppoe_xmit(void *private, struct sk_buff *skb)
{
- struct sock *sk = chan->private;
+ struct sock *sk = private;
return __pppoe_xmit(sk, skb);
}
static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path *path,
- const struct ppp_channel *chan)
+ void *private)
{
- struct sock *sk = chan->private;
+ struct sock *sk = private;
struct pppox_sock *po = pppox_sk(sk);
struct net_device *dev = po->pppoe_dev;
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
index 5861a2f6ce3e..df4fb23a926d 100644
--- a/drivers/net/ppp/pppox.c
+++ b/drivers/net/ppp/pppox.c
@@ -55,7 +55,7 @@ void pppox_unbind_sock(struct sock *sk)
/* Clear connection to ppp device, if attached. */
if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
- ppp_unregister_channel(&pppox_sk(sk)->chan);
+ ppp_unregister_channel(pppox_sk(sk)->chan);
sk->sk_state = PPPOX_DEAD;
}
}
@@ -80,7 +80,7 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
rc = -EINVAL;
- index = ppp_channel_index(&po->chan);
+ index = ppp_channel_index(po->chan);
if (put_user(index , (int __user *) arg))
break;
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index cc8c102122d8..e49abbe63bf1 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -146,9 +146,9 @@ static struct rtable *pptp_route_output(const struct pppox_sock *po,
return ip_route_output_flow(net, fl4, sk);
}
-static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+static int pptp_xmit(void *private, struct sk_buff *skb)
{
- struct sock *sk = chan->private;
+ struct sock *sk = private;
struct pppox_sock *po = pppox_sk(sk);
struct net *net = sock_net(sk);
struct pptp_opt *opt = &po->proto.pptp;
@@ -338,7 +338,7 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
skb->ip_summed = CHECKSUM_NONE;
skb_set_network_header(skb, skb->head-skb->data);
- ppp_input(&po->chan, skb);
+ ppp_input(po->chan, skb);
return NET_RX_SUCCESS;
}
@@ -422,6 +422,8 @@ static int pptp_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
struct pppox_sock *po = pppox_sk(sk);
struct pptp_opt *opt = &po->proto.pptp;
+ struct ppp_channel_conf conf = {};
+ struct ppp_channel *chan;
struct rtable *rt;
struct flowi4 fl4;
int error = 0;
@@ -453,9 +455,6 @@ static int pptp_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,
goto end;
}
- po->chan.private = sk;
- po->chan.ops = &pptp_chan_ops;
-
rt = pptp_route_output(po, &fl4);
if (IS_ERR(rt)) {
error = -EHOSTUNREACH;
@@ -463,18 +462,23 @@ static int pptp_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,
}
sk_setup_caps(sk, &rt->dst);
- po->chan.mtu = dst_mtu(&rt->dst);
- if (!po->chan.mtu)
- po->chan.mtu = PPP_MRU;
- po->chan.mtu -= PPTP_HEADER_OVERHEAD;
-
- po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
- po->chan.direct_xmit = true;
- error = ppp_register_channel(&po->chan);
- if (error) {
+ conf.private = sk;
+ conf.ops = &pptp_chan_ops;
+ conf.hdrlen = 2 + sizeof(struct pptp_gre_header);
+ conf.direct_xmit = true;
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = dst_mtu(&rt->dst);
+ if (!conf.mtu)
+ conf.mtu = PPP_MRU;
+ conf.mtu -= PPTP_HEADER_OVERHEAD;
+#endif
+ chan = ppp_register_channel(&conf);
+ if (!chan) {
+ error = -ENOMEM;
pr_err("PPTP: failed to register PPP channel (%d)\n", error);
goto end;
}
+ po->chan = chan;
opt->dst_addr = sp->sa_addr.pptp;
sk->sk_state |= PPPOX_CONNECTED;
@@ -577,10 +581,10 @@ static int pptp_create(struct net *net, struct socket *sock, int kern)
return error;
}
-static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
- unsigned long arg)
+static int pptp_ppp_ioctl(void *private, unsigned int cmd,
+ unsigned long arg)
{
- struct sock *sk = chan->private;
+ struct sock *sk = private;
struct pppox_sock *po = pppox_sk(sk);
struct pptp_opt *opt = &po->proto.pptp;
void __user *argp = (void __user *)arg;
diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c
index ad2c5157a018..7ac5a2d02d44 100644
--- a/drivers/tty/ipwireless/network.c
+++ b/drivers/tty/ipwireless/network.c
@@ -88,10 +88,10 @@ static void notify_packet_sent(void *callback_data, unsigned int packet_length)
/*
* Called by the ppp system when it has a packet to send to the hardware.
*/
-static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel,
+static int ipwireless_ppp_start_xmit(void *private,
struct sk_buff *skb)
{
- struct ipw_network *network = ppp_channel->private;
+ struct ipw_network *network = private;
unsigned long flags;
spin_lock_irqsave(&network->lock, flags);
@@ -153,10 +153,10 @@ static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel,
}
/* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */
-static int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel,
+static int ipwireless_ppp_ioctl(void *private,
unsigned int cmd, unsigned long arg)
{
- struct ipw_network *network = ppp_channel->private;
+ struct ipw_network *network = private;
int err, val;
u32 accm[8];
int __user *user_arg = (int __user *) arg;
@@ -254,19 +254,17 @@ static void do_go_online(struct work_struct *work_go_online)
spin_lock_irqsave(&network->lock, flags);
if (!network->ppp_channel) {
+ struct ppp_channel_conf conf = {};
struct ppp_channel *channel;
spin_unlock_irqrestore(&network->lock, flags);
- channel = kzalloc_obj(struct ppp_channel);
- if (!channel) {
- printk(KERN_ERR IPWIRELESS_PCCARD_NAME
- ": unable to allocate PPP channel\n");
- return;
- }
- channel->private = network;
- channel->mtu = 16384; /* Wild guess */
- channel->hdrlen = 2;
- channel->ops = &ipwireless_ppp_channel_ops;
+
+ conf.private = network;
+ conf.hdrlen = 2;
+ conf.ops = &ipwireless_ppp_channel_ops;
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = 16384; /* Wild guess */
+#endif
network->flags = 0;
network->rbits = 0;
@@ -275,10 +273,10 @@ static void do_go_online(struct work_struct *work_go_online)
network->xaccm[0] = ~0U;
network->xaccm[3] = 0x60000000U;
network->raccm = ~0U;
- if (ppp_register_channel(channel) < 0) {
+ channel = ppp_register_channel(&conf);
+ if (!channel) {
printk(KERN_ERR IPWIRELESS_PCCARD_NAME
": unable to register PPP channel\n");
- kfree(channel);
return;
}
spin_lock_irqsave(&network->lock, flags);
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index 594d6dc3f4c9..a1d7c11182ec 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -40,7 +40,7 @@ struct pptp_opt {
struct pppox_sock {
/* struct sock must be the first member of pppox_sock */
struct sock sk;
- struct ppp_channel chan;
+ struct ppp_channel *chan;
struct pppox_sock __rcu *next; /* for hash table */
union {
struct pppoe_opt pppoe;
diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h
index 2f63e9a6cc88..05f850e6b696 100644
--- a/include/linux/ppp_channel.h
+++ b/include/linux/ppp_channel.h
@@ -27,55 +27,64 @@ struct ppp_channel;
struct ppp_channel_ops {
/* Send a packet (or multilink fragment) on this channel.
Returns 1 if it was accepted, 0 if not. */
- int (*start_xmit)(struct ppp_channel *, struct sk_buff *);
+ int (*start_xmit)(void *private, struct sk_buff *skb);
/* Handle an ioctl call that has come in via /dev/ppp. */
- int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
- int (*fill_forward_path)(struct net_device_path_ctx *,
- struct net_device_path *,
- const struct ppp_channel *);
+ int (*ioctl)(void *private, unsigned int cmd, unsigned long arg);
+ int (*fill_forward_path)(struct net_device_path_ctx *ctx,
+ struct net_device_path *path,
+ void *private);
};
-struct ppp_channel {
+struct ppp_channel_conf {
void *private; /* channel private data */
const struct ppp_channel_ops *ops; /* operations for this channel */
- int mtu; /* max transmit packet size */
int hdrlen; /* amount of headroom channel needs */
- void *ppp; /* opaque to channel */
- int speed; /* transfer rate (bytes/second) */
bool direct_xmit; /* no qdisc, xmit directly */
+#ifdef CONFIG_PPP_MULTILINK
+ int speed; /* transfer rate (bytes/second) */
+ int mtu; /* max transmit packet size */
+#endif
};
#ifdef __KERNEL__
/* Called by the channel when it can send some more data. */
-extern void ppp_output_wakeup(struct ppp_channel *);
+void ppp_output_wakeup(struct ppp_channel *pch);
/* Called by the channel to process a received PPP packet.
The packet should have just the 2-byte PPP protocol header. */
-extern void ppp_input(struct ppp_channel *, struct sk_buff *);
+void ppp_input(struct ppp_channel *pch, struct sk_buff *skb);
/* Called by the channel when an input error occurs, indicating
that we may have missed a packet. */
-extern void ppp_input_error(struct ppp_channel *);
+void ppp_input_error(struct ppp_channel *pch);
-/* Attach a channel to a given PPP unit in specified net. */
-extern int ppp_register_net_channel(struct net *, struct ppp_channel *);
+/* Create a new, unattached ppp channel for specified net. */
+struct ppp_channel *ppp_register_net_channel(struct net *net,
+ const struct ppp_channel_conf *chan);
-/* Attach a channel to a given PPP unit. */
-extern int ppp_register_channel(struct ppp_channel *);
+/* Create a new, unattached ppp channel. */
+struct ppp_channel *ppp_register_channel(const struct ppp_channel_conf *chan);
/* Detach a channel from its PPP unit (e.g. on hangup). */
-extern void ppp_unregister_channel(struct ppp_channel *);
+void ppp_unregister_channel(struct ppp_channel *pch);
/* Get the channel number for a channel */
-extern int ppp_channel_index(struct ppp_channel *);
+int ppp_channel_index(struct ppp_channel *pch);
/* Get the unit number associated with a channel, or -1 if none */
-extern int ppp_unit_number(struct ppp_channel *);
+int ppp_unit_number(struct ppp_channel *pch);
/* Get the device name associated with a channel, or NULL if none.
* Caller must hold RCU read lock.
*/
-extern char *ppp_dev_name(struct ppp_channel *);
+char *ppp_dev_name(struct ppp_channel *pch);
+
+/* Update the MTU of a multilink channel */
+#ifdef CONFIG_PPP_MULTILINK
+void ppp_channel_update_mtu(struct ppp_channel *pch, int mtu);
+#else
+static inline void ppp_channel_update_mtu(struct ppp_channel *pch, int mtu) {}
+#endif
/*
* SMP locking notes:
diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c
index e3c422dc533a..d801233700e7 100644
--- a/net/atm/pppoatm.c
+++ b/net/atm/pppoatm.c
@@ -64,7 +64,7 @@ struct pppoatm_vcc {
atomic_t inflight;
unsigned long blocked;
int flags; /* SC_COMP_PROT - compress protocol */
- struct ppp_channel chan; /* interface to generic ppp layer */
+ struct ppp_channel *chan; /* interface to generic ppp layer */
struct tasklet_struct wakeup_tasklet;
};
@@ -91,11 +91,6 @@ static inline struct pppoatm_vcc *atmvcc_to_pvcc(const struct atm_vcc *atmvcc)
return (struct pppoatm_vcc *) (atmvcc->user_back);
}
-static inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan)
-{
- return (struct pppoatm_vcc *) (chan->private);
-}
-
/*
* We can't do this directly from our _pop handler, since the ppp code
* doesn't want to be called in interrupt context, so we do it from
@@ -105,7 +100,7 @@ static void pppoatm_wakeup_sender(struct tasklet_struct *t)
{
struct pppoatm_vcc *pvcc = from_tasklet(pvcc, t, wakeup_tasklet);
- ppp_output_wakeup(&pvcc->chan);
+ ppp_output_wakeup(pvcc->chan);
}
static void pppoatm_release_cb(struct atm_vcc *atmvcc)
@@ -172,7 +167,7 @@ static void pppoatm_unassign_vcc(struct atm_vcc *atmvcc)
atmvcc->pop = pvcc->old_pop;
atmvcc->release_cb = pvcc->old_release_cb;
tasklet_kill(&pvcc->wakeup_tasklet);
- ppp_unregister_channel(&pvcc->chan);
+ ppp_unregister_channel(pvcc->chan);
atmvcc->user_back = NULL;
kfree(pvcc);
}
@@ -201,7 +196,7 @@ static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
skb_pull(skb, LLC_LEN);
break;
case e_autodetect:
- if (pvcc->chan.ppp == NULL) { /* Not bound yet! */
+ if (!pvcc->chan) { /* Not bound yet! */
kfree_skb(skb);
return;
}
@@ -215,7 +210,8 @@ static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
!memcmp(skb->data, &pppllc[LLC_LEN],
sizeof(pppllc) - LLC_LEN)) {
pvcc->encaps = e_vc;
- pvcc->chan.mtu += LLC_LEN;
+ ppp_channel_update_mtu(pvcc->chan,
+ atmvcc->qos.txtp.max_sdu - PPP_HDRLEN);
break;
}
pr_debug("Couldn't autodetect yet (skb: %6ph)\n", skb->data);
@@ -223,12 +219,12 @@ static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
case e_vc:
break;
}
- ppp_input(&pvcc->chan, skb);
+ ppp_input(pvcc->chan, skb);
return;
error:
kfree_skb(skb);
- ppp_input_error(&pvcc->chan);
+ ppp_input_error(pvcc->chan);
}
static int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
@@ -286,9 +282,9 @@ static int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
* as success, just to be clear what we're really doing.
*/
#define DROP_PACKET 1
-static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
+static int pppoatm_send(void *private, struct sk_buff *skb)
{
- struct pppoatm_vcc *pvcc = chan_to_pvcc(chan);
+ struct pppoatm_vcc *pvcc = private;
struct atm_vcc *vcc;
int ret;
@@ -367,16 +363,15 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
}
/* This handles ioctls sent to the /dev/ppp interface */
-static int pppoatm_devppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
- unsigned long arg)
+static int pppoatm_devppp_ioctl(void *private, unsigned int cmd,
+ unsigned long arg)
{
+ struct pppoatm_vcc *pvcc = private;
switch (cmd) {
case PPPIOCGFLAGS:
- return put_user(chan_to_pvcc(chan)->flags, (int __user *) arg)
- ? -EFAULT : 0;
+ return put_user(pvcc->flags, (int __user *)arg) ? -EFAULT : 0;
case PPPIOCSFLAGS:
- return get_user(chan_to_pvcc(chan)->flags, (int __user *) arg)
- ? -EFAULT : 0;
+ return get_user(pvcc->flags, (int __user *)arg) ? -EFAULT : 0;
}
return -ENOTTY;
}
@@ -388,9 +383,10 @@ static const struct ppp_channel_ops pppoatm_ops = {
static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
{
+ struct ppp_channel_conf conf = {};
struct atm_backend_ppp be;
struct pppoatm_vcc *pvcc;
- int err;
+ struct ppp_channel *chan;
if (copy_from_user(&be, arg, sizeof be))
return -EFAULT;
@@ -409,16 +405,19 @@ static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
pvcc->old_owner = atmvcc->owner;
pvcc->old_release_cb = atmvcc->release_cb;
pvcc->encaps = (enum pppoatm_encaps) be.encaps;
- pvcc->chan.private = pvcc;
- pvcc->chan.ops = &pppoatm_ops;
- pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN -
+ conf.private = pvcc;
+ conf.ops = &pppoatm_ops;
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN -
(be.encaps == e_vc ? 0 : LLC_LEN);
+#endif
tasklet_setup(&pvcc->wakeup_tasklet, pppoatm_wakeup_sender);
- err = ppp_register_channel(&pvcc->chan);
- if (err != 0) {
+ chan = ppp_register_channel(&conf);
+ if (!chan) {
kfree(pvcc);
- return err;
+ return -ENOMEM;
}
+ pvcc->chan = chan;
atmvcc->user_back = pvcc;
atmvcc->push = pppoatm_push;
atmvcc->pop = pppoatm_pop;
@@ -458,11 +457,11 @@ static int pppoatm_ioctl(struct socket *sock, unsigned int cmd,
return pppoatm_assign_vcc(atmvcc, argp);
}
case PPPIOCGCHAN:
- return put_user(ppp_channel_index(&atmvcc_to_pvcc(atmvcc)->
- chan), (int __user *) argp) ? -EFAULT : 0;
+ return put_user(ppp_channel_index(atmvcc_to_pvcc(atmvcc)->chan),
+ (int __user *)argp) ? -EFAULT : 0;
case PPPIOCGUNIT:
- return put_user(ppp_unit_number(&atmvcc_to_pvcc(atmvcc)->
- chan), (int __user *) argp) ? -EFAULT : 0;
+ return put_user(ppp_unit_number(atmvcc_to_pvcc(atmvcc)->chan),
+ (int __user *)argp) ? -EFAULT : 0;
}
return -ENOIOCTLCMD;
}
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 99d6582f41de..6c7b08f4e49a 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -121,7 +121,7 @@ struct pppol2tp_session {
struct sock *__sk; /* Copy of .sk, for cleanup */
};
-static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
+static int pppol2tp_xmit(void *private, struct sk_buff *skb);
static const struct ppp_channel_ops pppol2tp_chan_ops = {
.start_xmit = pppol2tp_xmit,
@@ -221,7 +221,7 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int
struct pppox_sock *po;
po = pppox_sk(sk);
- ppp_input(&po->chan, skb);
+ ppp_input(po->chan, skb);
} else {
if (sock_queue_rcv_skb(sk, skb) < 0) {
atomic_long_inc(&session->stats.rx_errors);
@@ -326,9 +326,9 @@ static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m,
* the skb it supplied, not our cloned skb. So we take care to always
* leave the original skb unfreed if we return an error.
*/
-static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+static int pppol2tp_xmit(void *private, struct sk_buff *skb)
{
- struct sock *sk = (struct sock *)chan->private;
+ struct sock *sk = private;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
int uhlen, headroom;
@@ -504,7 +504,7 @@ static void pppol2tp_show(struct seq_file *m, void *arg)
if (sk) {
struct pppox_sock *po = pppox_sk(sk);
- seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
+ seq_printf(m, " interface %s\n", ppp_dev_name(po->chan));
}
rcu_read_unlock();
}
@@ -612,7 +612,7 @@ static int pppol2tp_sockaddr_get_info(const void *sa, int sa_len,
* numbers and no IP option. Not quite accurate, but the result is mostly
* unused anyway.
*/
-static int pppol2tp_tunnel_mtu(const struct l2tp_tunnel *tunnel)
+static int __maybe_unused pppol2tp_tunnel_mtu(const struct l2tp_tunnel *tunnel)
{
int mtu;
@@ -694,6 +694,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr_unsized *userva
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
struct l2tp_session_cfg cfg = { 0, };
+ struct ppp_channel_conf conf = {};
+ struct ppp_channel *chan;
bool drop_refcnt = false;
bool new_session = false;
bool new_tunnel = false;
@@ -792,18 +794,22 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr_unsized *userva
* the net device's hard_header_len at registration, which must be
* sufficient regardless of whether sequence numbers are enabled later.
*/
- po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_SEQ;
+ conf.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_SEQ;
- po->chan.private = sk;
- po->chan.ops = &pppol2tp_chan_ops;
- po->chan.mtu = pppol2tp_tunnel_mtu(tunnel);
- po->chan.direct_xmit = true;
+ conf.private = sk;
+ conf.ops = &pppol2tp_chan_ops;
+#ifdef CONFIG_PPP_MULTILINK
+ conf.mtu = pppol2tp_tunnel_mtu(tunnel);
+#endif
+ conf.direct_xmit = true;
- error = ppp_register_net_channel(sock_net(sk), &po->chan);
- if (error) {
+ chan = ppp_register_net_channel(sock_net(sk), &conf);
+ if (!chan) {
+ error = -ENOMEM;
mutex_unlock(&ps->sk_lock);
goto end;
}
+ po->chan = chan;
out_no_ppp:
/* This is how we get the session context from the socket. */
@@ -1550,7 +1556,7 @@ static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
if (sk) {
struct pppox_sock *po = pppox_sk(sk);
- seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
+ seq_printf(m, " interface %s\n", ppp_dev_name(po->chan));
}
rcu_read_unlock();
}
--
2.43.0
^ permalink raw reply related
* [PATCH] serial: 8250_port: recognize UPIO_AU
From: Manuel Lauss @ 2026-04-30 13:58 UTC (permalink / raw)
To: linux-serial; +Cc: Manuel Lauss
My MIPS Alchemy systems generate the following warning during
probe of the 8250 driver:
WARNING: drivers/tty/serial/8250/8250_port.c:462 at set_io_from_upio+0xfc/0x124, CPU#0: swapper/0/1
Unsupported UART type 4
[...]
[<80521d40>] set_io_from_upio+0xfc/0x124
[<80521dfc>] serial8250_set_defaults+0x94/0xe0
[<80520fb4>] serial8250_register_8250_port+0x288/0x51c
[<805214ec>] serial8250_probe+0x160/0x1e8
[<8053b5f0>] platform_probe+0x64/0x90
The least invasive fix is to recognize UPIO_AU (type 4) in set_io_from_upio()
and do nothing, since all parameters have already been set up in
8250_rt288x.c::au_platform_setup().
Run-tested on Alchemy Au1300 platform.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
drivers/tty/serial/8250/8250_port.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index af78cc02f38e..02e6cdefdbac 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -458,6 +458,8 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = io_serial_out;
break;
#endif
+ case UPIO_AU:
+ break;
default:
WARN(p->iotype != UPIO_PORT || p->iobase,
"Unsupported UART type %x\n", p->iotype);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v1] serial: qcom-geni: Avoid probing debug console UART without console support
From: kernel test robot @ 2026-04-30 14:40 UTC (permalink / raw)
To: Aniket Randive, gregkh, jirislaby, linux-arm-msm, linux-kernel,
linux-serial, praveen.talari, anup.kulkarni, dmitry.baryshkov,
viken.dadhaniya
Cc: oe-kbuild-all, Aniket Randive
In-Reply-To: <20260413072501.263871-1-aniket.randive@oss.qualcomm.com>
Hi Aniket,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus usb/usb-testing usb/usb-next usb/usb-linus linus/master v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Aniket-Randive/serial-qcom-geni-Avoid-probing-debug-console-UART-without-console-support/20260423-150710
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20260413072501.263871-1-aniket.randive%40oss.qualcomm.com
patch subject: [PATCH v1] serial: qcom-geni: Avoid probing debug console UART without console support
config: csky-randconfig-001-20260430 (https://download.01.org/0day-ci/archive/20260430/202604302204.1qhU6zO5-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604302204.1qhU6zO5-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604302204.1qhU6zO5-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/tty/serial/qcom_geni_serial.c:2011:43: warning: 'sa8255p_qcom_geni_console_data' defined but not used [-Wunused-const-variable=]
2011 | static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/tty/serial/qcom_geni_serial.c:1995:43: warning: 'qcom_geni_console_data' defined but not used [-Wunused-const-variable=]
1995 | static const struct qcom_geni_device_data qcom_geni_console_data = {
| ^~~~~~~~~~~~~~~~~~~~~~
vim +/sa8255p_qcom_geni_console_data +2011 drivers/tty/serial/qcom_geni_serial.c
c4f528795d1add Karthikeyan Ramasubramanian 2018-03-14 1994
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 @1995 static const struct qcom_geni_device_data qcom_geni_console_data = {
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 1996 .console = true,
2aaa43c7077833 Bartosz Golaszewski 2022-12-29 1997 .mode = GENI_SE_FIFO,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 1998 .resources_init = geni_serial_resource_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 1999 .set_rate = geni_serial_set_rate,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2000 .power_state = geni_serial_resource_state,
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2001 };
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2002
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2003 static const struct qcom_geni_device_data qcom_geni_uart_data = {
40ec6d41c841e2 Bartosz Golaszewski 2022-12-29 2004 .console = false,
2aaa43c7077833 Bartosz Golaszewski 2022-12-29 2005 .mode = GENI_SE_DMA,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2006 .resources_init = geni_serial_resource_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2007 .set_rate = geni_serial_set_rate,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2008 .power_state = geni_serial_resource_state,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2009 };
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2010
abffd1e6c4f1c9 Praveen Talari 2025-11-10 @2011 static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2012 .console = true,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2013 .mode = GENI_SE_FIFO,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2014 .pd_data = {
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2015 .pd_flags = PD_FLAG_DEV_LINK_ON,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2016 .pd_names = (const char*[]) { "power", "perf" },
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2017 .num_pd_names = 2,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2018 },
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2019 .resources_init = geni_serial_pwr_init,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2020 .set_rate = geni_serial_set_level,
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2021 };
abffd1e6c4f1c9 Praveen Talari 2025-11-10 2022
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [tty:tty-testing] BUILD SUCCESS a2083fd1fa7aa0ef5cd8fd92396da0de2d0654b0
From: kernel test robot @ 2026-04-30 14:42 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-serial
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
branch HEAD: a2083fd1fa7aa0ef5cd8fd92396da0de2d0654b0 serial: qcom: Unify user-visible "Qualcomm" name
elapsed time: 4137m
configs tested: 191
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc defconfig gcc-15.2.0
arc randconfig-001 gcc-8.5.0
arc randconfig-001-20260430 gcc-8.5.0
arc randconfig-002 gcc-8.5.0
arc randconfig-002-20260430 gcc-8.5.0
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm randconfig-001 gcc-8.5.0
arm randconfig-001-20260430 gcc-8.5.0
arm randconfig-002 gcc-8.5.0
arm randconfig-002-20260430 gcc-8.5.0
arm randconfig-003 gcc-8.5.0
arm randconfig-003-20260430 gcc-8.5.0
arm randconfig-004 gcc-8.5.0
arm randconfig-004-20260430 gcc-8.5.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001 clang-23
arm64 randconfig-001-20260430 clang-23
arm64 randconfig-002 clang-23
arm64 randconfig-002-20260430 clang-23
arm64 randconfig-003 clang-23
arm64 randconfig-003-20260430 clang-23
arm64 randconfig-004 clang-23
arm64 randconfig-004-20260430 clang-23
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001 clang-23
csky randconfig-001-20260430 clang-23
csky randconfig-002 clang-23
csky randconfig-002-20260430 clang-23
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260430 gcc-14.3.0
hexagon randconfig-002-20260430 gcc-14.3.0
i386 allmodconfig clang-20
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 buildonly-randconfig-001-20260430 gcc-14
i386 buildonly-randconfig-002-20260430 gcc-14
i386 buildonly-randconfig-003-20260430 gcc-14
i386 buildonly-randconfig-004-20260430 gcc-14
i386 buildonly-randconfig-005-20260430 gcc-14
i386 buildonly-randconfig-006-20260430 gcc-14
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260430 clang-20
i386 randconfig-002-20260430 clang-20
i386 randconfig-003-20260430 clang-20
i386 randconfig-004-20260430 clang-20
i386 randconfig-005-20260430 clang-20
i386 randconfig-006-20260430 clang-20
i386 randconfig-007-20260430 clang-20
i386 randconfig-011 clang-20
i386 randconfig-011-20260430 clang-20
i386 randconfig-012 clang-20
i386 randconfig-012-20260430 clang-20
i386 randconfig-013 clang-20
i386 randconfig-013-20260430 clang-20
i386 randconfig-014 clang-20
i386 randconfig-014-20260430 clang-20
i386 randconfig-015 clang-20
i386 randconfig-015-20260430 clang-20
i386 randconfig-016 clang-20
i386 randconfig-016-20260430 clang-20
i386 randconfig-017 clang-20
i386 randconfig-017-20260430 clang-20
loongarch allmodconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260430 gcc-14.3.0
loongarch randconfig-002-20260430 gcc-14.3.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips xway_defconfig clang-23
nios2 allmodconfig clang-23
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 randconfig-001-20260430 gcc-14.3.0
nios2 randconfig-002-20260430 gcc-14.3.0
openrisc allmodconfig clang-23
openrisc allnoconfig clang-23
openrisc defconfig gcc-15.2.0
openrisc simple_smp_defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260430 gcc-13.4.0
parisc randconfig-002-20260430 gcc-13.4.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc mvme5100_defconfig gcc-15.2.0
powerpc pasemi_defconfig clang-23
powerpc randconfig-001-20260430 gcc-13.4.0
powerpc randconfig-002-20260430 gcc-13.4.0
powerpc64 randconfig-001-20260430 gcc-13.4.0
powerpc64 randconfig-002-20260430 gcc-13.4.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001 clang-23
riscv randconfig-001-20260430 clang-23
riscv randconfig-002 clang-23
riscv randconfig-002-20260430 clang-23
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001 clang-23
s390 randconfig-001-20260430 clang-23
s390 randconfig-002 clang-23
s390 randconfig-002-20260430 clang-23
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh defconfig gcc-14
sh randconfig-001 clang-23
sh randconfig-001-20260430 clang-23
sh randconfig-002 clang-23
sh randconfig-002-20260430 clang-23
sh se7206_defconfig gcc-15.2.0
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260430 gcc-12.5.0
sparc randconfig-002-20260430 gcc-12.5.0
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260430 gcc-12.5.0
sparc64 randconfig-002-20260430 gcc-12.5.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260430 gcc-12.5.0
um randconfig-002-20260430 gcc-12.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260430 gcc-14
x86_64 buildonly-randconfig-002-20260430 gcc-14
x86_64 buildonly-randconfig-003-20260430 gcc-14
x86_64 buildonly-randconfig-004-20260430 gcc-14
x86_64 buildonly-randconfig-005-20260430 gcc-14
x86_64 buildonly-randconfig-006-20260430 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-011-20260430 clang-20
x86_64 randconfig-012-20260430 clang-20
x86_64 randconfig-013-20260430 clang-20
x86_64 randconfig-014-20260430 clang-20
x86_64 randconfig-015-20260430 clang-20
x86_64 randconfig-016-20260430 clang-20
x86_64 randconfig-071-20260430 gcc-14
x86_64 randconfig-072-20260430 gcc-14
x86_64 randconfig-073-20260430 gcc-14
x86_64 randconfig-074-20260430 gcc-14
x86_64 randconfig-075-20260430 gcc-14
x86_64 randconfig-076-20260430 gcc-14
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa randconfig-001-20260430 gcc-12.5.0
xtensa randconfig-002-20260430 gcc-12.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 08/15] serial: earlycon: use uart_iotype_*() to simplify code
From: Andy Shevchenko @ 2026-04-30 15:03 UTC (permalink / raw)
To: Hugo Villeneuve
Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
linux-serial, Hugo Villeneuve
In-Reply-To: <20260428-tty-upio-v2-8-01c1857cf761@dimonoff.com>
On Tue, Apr 28, 2026 at 01:53:54PM -0400, Hugo Villeneuve wrote:
> Make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io()
> to simplify and improve code readability.
...
> + char address[64] = "";
TBH, I prefer two pr_info() calls as that approach
- doesn't require temporary buffer
- doesn't use heavy s*printf() on top of the existing printing
- doesn't limit the flexibility of each of the strings (64 might
become not enough in some cases, however unlikely to happen)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 05/15] serial: core: replace snprintf with more robust scnprintf
From: Andy Shevchenko @ 2026-04-30 15:07 UTC (permalink / raw)
To: Hugo Villeneuve
Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
linux-serial, Hugo Villeneuve
In-Reply-To: <20260428-tty-upio-v2-5-01c1857cf761@dimonoff.com>
On Tue, Apr 28, 2026 at 01:53:51PM -0400, Hugo Villeneuve wrote:
>
> Use scnprintf() so we could perhaps one day get rid of snprintf() entirely.
Ah, now I understand the approach in earlycon. Hmm... Still not sure which one
I prefer (it's not related to the contents of this patch anyway).
...
> default:
> strscpy(address, "*unknown*", sizeof(address));
Side note: This may use 2-argument strscpy().
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Andy Shevchenko @ 2026-04-30 15:08 UTC (permalink / raw)
To: Hugo Villeneuve
Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
linux-serial, Hugo Villeneuve
In-Reply-To: <20260428-tty-upio-v2-2-01c1857cf761@dimonoff.com>
On Tue, Apr 28, 2026 at 01:53:48PM -0400, Hugo Villeneuve wrote:
> To help simplify code that check on the io type mode of the port.
...
> +bool uart_iotype_legacy_io(enum uart_iotype iotype)
Why do we use 'legacy'? Still in use in modern CPUs...
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Hugo Villeneuve @ 2026-04-30 15:30 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
linux-serial, Hugo Villeneuve
In-Reply-To: <afNwaUf8esqaqxUe@black.igk.intel.com>
On Thu, 30 Apr 2026 17:08:25 +0200
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> On Tue, Apr 28, 2026 at 01:53:48PM -0400, Hugo Villeneuve wrote:
>
> > To help simplify code that check on the io type mode of the port.
>
> ...
>
> > +bool uart_iotype_legacy_io(enum uart_iotype iotype)
>
> Why do we use 'legacy'? Still in use in modern CPUs...
TBH I do not remember exactly where i got this, but what would you
suggest?
Maybe:
uart_iotype_io()
Hugo.
^ permalink raw reply
* Re: [PATCH v7 0/4] rust: add basic serial device bus abstractions
From: Rob Herring @ 2026-04-30 19:58 UTC (permalink / raw)
To: Markus Probst
Cc: 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, linux-serial, linux-kernel, rust-for-linux,
linux-pm, driver-core, dri-devel
In-Reply-To: <20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de>
On Wed, Apr 29, 2026 at 08:21:30PM +0200, Markus Probst wrote:
> This patch series adds the serdev device bus rust abstraction into the
> kernel.
>
> This abstraction will be used by a driver,
> which targets the MCU devices in Synology devices.
>
> Kari Argillander also messaged me, stating that he wants to write a
> watchdog driver with this abstraction (needing initial device data).
>
> @Rob: Are you willing to maintain these rust abstractions yourself,
> as you are the expert on this subsystem, otherwise I would take care of
> it with a "SERIAL DEVICE BUS [RUST]" section in the MAINTAINERS file. In
> the second case, I assume you are going to pick those patches as-is into
> your tree, after they have been reviewed?
Well I can ignore the Rust part as much as I ignore the C serdev part...
Honestly, I need to find someone else to maintain all of it as I don't
really have the bandwidth. I don't think we should split it though.
And I don't have a tree for serdev. Greg picks up the serdev patches. If
the Rust folks are fine with them, then I am.
Rob
^ permalink raw reply
* [PATCH] serial: atmel: honor CREAD in atmel_set_termios
From: Rakesh Alasyam @ 2026-05-01 8:13 UTC (permalink / raw)
To: richard.genoud, gregkh, jirislaby
Cc: nicolas.ferre, alexandre.belloni, claudiu.beznea, linux-serial,
linux-kernel, linux-arm-kernel, Rakesh Alasyam
Ignore received characters when CREAD is cleared by adding RXRDY
to ignore_status_mask.
This replaces an existing TODO in the driver.
Signed-off-by: Rakesh Alasyam <alasyamrakesh77@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 5d8c1cfc1c60..5b062d8ccabe 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2184,8 +2184,8 @@ static void atmel_set_termios(struct uart_port *port,
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= ATMEL_US_OVRE;
}
- /* TODO: Ignore all characters if CREAD is set.*/
-
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= ATMEL_US_RXRDY;
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Andy Shevchenko @ 2026-05-01 11:48 UTC (permalink / raw)
To: Hugo Villeneuve
Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
linux-serial, Hugo Villeneuve
In-Reply-To: <20260430113011.fea8a6edca8d864429a63fd5@hugovil.com>
On Thu, Apr 30, 2026 at 11:30:11AM -0400, Hugo Villeneuve wrote:
> On Thu, 30 Apr 2026 17:08:25 +0200
> Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > On Tue, Apr 28, 2026 at 01:53:48PM -0400, Hugo Villeneuve wrote:
...
> > > +bool uart_iotype_legacy_io(enum uart_iotype iotype)
> >
> > Why do we use 'legacy'? Still in use in modern CPUs...
>
> TBH I do not remember exactly where i got this, but what would you
> suggest?
>
> Maybe:
> uart_iotype_io()
Yep!
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox