This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nuttx-ntfc.git
The following commit(s) were added to refs/heads/main by this push:
new f68efe4 ntfc: honor line_buffered on QEMU devices
f68efe4 is described below
commit f68efe4e6080c0fa7018664cd284789d3314cf55
Author: raiden00pl <[email protected]>
AuthorDate: Mon Aug 31 18:29:39 2026 +0200
ntfc: honor line_buffered on QEMU devices
honor line_buffered on QEMU devices
Signed-off-by: raiden00pl <[email protected]>
Assisted-by: Claude Code
---
Documentation/config-yaml.rst | 12 ++++++----
Documentation/config.yaml | 1 +
src/ntfc/device/host.py | 21 +++++++++++++++++
src/ntfc/device/qemu.py | 15 ------------
src/ntfc/device/sim.py | 29 +++--------------------
tests/device/test_qemu.py | 54 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 87 insertions(+), 45 deletions(-)
diff --git a/Documentation/config-yaml.rst b/Documentation/config-yaml.rst
index e23ad76..8383404 100644
--- a/Documentation/config-yaml.rst
+++ b/Documentation/config-yaml.rst
@@ -126,7 +126,8 @@ for serial targets without flow control and remains the
default.
Set ``line_buffered: true`` for a core to write a complete command in one
transport operation instead. This reduces host-side overhead for ``sim`` and
-can also be enabled for serial targets with reliable flow control.
+``qemu`` and can also be enabled for serial targets with reliable flow
+control.
.. code-block:: yaml
@@ -138,9 +139,12 @@ can also be enabled for serial targets with reliable flow
control.
device: "sim"
line_buffered: true
-For the simulator, line-buffered mode also disables the pexpect per-send
-delay. Do not enable this option for serial targets that cannot
-reliably accept a full command at once.
+For host-based devices (``sim`` and ``qemu``), line-buffered mode also
+disables the pexpect per-send delay. Do not enable this option for serial
+targets that cannot reliably accept a full command at once.
+
+The target serial RX buffer must hold a whole command line, otherwise the
+tail of long commands is dropped and they time out.
**SMP (Symmetric Multi-Processing)**
diff --git a/Documentation/config.yaml b/Documentation/config.yaml
index 6d17628..b091634 100644
--- a/Documentation/config.yaml
+++ b/Documentation/config.yaml
@@ -91,6 +91,7 @@ product: # many products can be
supported in tests (pro
line_buffered: false # (optional) send each command in one
transport write.
# Keep false for the default byte-wise
behavior.
# Enable for sim or a serial transport with
flow control.
+ # Target serial RX buffer must hold a whole
command line.
dcmake: # (optional) Defines passed to CMake build
DEFINE1: "VALUE1"
DEFINE2: "VALUE2"
diff --git a/src/ntfc/device/host.py b/src/ntfc/device/host.py
index 6ccba99..d632387 100644
--- a/src/ntfc/device/host.py
+++ b/src/ntfc/device/host.py
@@ -43,6 +43,9 @@ if TYPE_CHECKING:
class DeviceHost(DeviceCommon):
"""This class implements common interface for host emulated devices."""
+ # appended when a command lacks a trailing newline
+ NEWLINE_PAD = b"\n"
+
def __init__(self, conf: "CoreConfig"):
"""Initialize host based device.
@@ -77,6 +80,24 @@ class DeviceHost(DeviceCommon):
return self.host_open(self._cmd)
+ def _write(self, data: bytes) -> None:
+ """Write to the host device."""
+ if not self.dev_is_health():
+ return
+
+ assert self._child
+
+ if data[-1] != ord("\n"):
+ data += self.NEWLINE_PAD
+
+ if self._conf.line_buffered:
+ self._child.send(data)
+ return
+
+ # send char by char to avoid line length full
+ for c in data:
+ self._child.send(bytes([c]))
+
def _write_ctrl(self, c: str) -> None:
"""Write a control character to the host device."""
if not self.dev_is_health():
diff --git a/src/ntfc/device/qemu.py b/src/ntfc/device/qemu.py
index 81521b8..a655d26 100644
--- a/src/ntfc/device/qemu.py
+++ b/src/ntfc/device/qemu.py
@@ -80,18 +80,3 @@ class DeviceQemu(DeviceHost):
def name(self) -> str:
"""Get device name."""
return "qemu"
-
- def _write(self, data: bytes) -> None: # pragma: no cover
- """Write to the host device."""
- if not self.dev_is_health():
- return
-
- assert self._child
-
- # send char by char to avoid line length full
- for c in data:
- self._child.send(bytes([c]))
-
- # add new line if missing
- if data[-1] != ord("\n"):
- self._child.send(b"\n")
diff --git a/src/ntfc/device/sim.py b/src/ntfc/device/sim.py
index 57cb226..d4ebeb5 100644
--- a/src/ntfc/device/sim.py
+++ b/src/ntfc/device/sim.py
@@ -35,6 +35,9 @@ if TYPE_CHECKING:
class DeviceSim(DeviceHost):
"""This class implements host-based sim emulator."""
+ # sometimes sim misses a single trailing newline, so send two
+ NEWLINE_PAD = b"\n\n"
+
def __init__(self, conf: "CoreConfig"):
"""Initialize sim emulator device."""
DeviceHost.__init__(self, conf)
@@ -55,29 +58,3 @@ class DeviceSim(DeviceHost):
def name(self) -> str:
"""Get device name."""
return "sim"
-
- def _write(self, data: bytes) -> None:
- """Write to the host device."""
- if not self.dev_is_health():
- return
-
- assert self._child
-
- if self._conf.line_buffered:
- if data[-1] != ord("\n"):
- # Sometimes sim misses a single trailing newline.
- data += b"\n\n"
-
- self._child.send(data)
- return
-
- # send char by char to avoid line length full
- for c in data:
- self._child.send(bytes([c]))
-
- # add new line if missing
- if data[-1] != ord("\n"):
- # sometimes new line send to sim is missing
- # so we have to send more than one new line
- self._child.send(b"\n")
- self._child.send(b"\n")
diff --git a/tests/device/test_qemu.py b/tests/device/test_qemu.py
index ac062fb..c99741e 100644
--- a/tests/device/test_qemu.py
+++ b/tests/device/test_qemu.py
@@ -25,6 +25,60 @@ import pytest
from ntfc.device.qemu import DeviceQemu
+def test_device_qemu_write_adds_newline():
+ with patch("ntfc.coreconfig.CoreConfig") as mockdevice:
+ config = mockdevice.return_value
+ config.os = "nuttx"
+ config.read_poll_interval = 0.1
+ config.line_buffered = False
+ qemu = DeviceQemu(config)
+
+ sent = []
+
+ class FakeChild:
+ def isalive(self):
+ return True
+
+ def send(self, data):
+ sent.append(data)
+
+ qemu._child = FakeChild()
+
+ qemu._write(b"abc")
+ assert sent == [b"a", b"b", b"c", b"\n"]
+
+ sent.clear()
+ qemu._write(b"abc\n")
+ assert sent == [b"a", b"b", b"c", b"\n"]
+
+
+def test_device_qemu_line_buffered_write():
+ with patch("ntfc.coreconfig.CoreConfig") as mockdevice:
+ config = mockdevice.return_value
+ config.os = "nuttx"
+ config.read_poll_interval = 0.1
+ config.line_buffered = True
+ qemu = DeviceQemu(config)
+
+ sent = []
+
+ class FakeChild:
+ def isalive(self):
+ return True
+
+ def send(self, data):
+ sent.append(data)
+
+ qemu._child = FakeChild()
+
+ qemu._write(b"abc")
+ assert sent == [b"abc\n"]
+
+ sent.clear()
+ qemu._write(b"abc\n")
+ assert sent == [b"abc\n"]
+
+
def test_device_qemu_open():
with patch("ntfc.coreconfig.CoreConfig") as mockdevice: