From: Abhinav Saxena <xandfury@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jirislaby@kernel.org>,
Nathan Chancellor <nathan@kernel.org>,
Nick Desaulniers <nick.desaulniers+lkml@gmail.com>,
Bill Wendling <morbo@google.com>,
Justin Stitt <justinstitt@google.com>,
Kees Cook <kees@kernel.org>
Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
llvm@lists.linux.dev, linux-hardening@vger.kernel.org,
Abhinav Saxena <xandfury@gmail.com>
Subject: [RFC PATCH 3/5] tty: Add mock TTY driver for KUnit testing
Date: Tue, 26 Aug 2025 16:51:33 -0600 [thread overview]
Message-ID: <20250826-tty-tests-v1-3-e904a817df92@gmail.com> (raw)
In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com>
Implement a minimal mock TTY driver that provides deterministic
behavior for testing core TTY functionality. The driver simulates
immediate data transmission with configurable statistics tracking.
The mock driver enables testing of TTY core paths without hardware
dependencies or timing-sensitive behavior, ensuring reproducible
test results across different systems.
Signed-off-by: Abhinav Saxena <xandfury@gmail.com>
---
drivers/tty/tests/tty_mock.c | 186 +++++++++++++++++++++++++++++++++++++++++++
drivers/tty/tests/tty_mock.h | 34 ++++++++
2 files changed, 220 insertions(+)
diff --git a/drivers/tty/tests/tty_mock.c b/drivers/tty/tests/tty_mock.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5488760bb83c2837bb5226e3c33ec370c2c9c07
--- /dev/null
+++ b/drivers/tty/tests/tty_mock.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Minimal mock TTY driver for KUnit tests. Based on ttynull and ttyprintk
+ *
+ * Behavior:
+ * - write() pretends to transmit all bytes immediately
+ * - write_room() is large
+ * - chars_in_buffer() is 0
+ *
+ * Tracks only: total_writes, total_bytes, last_write_len
+ *
+ * Copyright (c) 2025 Abhinav Saxena <xandury@gmail.com>
+ */
+
+#include <kunit/visibility.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "tty_mock.h"
+
+#define TTYMOCK_NAME "ttymock"
+#define TTYMOCK_ROOM 4096
+
+static struct tty_port mock_port; /* single port */
+
+/* --- Stats (private) --- */
+static struct {
+ u64 total_writes;
+ u64 total_bytes;
+ u32 last_write_len;
+ spinlock_t lock;
+} mock_state;
+
+/* --- tty_operations --- */
+
+static int mock_open(struct tty_struct *tty, struct file *file)
+{
+ tty->driver_data = &mock_port;
+ return tty_port_open(&mock_port, tty, file);
+}
+
+static void mock_close(struct tty_struct *tty, struct file *file)
+{
+ tty_port_close(&mock_port, tty, file);
+ tty->driver_data = NULL;
+}
+
+static ssize_t mock_write(struct tty_struct *tty, const u8 *buf, size_t cnt)
+{
+ unsigned long flags;
+
+ if (!buf)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mock_state.lock, flags);
+ mock_state.total_writes++;
+ mock_state.total_bytes += cnt;
+ mock_state.last_write_len = cnt;
+ spin_unlock_irqrestore(&mock_state.lock, flags);
+
+ return cnt; /* everything written immediately */
+}
+
+static unsigned int mock_write_room(struct tty_struct *tty)
+{
+ return TTYMOCK_ROOM;
+}
+
+static unsigned int mock_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static const struct tty_operations mock_ops = {
+ .open = mock_open,
+ .close = mock_close,
+ .write = mock_write,
+ .write_room = mock_write_room,
+ .chars_in_buffer = mock_chars_in_buffer,
+};
+
+/* --- tty_port_operations --- */
+
+static bool mock_carrier_raised(struct tty_port *port)
+{
+ return true;
+}
+
+static void mock_shutdown(struct tty_port *port) { }
+
+static const struct tty_port_operations mock_port_ops = {
+ .carrier_raised = mock_carrier_raised,
+ .shutdown = mock_shutdown,
+};
+
+/* --- Public helpers --- */
+
+int tty_mock_register(struct tty_driver **out_drv, struct device *parent)
+{
+ struct tty_driver *drv;
+ struct device *dev;
+ int ret;
+
+ spin_lock_init(&mock_state.lock);
+
+ drv = tty_alloc_driver(1, TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_UNNUMBERED_NODE |
+ TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ drv->driver_name = TTYMOCK_NAME;
+ drv->name = TTYMOCK_NAME;
+ drv->type = TTY_DRIVER_TYPE_SERIAL;
+ drv->subtype = SERIAL_TYPE_NORMAL;
+ drv->init_termios = tty_std_termios;
+ tty_set_operations(drv, &mock_ops);
+
+ ret = tty_register_driver(drv);
+ if (ret) {
+ tty_driver_kref_put(drv);
+ return ret;
+ }
+
+ tty_port_init(&mock_port);
+ mock_port.ops = &mock_port_ops;
+
+ dev = tty_port_register_device(&mock_port, drv, 0, parent);
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ tty_unregister_driver(drv);
+ tty_driver_kref_put(drv);
+ tty_port_destroy(&mock_port);
+ return ret;
+ }
+
+ if (out_drv)
+ *out_drv = drv;
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_register);
+
+void tty_mock_unregister(struct tty_driver *drv)
+{
+ if (!drv)
+ return;
+
+ tty_port_unregister_device(&mock_port, drv, 0);
+ tty_unregister_driver(drv);
+ tty_driver_kref_put(drv);
+ tty_port_destroy(&mock_port);
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_unregister);
+
+struct tty_mock_stats tty_mock_get_stats(void)
+{
+ unsigned long flags;
+ struct tty_mock_stats state;
+
+ spin_lock_irqsave(&mock_state.lock, flags);
+ state.total_writes = mock_state.total_writes;
+ state.total_bytes = mock_state.total_bytes;
+ state.last_write_len = mock_state.last_write_len;
+ spin_unlock_irqrestore(&mock_state.lock, flags);
+
+ return state;
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_get_stats);
+
+void tty_mock_reset_stats(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mock_state.lock, flags);
+ mock_state.total_writes = 0;
+ mock_state.total_bytes = 0;
+ mock_state.last_write_len = 0;
+ spin_unlock_irqrestore(&mock_state.lock, flags);
+}
+EXPORT_SYMBOL_IF_KUNIT(tty_mock_reset_stats);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/tests/tty_mock.h b/drivers/tty/tests/tty_mock.h
new file mode 100644
index 0000000000000000000000000000000000000000..e61eeccc6181fc459d8db790b29350dbf3d9f588
--- /dev/null
+++ b/drivers/tty/tests/tty_mock.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * TTY - Mock driver
+ *
+ * Copyright (c) 2025 Abhinav Saxena <xandury@gmail.com>
+ *
+ */
+
+#ifndef _TTY_MOCK_H
+#define _TTY_MOCK_H
+
+#include <linux/device.h>
+#include <linux/tty_driver.h>
+#include <linux/types.h>
+
+/* Register a single-port mock tty driver and create device #0. */
+int tty_mock_register(struct tty_driver **out_drv, struct device *parent);
+/* Tear down device, unregister driver and destroy port. */
+void tty_mock_unregister(struct tty_driver *drv);
+
+/* --- Stats available to KUnit tests --- */
+struct tty_mock_stats {
+ u64 total_writes;
+ u64 total_bytes;
+ u32 last_write_len;
+};
+
+/* Returns a snapshot of counters. */
+struct tty_mock_stats tty_mock_get_stats(void);
+
+/* Reset all statistics counters to zero. */
+void tty_mock_reset_stats(void);
+
+#endif /* _TTY_MOCK_H */
--
2.43.0
next prev parent reply other threads:[~2025-08-26 22:51 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-26 22:51 [RFC PATCH 0/5] tty: Add KUnit test framework for TTY drivers Abhinav Saxena
2025-08-26 22:51 ` [RFC PATCH 1/5] tty: Add KUnit test infrastructure configuration Abhinav Saxena
2025-08-26 22:51 ` [RFC PATCH 2/5] tty: Add KUnit test helper functions Abhinav Saxena
2025-08-26 22:51 ` Abhinav Saxena [this message]
2025-08-26 22:51 ` [RFC PATCH 4/5] tty: Add KUnit tests for core TTY functionality Abhinav Saxena
2025-08-26 22:51 ` [RFC PATCH 5/5] tty: Add KUnit tests for ttynull driver Abhinav Saxena
2025-08-31 6:07 ` [RFC PATCH 0/5] tty: Add KUnit test framework for TTY drivers Jiri Slaby
2025-09-03 0:56 ` Abhinav Saxena
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250826-tty-tests-v1-3-e904a817df92@gmail.com \
--to=xandfury@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=jirislaby@kernel.org \
--cc=justinstitt@google.com \
--cc=kees@kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=llvm@lists.linux.dev \
--cc=morbo@google.com \
--cc=nathan@kernel.org \
--cc=nick.desaulniers+lkml@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).