-
Notifications
You must be signed in to change notification settings - Fork 182
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
remote/client: Provide an internal console
At present Labgrid uses microcom as its console. This has some limitations: - console output is lost between when the board is reset and microcom connects - txdelay cannot be handled in microcom, meaning that boards may fail to receive expected output - the console may echo a few characters back to the caller in the time between when 'labgrid-client console' is executed and when microcom starts (which causes failures with U-Boot test system) For many use cases, microcom is more than is needed, so provide a simple internal terminal which resolved the above problems. It is enabled by a '-i' option to the 'console' command, as well as an environment variable, so that it can be adjustly without updating a lot of scripts. To exit, press Ctrl-] twice, quickly. Signed-off-by: Simon Glass <sjg@chromium.org>
- Loading branch information
Showing
2 changed files
with
160 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import asyncio | ||
import collections | ||
import logging | ||
import os | ||
import sys | ||
from pexpect import TIMEOUT | ||
from serial.serialutil import SerialException | ||
import termios | ||
import time | ||
|
||
EXIT_CHAR = 0x1d # FS (Ctrl + ]) | ||
|
||
async def microcom(session, host, port, place, resource, logfile, listen_only): | ||
call = ['microcom', '-q', '-s', str(resource.speed), '-t', f"{host}:{port}"] | ||
|
||
if listen_only: | ||
call.append("--listenonly") | ||
|
||
if logfile: | ||
call.append(f"--logfile={logfile}") | ||
logging.info(f"connecting to {resource} calling {' '.join(call)}") | ||
try: | ||
p = await asyncio.create_subprocess_exec(*call) | ||
except FileNotFoundError as e: | ||
raise ServerError(f"failed to execute microcom: {e}") | ||
while p.returncode is None: | ||
try: | ||
await asyncio.wait_for(p.wait(), 1.0) | ||
except asyncio.TimeoutError: | ||
# subprocess is still running | ||
pass | ||
|
||
try: | ||
session._check_allowed(place) | ||
except UserError: | ||
p.terminate() | ||
try: | ||
await asyncio.wait_for(p.wait(), 1.0) | ||
except asyncio.TimeoutError: | ||
# try harder | ||
p.kill() | ||
await asyncio.wait_for(p.wait(), 1.0) | ||
raise | ||
if p.returncode: | ||
print("connection lost", file=sys.stderr) | ||
return p.returncode | ||
|
||
BUF_SIZE = 1024 | ||
|
||
async def run(serial, log_fd, listen_only): | ||
prev = collections.deque(maxlen=2) | ||
|
||
deadline = None | ||
to_serial = b'' | ||
next_serial = time.monotonic() | ||
txdelay = serial.txdelay | ||
while True: | ||
activity = bool(to_serial) | ||
try: | ||
data = serial.read(size=BUF_SIZE, timeout=0.001) | ||
if data: | ||
activity = True | ||
sys.stdout.buffer.write(data) | ||
sys.stdout.buffer.flush() | ||
if log_fd: | ||
log_fd.write(data) | ||
log_fd.flush() | ||
|
||
except TIMEOUT: | ||
pass | ||
|
||
except SerialException: | ||
break | ||
|
||
if not listen_only: | ||
data = os.read(sys.stdin.fileno(), BUF_SIZE) | ||
if data: | ||
activity = True | ||
if not deadline: | ||
deadline = time.monotonic() + .5 # seconds | ||
prev.extend(data) | ||
count = prev.count(EXIT_CHAR) | ||
if count == 2: | ||
break | ||
|
||
to_serial += data | ||
|
||
if to_serial and time.monotonic() > next_serial: | ||
serial._write(to_serial[:1]) | ||
to_serial = to_serial[1:] | ||
next_serial += txdelay | ||
|
||
if deadline and time.monotonic() > deadline: | ||
prev.clear() | ||
deadline = None | ||
if not activity: | ||
time.sleep(.001) | ||
|
||
# Blank line to move past any partial output | ||
print() | ||
|
||
async def internal(serial, logfile, listen_only): | ||
try: | ||
if not listen_only: | ||
fd = sys.stdin.fileno() | ||
old = termios.tcgetattr(fd) | ||
new = termios.tcgetattr(fd) | ||
new[3] = new[3] & ~(termios.ICANON | termios.ECHO | termios.ISIG) | ||
new[6][termios.VMIN] = 0 | ||
new[6][termios.VTIME] = 0 | ||
termios.tcsetattr(fd, termios.TCSANOW, new) | ||
|
||
log_fd = None | ||
if logfile: | ||
log_fd = open(logfile, 'wb') | ||
|
||
logging.info('Console start:') | ||
await run(serial, log_fd, listen_only) | ||
|
||
finally: | ||
if not listen_only: | ||
termios.tcsetattr(fd, termios.TCSAFLUSH, old) | ||
if log_fd: | ||
log_fd.close() |