west: runners: blackmagicprobe: guess the gdb port based on the OS
Add some logic in blackmagicprobe.py to guess the GDB serial port based on the OS instead of just trying the flag or /dev/ttyACM0. The new logic explicitly looks for the well known path for BMP on Linux (/dev/ttyBmpGdb), so that we can remove it from the board files that have it set explicitly right now. At the same time it still allows setting the port from the flag and also checks an environment variable, so that it can be used for systems with multiple probes. As a fallback, it looks for a valid serial port, picking the first one (the probe creates two, the GDB one is the first one), and raise an exception if nothing is found. Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
This commit is contained in:
parent
cf39a737d9
commit
79856e7637
2 changed files with 209 additions and 3 deletions
|
@ -5,11 +5,106 @@
|
|||
'''Runner for flashing with Black Magic Probe.'''
|
||||
# https://github.com/blacksphere/blackmagic/wiki
|
||||
|
||||
import glob
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||
|
||||
try:
|
||||
import serial.tools.list_ports
|
||||
MISSING_REQUIREMENTS = False
|
||||
except ImportError:
|
||||
MISSING_REQUIREMENTS = True
|
||||
|
||||
# Default path for linux, based on the project udev.rules file.
|
||||
DEFAULT_LINUX_BMP_PATH = '/dev/ttyBmpGdb'
|
||||
|
||||
# Interface descriptor for the GDB port as defined in the BMP firmware.
|
||||
BMP_GDB_INTERFACE = 'Black Magic GDB Server'
|
||||
|
||||
# Product string as defined in the BMP firmware.
|
||||
BMP_GDB_PRODUCT = "Black Magic Probe"
|
||||
|
||||
# BMP vendor and product ID.
|
||||
BMP_GDB_VID = 0x1d50
|
||||
BMP_GDB_PID = 0x6018
|
||||
|
||||
LINUX_SERIAL_GLOB = '/dev/ttyACM*'
|
||||
DARWIN_SERIAL_GLOB = '/dev/cu.usbmodem*'
|
||||
|
||||
def blackmagicprobe_gdb_serial_linux():
|
||||
'''Guess the GDB port on Linux platforms.'''
|
||||
if os.path.exists(DEFAULT_LINUX_BMP_PATH):
|
||||
return DEFAULT_LINUX_BMP_PATH
|
||||
|
||||
if not MISSING_REQUIREMENTS:
|
||||
for port in serial.tools.list_ports.comports():
|
||||
if port.interface == BMP_GDB_INTERFACE:
|
||||
return port.device
|
||||
|
||||
ports = glob.glob(LINUX_SERIAL_GLOB)
|
||||
if not ports:
|
||||
raise RuntimeError(
|
||||
f'cannot find any valid port matching {LINUX_SERIAL_GLOB}')
|
||||
return sorted(ports)[0]
|
||||
|
||||
def blackmagicprobe_gdb_serial_darwin():
|
||||
'''Guess the GDB port on Darwin platforms.'''
|
||||
if not MISSING_REQUIREMENTS:
|
||||
bmp_ports = []
|
||||
for port in serial.tools.list_ports.comports():
|
||||
if port.description and port.description.startswith(
|
||||
BMP_GDB_PRODUCT):
|
||||
bmp_ports.append(port.device)
|
||||
if bmp_ports:
|
||||
return sorted(bmp_ports)[0]
|
||||
|
||||
ports = glob.glob(DARWIN_SERIAL_GLOB)
|
||||
if not ports:
|
||||
raise RuntimeError(
|
||||
f'cannot find any valid port matching {DARWIN_SERIAL_GLOB}')
|
||||
return sorted(ports)[0]
|
||||
|
||||
def blackmagicprobe_gdb_serial_win32():
|
||||
'''Guess the GDB port on Windows platforms.'''
|
||||
if not MISSING_REQUIREMENTS:
|
||||
bmp_ports = []
|
||||
for port in serial.tools.list_ports.comports():
|
||||
if port.vid == BMP_GDB_VID and port.pid == BMP_GDB_PID:
|
||||
bmp_ports.append(port.device)
|
||||
if bmp_ports:
|
||||
return sorted(bmp_ports)[0]
|
||||
|
||||
return 'COM1'
|
||||
|
||||
def blackmagicprobe_gdb_serial(port):
|
||||
'''Guess the GDB port for the probe.
|
||||
|
||||
Return the port to use, in order of priority:
|
||||
- the port specified manually
|
||||
- the port in the BMP_GDB_SERIAL environment variable
|
||||
- a guessed one depending on the host
|
||||
'''
|
||||
if port:
|
||||
return port
|
||||
|
||||
if 'BMP_GDB_SERIAL' in os.environ:
|
||||
return os.environ['BMP_GDB_SERIAL']
|
||||
|
||||
platform = sys.platform
|
||||
if platform.startswith('linux'):
|
||||
return blackmagicprobe_gdb_serial_linux()
|
||||
elif platform.startswith('darwin'):
|
||||
return blackmagicprobe_gdb_serial_darwin()
|
||||
elif platform.startswith('win32'):
|
||||
return blackmagicprobe_gdb_serial_win32()
|
||||
else:
|
||||
raise RuntimeError(f'unsupported platform: {platform}')
|
||||
|
||||
|
||||
class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
||||
'''Runner front-end for Black Magic probe.'''
|
||||
|
||||
|
@ -21,7 +116,8 @@ class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
|||
#
|
||||
# https://github.com/zephyrproject-rtos/zephyr/issues/50789
|
||||
self.elf_file = Path(cfg.elf_file).as_posix()
|
||||
self.gdb_serial = gdb_serial
|
||||
self.gdb_serial = blackmagicprobe_gdb_serial(gdb_serial)
|
||||
self.logger.info(f'using GDB serial: {self.gdb_serial}')
|
||||
if connect_rst:
|
||||
self.connect_rst_enable_arg = [
|
||||
'-ex', "monitor connect_rst enable",
|
||||
|
@ -49,8 +145,7 @@ class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
|||
|
||||
@classmethod
|
||||
def do_add_parser(cls, parser):
|
||||
parser.add_argument('--gdb-serial', default='/dev/ttyACM0',
|
||||
help='GDB serial port')
|
||||
parser.add_argument('--gdb-serial', help='GDB serial port')
|
||||
parser.add_argument('--connect-rst', '--connect-srst', action='store_true',
|
||||
help='Assert SRST during connect? (default: no)')
|
||||
|
||||
|
|
|
@ -5,11 +5,14 @@
|
|||
|
||||
import argparse
|
||||
from unittest.mock import patch, call
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from runners import blackmagicprobe
|
||||
from runners.blackmagicprobe import BlackMagicProbeRunner
|
||||
from conftest import RC_KERNEL_ELF, RC_GDB
|
||||
import serial.tools.list_ports
|
||||
|
||||
TEST_GDB_SERIAL = 'test-gdb-serial'
|
||||
|
||||
|
@ -88,3 +91,111 @@ def test_blackmagicprobe_connect_rst(cc, req, command, runner_config):
|
|||
runner.run(command)
|
||||
expected = EXPECTED_CONNECT_SRST_COMMAND[command]
|
||||
assert expected in cc.call_args_list[0][0][0]
|
||||
|
||||
@pytest.mark.parametrize('arg, env, expected', [
|
||||
# Argument has priority
|
||||
('/dev/XXX', None, '/dev/XXX'),
|
||||
('/dev/XXX', '/dev/YYYY', '/dev/XXX'),
|
||||
|
||||
# Then BMP_GDB_SERIAL env variable
|
||||
(None, '/dev/XXX', '/dev/XXX'),
|
||||
])
|
||||
def test_blackmagicprobe_gdb_serial_generic(arg, env, expected):
|
||||
if env:
|
||||
os.environ['BMP_GDB_SERIAL'] = env
|
||||
else:
|
||||
if 'BMP_GDB_SERIAL' in os.environ:
|
||||
os.environ.pop('BMP_GDB_SERIAL')
|
||||
|
||||
ret = blackmagicprobe.blackmagicprobe_gdb_serial(arg)
|
||||
assert expected == ret
|
||||
|
||||
@pytest.mark.parametrize('known_path, comports, globs, expected', [
|
||||
(True, False, ['/dev/ttyACM0', '/dev/ttyACM1'],
|
||||
blackmagicprobe.DEFAULT_LINUX_BMP_PATH),
|
||||
(False, True, [], '/dev/ttyACM3'),
|
||||
(False, False, ['/dev/ttyACM0', '/dev/ttyACM1'], '/dev/ttyACM0'),
|
||||
(False, False, ['/dev/ttyACM1', '/dev/ttyACM0'], '/dev/ttyACM0'),
|
||||
])
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
@patch('os.path.exists')
|
||||
@patch('glob.glob')
|
||||
def test_blackmagicprobe_gdb_serial_linux(gg, ope, stlpc, known_path, comports,
|
||||
globs, expected):
|
||||
gg.return_value = globs
|
||||
ope.return_value = known_path
|
||||
if comports:
|
||||
fake_comport1 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/ttyACM1')
|
||||
fake_comport1.interface = 'something'
|
||||
fake_comport2 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/ttyACM2')
|
||||
fake_comport2.interface = None
|
||||
fake_comport3 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/ttyACM3')
|
||||
fake_comport3.interface = blackmagicprobe.BMP_GDB_INTERFACE
|
||||
stlpc.return_value = [fake_comport1, fake_comport2, fake_comport3]
|
||||
else:
|
||||
stlpc.return_value = []
|
||||
|
||||
ret = blackmagicprobe.blackmagicprobe_gdb_serial_linux()
|
||||
assert expected == ret
|
||||
|
||||
@pytest.mark.parametrize('comports, globs, expected', [
|
||||
(True, [], '/dev/cu.usbmodem3'),
|
||||
(False, ['/dev/cu.usbmodemAABBCC0', '/dev/cu.usbmodemAABBCC1'],
|
||||
'/dev/cu.usbmodemAABBCC0'),
|
||||
(False, ['/dev/cu.usbmodemAABBCC1', '/dev/cu.usbmodemAABBCC0'],
|
||||
'/dev/cu.usbmodemAABBCC0'),
|
||||
])
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
@patch('glob.glob')
|
||||
def test_blackmagicprobe_gdb_serial_darwin(gg, stlpc, comports, globs, expected):
|
||||
gg.return_value = globs
|
||||
if comports:
|
||||
fake_comport1 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/cu.usbmodem1')
|
||||
fake_comport1.description = 'unrelated'
|
||||
fake_comport2 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/cu.usbmodem2')
|
||||
fake_comport2.description = None
|
||||
fake_comport3 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/cu.usbmodem3')
|
||||
fake_comport3.description = f'{blackmagicprobe.BMP_GDB_PRODUCT} v1234'
|
||||
fake_comport4 = serial.tools.list_ports_common.ListPortInfo(
|
||||
'/dev/cu.usbmodem4')
|
||||
fake_comport4.description = f'{blackmagicprobe.BMP_GDB_PRODUCT} v1234'
|
||||
stlpc.return_value = [fake_comport1, fake_comport2,
|
||||
fake_comport4, fake_comport3]
|
||||
else:
|
||||
stlpc.return_value = []
|
||||
|
||||
ret = blackmagicprobe.blackmagicprobe_gdb_serial_darwin()
|
||||
assert expected == ret
|
||||
|
||||
@pytest.mark.parametrize('comports, expected', [
|
||||
(True, 'COM4'),
|
||||
(False, 'COM1'),
|
||||
])
|
||||
@patch('serial.tools.list_ports.comports')
|
||||
def test_blackmagicprobe_gdb_serial_win32(stlpc, comports, expected):
|
||||
if comports:
|
||||
fake_comport1 = serial.tools.list_ports_common.ListPortInfo('COM2')
|
||||
fake_comport1.vid = 123
|
||||
fake_comport1.pid = 456
|
||||
fake_comport2 = serial.tools.list_ports_common.ListPortInfo('COM3')
|
||||
fake_comport2.vid = None
|
||||
fake_comport2.pid = None
|
||||
fake_comport3 = serial.tools.list_ports_common.ListPortInfo('COM4')
|
||||
fake_comport3.vid = blackmagicprobe.BMP_GDB_VID
|
||||
fake_comport3.pid = blackmagicprobe.BMP_GDB_PID
|
||||
fake_comport4 = serial.tools.list_ports_common.ListPortInfo('COM5')
|
||||
fake_comport4.vid = blackmagicprobe.BMP_GDB_VID
|
||||
fake_comport4.pid = blackmagicprobe.BMP_GDB_PID
|
||||
stlpc.return_value = [fake_comport1, fake_comport2,
|
||||
fake_comport4, fake_comport3]
|
||||
else:
|
||||
stlpc.return_value = []
|
||||
|
||||
ret = blackmagicprobe.blackmagicprobe_gdb_serial_win32()
|
||||
assert expected == ret
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue