pbhw: --one-wire never reached the suite's own sessions

The flag was plumbed through pbrig.Deployment to the host-tool subprocess
calls and nowhere else, so identity() and scan() opened a raw port and drove
a shared line as though it were two wires. On real one-wire hardware the
adapter's echo answers the knock before the device does, so the suite would
have died at its very first check — "the loader never answered; nothing below
can be trusted" — for the one deployment the flag exists to test, and every
result after it is gated on that check passing.

Both now open through pbrig.Rig.open_port(), which applies the deployment's
link mode. The gap underneath was that only the subprocess path could reach
those facts at all; anything driving the protocol in-process had to restate
them, and did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 13:25:46 +02:00
parent f71d76a815
commit 5d520a1ff9
2 changed files with 21 additions and 2 deletions

View File

@@ -53,7 +53,7 @@ class Suite:
"""The info block, which every later check takes its bounds from.""" """The info block, which every later check takes its bounds from."""
module = pbrig.load_pureboot(self.rig.d.pureboot) module = pbrig.load_pureboot(self.rig.d.pureboot)
self.rig.reset() self.rig.reset()
port = module.Port(self.rig.d.port, self.rig.d.baud) port = self.rig.open_port() # wrapped for the echo where the line is shared
try: try:
loader = module.Loader(port) loader = module.Loader(port)
if self.rig.d.autobaud: if self.rig.d.autobaud:
@@ -87,7 +87,10 @@ class Suite:
rate = module.scan_rate(self.rig.d.baud, pct) rate = module.scan_rate(self.rig.d.baud, pct)
self.rig.reset() self.rig.reset()
try: try:
port = module.Port(self.rig.d.port, rate) # Same wrap as identity(): on a shared line an undiscarded
# echo answers every rate a scan probes, so the walk would
# report the first one it tried.
port = self.rig.open_port(rate)
except module.Error as error: except module.Error as error:
self.check("scan opens every probe rate", False, f"{rate} Bd: {error}") self.check("scan opens every probe rate", False, f"{rate} Bd: {error}")
return return

View File

@@ -281,6 +281,22 @@ class Rig:
return 99, f"TIMEOUT after {timeout}s\n{expired.stdout or ''}{expired.stderr or ''}" return 99, f"TIMEOUT after {timeout}s\n{expired.stdout or ''}{expired.stderr or ''}"
return result.returncode, (result.stdout or "") + (result.stderr or "") return result.returncode, (result.stdout or "") + (result.stderr or "")
def open_port(self, baud: int | None = None):
"""A port opened the way this deployment says to speak to the board.
Everything the rig runs as a *subprocess* gets its flags from
`pureboot()` above; anything that drives the protocol in-process has
to reach the same facts, and until this existed only the subprocess
path could. A shared line is the one where that gap is fatal rather
than untidy: the host reads back every byte it writes, so an
undiscarded echo answers the knock before the device does. Open
through here and a one-wire deployment cannot be silently driven as
a two-wire one.
"""
module = load_pureboot(self.d.pureboot)
port = module.Port(self.d.port, self.d.baud if baud is None else baud)
return module.OneWirePort(port) if self.d.one_wire else port
def capture(self, seconds: float = 2.0, baud: int | None = None) -> bytes: def capture(self, seconds: float = 2.0, baud: int | None = None) -> bytes:
"""Listen to whatever the board is saying, at an arbitrary rate. """Listen to whatever the board is saying, at an arbitrary rate.