pureboot: the info block is what proves a knock landed
A prompt byte alone does not: one left over from a previous session can still be in the pipeline while the port opening resets the device into a fresh window, where the bare command that follows is discarded. Each attempt is now the whole handshake, retried until the block comes back. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -364,26 +364,37 @@ class Loader:
|
||||
self.info = None
|
||||
|
||||
def connect(self, wait):
|
||||
"""Knock until the window answers, then read the info block. Also
|
||||
converges into a live session: the knock bytes are ignored there and
|
||||
the drain absorbs whatever they produced."""
|
||||
self.port.flush_input()
|
||||
"""Knock until the info block comes back. The block is what proves the
|
||||
loader is listening — a prompt byte alone does not, since one left over
|
||||
from a previous session can still be in the pipeline while the port
|
||||
opening resets the device into a fresh activation window, where a
|
||||
command without its knock is discarded. Each attempt is therefore the
|
||||
whole handshake, retried until it produces the block or the window
|
||||
closes. Also converges into a live session: the knock bytes are ignored
|
||||
there and the drain absorbs whatever they produced."""
|
||||
deadline = time.monotonic() + wait
|
||||
knocks = 0
|
||||
while True:
|
||||
self.port.flush_input()
|
||||
self.port.write(b"pb")
|
||||
knocks += 1
|
||||
if PROMPT in self.port.read_available(0.4):
|
||||
break
|
||||
while self.port.read_available(0.3):
|
||||
pass
|
||||
self.port.write(b"b")
|
||||
try:
|
||||
block = self.port.read_exact(12, 2.0)
|
||||
except Error:
|
||||
block = b""
|
||||
# A version the tool cannot speak is the loader's own answer,
|
||||
# not a failed knock: Info reports it rather than retrying.
|
||||
if block[0:2] == b"PB":
|
||||
self.info = Info(block)
|
||||
self._expect_prompt()
|
||||
verbose(f"loader answered knock {knocks}; info block read")
|
||||
return self.info
|
||||
if time.monotonic() > deadline:
|
||||
raise Error("no answer — reset the device within its activation window")
|
||||
while self.port.read_available(0.3):
|
||||
pass
|
||||
self.port.write(b"b")
|
||||
self.info = Info(self.port.read_exact(12, 2.0))
|
||||
self._expect_prompt()
|
||||
verbose(f"loader answered knock {knocks}; info block read")
|
||||
return self.info
|
||||
|
||||
def _expect_prompt(self, timeout=2.0):
|
||||
byte = self.port.read_exact(1, timeout)
|
||||
|
||||
Reference in New Issue
Block a user