From 48a02d60e2455e22706248d9b197b323f1660348 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 10 Jan 2023 09:06:46 +0100 Subject: [PATCH 1/3] dist/tools/usb-serial/ttys.py: Allow combining simple formats For scripts it can be useful to output not only one, but multiple formats (e.g. to obtain both path and serial of a TTY). The script now support passing multiple formats. Note that only simple formats can be combined, as the JSON and markdown table won't mix well with any other format. --- dist/tools/usb-serial/README.md | 14 +++++++- dist/tools/usb-serial/ttys.py | 60 +++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/dist/tools/usb-serial/README.md b/dist/tools/usb-serial/README.md index e7fcf4581a23..e117cac788b3 100644 --- a/dist/tools/usb-serial/README.md +++ b/dist/tools/usb-serial/README.md @@ -23,7 +23,19 @@ With the parameter `--format FORMAT` a different format than the default markdown table can be selected, e.g. `json` results in JSON output and `path` will print the paths of the matching TTYs without any formatting (useful for scripting). The full list of formats can be obtained by running the script with -the `--help` parameter +the `--help` parameter. + +Note: Formats other than `json` and `table` can be combined. A script that +required both path and serial of TTYs could use: + +``` +./ttys.py --format path serial +``` + +This will output one TTY per line with the selected fields separated by space. +To use a different separator than space (e.g. to create CSV files), the option +`--format-sep` can be used. If a field value contains the separator, it will +be quoted and quotation chars inside will be escaped. ### Filtering diff --git a/dist/tools/usb-serial/ttys.py b/dist/tools/usb-serial/ttys.py index dd1bc4883e0e..a22d4e9f17a7 100755 --- a/dist/tools/usb-serial/ttys.py +++ b/dist/tools/usb-serial/ttys.py @@ -61,9 +61,7 @@ def parse_args(args): Parse the given command line style arguments with argparse """ desc = "List and filter TTY interfaces that might belong to boards" - supported_formats = { - "table", - "json", + formats_combinable = { "path", "serial", "vendor", @@ -74,13 +72,20 @@ def parse_args(args): "ctime", "iface_num", } + formats_uncombinable = { + "table", + "json", + } + supported_formats = formats_combinable.union(formats_uncombinable) parser = argparse.ArgumentParser(description=desc) parser.add_argument("--most-recent", action="store_true", help="Print only the most recently connected matching " + "TTY") - parser.add_argument("--format", default="table", type=str, + parser.add_argument("--format", default=["table"], type=str, nargs='+', help=f"How to format the TTYs. Supported formats: " f"{sorted(supported_formats)}") + parser.add_argument("--format-sep", default=" ", type=str, + help="Separator between formats (default: space)") parser.add_argument("--serial", default=None, type=str, help="Print only devices matching this serial") parser.add_argument("--driver", default=None, type=str, @@ -108,8 +113,17 @@ def parse_args(args): args = parser.parse_args() - if args.format not in supported_formats: - sys.exit(f"Format \"{args.format}\" not supported") + if len(args.format) == 1: + if args.format[0] not in supported_formats: + sys.exit(f"Format \"{args.format[0]}\" not supported") + else: + for fmt in args.format: + if fmt not in formats_combinable: + if fmt in formats_uncombinable: + sys.exit(f"Format \"{fmt}\" cannot be combined with " + + "other formats") + else: + sys.exit(f"Format \"{fmt}\" not supported") if args.exclude_serial is None: if "EXCLUDE_TTY_SERIAL" in os.environ: @@ -154,21 +168,31 @@ def print_results(args, ttys): """ Print the given TTY devices according to the given args """ - if args.format == "json": - print(json.dumps(ttys, indent=2)) - return + if len(args.format) == 1: + if args.format[0] == "json": + print(json.dumps(ttys, indent=2)) + return - if args.format == "table": - for tty in ttys: - tty["ctime"] = time.strftime("%H:%M:%S", - time.localtime(tty["ctime"])) - headers = ["path", "driver", "vendor", "model", "model_db", "serial", - "ctime", "iface_num"] - print_table(ttys, headers) - return + if args.format[0] == "table": + for tty in ttys: + tty["ctime"] = time.strftime("%H:%M:%S", + time.localtime(tty["ctime"])) + headers = ["path", "driver", "vendor", "model", "model_db", + "serial", "ctime", "iface_num"] + print_table(ttys, headers) + return for tty in ttys: - print(tty[args.format]) + line = "" + for fmt in args.format: + item = tty[fmt] + if item.rfind(args.format_sep) >= 0: + # item contains separator --> quote it + # using json.dumps to also escape quotation chars and other + # unsafe stuff + item = json.dumps(item) + line += f"{args.format_sep}{item}" + print(line[len(args.format_sep):]) def generate_filters(args): From 015ee050f2e0284a4c90ce05a8fae049839c2c14 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 10 Jan 2023 09:32:35 +0100 Subject: [PATCH 2/3] makefiles/tools/serial.inc.mk: Allow detection of debug adapter Boards with an integrated debugger/programmer that also provides the serial as UART <--> USB adapter, the TTY serial matches the serial of the programmer. This adapts the `serial.inc.mk` to set the `DEBUG_ADAPTER_ID` to the TTY serial if (and only if) `MOST_RECENT_PORT` *and* `DEBUG_ADAPTER_ID_IS_TTY_SERIAL` both have a value of `1`. Boards with an integrated programmer are expected to set `DEBUG_ADAPTER_ID_IS_TTY_SERIAL` to `1` in their `Makefile.include`. --- boards/arduino-mega2560/Makefile.include | 4 ++-- doc/doxygen/src/flashing.md | 14 +++++++------- makefiles/tools/serial.inc.mk | 11 ++++++++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/boards/arduino-mega2560/Makefile.include b/boards/arduino-mega2560/Makefile.include index f8c28919fff9..67afc9315b92 100644 --- a/boards/arduino-mega2560/Makefile.include +++ b/boards/arduino-mega2560/Makefile.include @@ -25,11 +25,11 @@ ARDUINO_MEGA2560_COMPAT_WITH_CLONES ?= 1 ifeq (1,$(ARDUINO_MEGA2560_COMPAT_WITH_CLONES)) TTY_SELECT_CMD := $(RIOTTOOLS)/usb-serial/ttys.py \ --most-recent \ - --format path \ + --format path serial \ $(TTY_BOARD_FILTER) || \ $(RIOTTOOLS)/usb-serial/ttys.py \ --most-recent \ - --format path \ + --format path serial \ $(TTY_BOARD_FILTER_CLONE) endif diff --git a/doc/doxygen/src/flashing.md b/doc/doxygen/src/flashing.md index 46f0b8301ef9..298d4cd50ab1 100644 --- a/doc/doxygen/src/flashing.md +++ b/doc/doxygen/src/flashing.md @@ -478,20 +478,20 @@ In most cases, just adding a simple `TTY_BOARD_FILTER` is sufficient. If we however have wildly different flavors of the same board (e.g. genuine Arduino Mega 2560 with an ATmega16U2 and clones with a cheap USB to UART bridge) that we all want to support, we have to instead provide a `TTY_SELECT_CMD` that prints -the path to the TTY and exists with `0` if a TTY was found, or that exists with -`1` and prints nothing when no TTY was found. We can still use the `ttys.py` -script to detect all Arduino Mega 2560 versions: We first try to detect a -genuine Arduino Mega and fall back to selecting cheap USB UART bridges when that -fails using the `||` shell operator: +the path to and the serial of the TTY (separated by a space) and exists with +`0` if a TTY was found, or that exists with `1` and prints nothing when no TTY +was found. We can still use the `ttys.py` script to detect all Arduino Mega +2560 versions: We first try to detect a genuine Arduino Mega and fall back to +selecting cheap USB UART bridges when that fails using the `||` shell operator: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TTY_SELECT_CMD := $(RIOTTOOLS)/usb-serial/ttys.py \ --most-recent \ - --format path \ + --format path serial \ --vendor 'Arduino' \ --model-db 'Mega 2560|Mega ADK' || \ $(RIOTTOOLS)/usb-serial/ttys.py \ --most-recent \ - --format path \ + --format path serial \ --driver 'cp210x' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/makefiles/tools/serial.inc.mk b/makefiles/tools/serial.inc.mk index d2097afaa185..cb6e768a465c 100644 --- a/makefiles/tools/serial.inc.mk +++ b/makefiles/tools/serial.inc.mk @@ -5,10 +5,15 @@ ifeq (1,$(MOST_RECENT_PORT)) endif TTY_SELECT_CMD ?= $(RIOTTOOLS)/usb-serial/ttys.py \ --most-recent \ - --format path \ + --format path serial \ $(TTY_BOARD_FILTER) - PORT_DETECTED := $(shell $(TTY_SELECT_CMD) || echo 'no-tty-detected') - PORT ?= $(PORT_DETECTED) + TTY_DETECTED := $(shell $(TTY_SELECT_CMD) || echo 'no-tty-detected no-serial-detected') + PORT_DETECTED := $(firstword $(TTY_DETECTED)) + PORT_SERIAL_DETECTED := $(lastword $(TTY_DETECTED)) + PORT ?= $(firstword $(TTY_DETECTED)) + ifeq (1,$(DEBUG_ADAPTER_ID_IS_TTY_SERIAL)) + DEBUG_ADAPTER_ID ?= $(PORT_SERIAL_DETECTED) + endif endif # Otherwise, use as default the most commonly used ports on Linux and OSX PORT_LINUX ?= /dev/ttyACM0 From 64d4aec81281bc798286509b956c78b7ddd78803 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 10 Jan 2023 09:37:29 +0100 Subject: [PATCH 3/3] boards: Provide debug adapter ID from serial where possible Set `DEBUG_ADAPTER_ID_IS_TTY_SERIAL` to `1` for those boards to allow automatic detection of the debug adapter with `MOST_RECENT_PORT=1`. --- boards/common/nucleo/Makefile.include | 4 ++++ boards/hifive1b/Makefile.include | 4 ++++ boards/microbit-v2/Makefile.include | 4 ++++ boards/nrf52840dk/Makefile.include | 4 ++++ boards/qn9080dk/Makefile.include | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/boards/common/nucleo/Makefile.include b/boards/common/nucleo/Makefile.include index a5da8b7d5341..d25e33a0b187 100644 --- a/boards/common/nucleo/Makefile.include +++ b/boards/common/nucleo/Makefile.include @@ -11,6 +11,10 @@ include $(RIOTMAKE)/boards/stm32.inc.mk # USB serials to only select the UART bridge of embedded STLink debuggers. TTY_BOARD_FILTER := --model 'STM32 STLink' +# The TTY serial also is the ID of the debug adapter, as the TTY is provided by +# the debug adapter +DEBUG_ADAPTER_ID_IS_TTY_SERIAL := 1 + # variable needed by cpy2remed PROGRAMMER # it contains name of ST-Link removable media diff --git a/boards/hifive1b/Makefile.include b/boards/hifive1b/Makefile.include index 5d45a49bcdc8..c78c19baa691 100644 --- a/boards/hifive1b/Makefile.include +++ b/boards/hifive1b/Makefile.include @@ -25,5 +25,9 @@ endif # the UART bridge to the ESP32-SOLO-1 MCU instead of the FE310 MCU on the board. TTY_BOARD_FILTER := --model HiFive --iface-num 0 +# The TTY serial also is the ID of the debug adapter, as the TTY is provided by +# the debug adapter +DEBUG_ADAPTER_ID_IS_TTY_SERIAL := 1 + TESTRUNNER_RESET_DELAY = 1 $(call target-export-variables,test,TESTRUNNER_RESET_DELAY) diff --git a/boards/microbit-v2/Makefile.include b/boards/microbit-v2/Makefile.include index e853d5000d8c..c16ff76aee32 100644 --- a/boards/microbit-v2/Makefile.include +++ b/boards/microbit-v2/Makefile.include @@ -7,6 +7,10 @@ PROGRAMMERS_SUPPORTED += pyocd # programmer firmware revisions "fix" that. TTY_BOARD_FILTER := --model ".?BBC micro:bit CMSIS-DAP.?" +# The TTY serial also is the ID of the debug adapter, as the TTY is provided by +# the debug adapter +DEBUG_ADAPTER_ID_IS_TTY_SERIAL := 1 + # The board is not recognized automatically by pyocd, so the CPU target # option is passed explicitly PYOCD_FLASH_TARGET_TYPE ?= -t $(CPU) diff --git a/boards/nrf52840dk/Makefile.include b/boards/nrf52840dk/Makefile.include index c86c0c8eaba8..7a7fc118c90a 100644 --- a/boards/nrf52840dk/Makefile.include +++ b/boards/nrf52840dk/Makefile.include @@ -2,4 +2,8 @@ # USB serials to only select the UART bridge of integrated J-Link debugger. TTY_BOARD_FILTER := --model J-Link +# The TTY serial also is the ID of the debug adapter, as the TTY is provided by +# the debug adapter +DEBUG_ADAPTER_ID_IS_TTY_SERIAL := 1 + include $(RIOTBOARD)/common/nrf52xxxdk/Makefile.include diff --git a/boards/qn9080dk/Makefile.include b/boards/qn9080dk/Makefile.include index 29929054de74..9e02f2cd32fe 100644 --- a/boards/qn9080dk/Makefile.include +++ b/boards/qn9080dk/Makefile.include @@ -7,5 +7,9 @@ CFLAGS += \ QN908X_JLINK ?= $(QN9080DK_JLINK) JLINK_DEVICE ?= QN9080A +# The TTY serial also is the ID of the debug adapter, as the TTY is provided by +# the debug adapter +DEBUG_ADAPTER_ID_IS_TTY_SERIAL := 1 + # Include default QN908x board config include $(RIOTBOARD)/common/qn908x/Makefile.include