Skip to content

Commit

Permalink
all: Modify CLI and USB helpers to allow passing USB paths as addresses
Browse files Browse the repository at this point in the history
Issue #33

Being able to pass USB device addresses as paths instead of vid:pid addresses
is a nice feature to have. It allows running snagboot on multiple devices of
the same model that share the same ROM code or U-Boot gadget vid:pid address.

Please note that i.MX and AM335x supports for this feature are not ready
yet.

Remove snagflash/utils.py since all of these helpers can be imported from
snagrecover/utils.py anyway.

Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
  • Loading branch information
rgantois committed Nov 29, 2023
1 parent 72907c9 commit 2980889
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 113 deletions.
4 changes: 2 additions & 2 deletions docs/fw_binaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ OPTEE can also work.

configuration:
* path
* usb vid:pid (only if you have configured custom USB IDs in TF-A)
* usb vid:pid or bus-port1.port2.[...] (only if you have configured custom USB IDs in TF-A)

### Example build process for an stm32mp15-based board

Expand Down Expand Up @@ -254,7 +254,7 @@ specify them with the usb firmware parameter. SPL should support DFU.

configuration:
* path
* usb vid:pid (only if you changed the default vid/pid in U-Boot's config)
* usb vid:pid or bus-port1.port2.[...] (only if you have configured custom USB IDs in TF-A)

**u-boot:** FIT container with U-Boot proper for A53 and device tree blobs

Expand Down
6 changes: 4 additions & 2 deletions docs/snagflash.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ can add the following udev rule to get access:

`SUBSYSTEM=="usb", ATTRS{idVendor}=="vid", ATTRS{idProduct}=="pid", MODE="0660", TAG+="uaccess"`

Alternatively, you can also use bus-ports addresses, which can be more stable.

## DFU mode

In DFU mode, snagflash takes additional arguments :

* `-p --port vid:pid`
* `-p --port [vid:pid | bus-port1.port2.(...)]`
The USB address of the DFU device exposed by U-Boot
* `-D --dfu-config altsetting:path`
The altsetting and path of a file to download to the board. This should match
Expand Down Expand Up @@ -92,7 +94,7 @@ rules to create symlinks when a certain VID:PID pair is detected:

In fastboot mode, snagflash takes two additional arguments:

* `-p --port vid:pid`
* `-p --port [vid:pid | bus-port1.port2.(...)]`
The USB address of the Fastboot device exposed by U-Boot
* `-f --fastboot_cmd cmd:args`
A fastboot command to be sent to U-Boot. The following commands are supported
Expand Down
9 changes: 8 additions & 1 deletion docs/snagrecover.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ If you have changed your ROM code's USB VID/PID (this usually isn't the case),
you must specify it using the --rom-usb parameter:

* `--rom-usb vid:pid`
USB device address vid:pid
USB device address vid:pid or bus-port1.port2.[...]
e.g. --rom-usb 1111:ffff
e.g. --rom-usb 3-1.2

If you do not want to rely on vid:pid addresses, you can instead use bus-ports
addresses to specify USB devices in a more stable way.The bus-ports address of a
USB device can be found in sysfs :

`/sys/bus/usb/devices/...`.

When recovering an AM335x SOC via UART using the snagrecover, you have to pass
the --uart flag to the CLI. You can also pass the --baudrate flag in case the
Expand Down
4 changes: 2 additions & 2 deletions src/snagflash/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from snagflash.dfu import dfu_cli
from snagflash.ums import ums
from snagflash.fastboot import fastboot
from snagflash.utils import cli_error
from snagrecover.utils import cli_error
import logging
import sys

Expand All @@ -43,7 +43,7 @@ def cli():
common.add_argument("--logfile", help="set logfile", default="board_flashing.log")
common.add_argument("--version", help="show version", action="store_true")
common.add_argument("-P", "--protocol", help="Protocol to use for flashing", choices=["dfu","ums","fastboot"])
common.add_argument("-p", "--port", help="USB device address for DFU and Fastboot commands", metavar="vid:pid")
common.add_argument("-p", "--port", help="USB device address for DFU and Fastboot commands", metavar="vid:pid|bus-port1.port2.[...]")
common.add_argument("--timeout", help="USB timeout, sometimes increasing this is necessary when downloading large files", default=60000)
dfuargs = parser.add_argument_group("DFU")
dfuargs.add_argument("-D", "--dfu-config", help="The altsetting and path of a file to download to the board. in DFU mode", action="append", metavar="altsetting:path")
Expand Down
9 changes: 3 additions & 6 deletions src/snagflash/dfu.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from snagrecover.protocols import dfu
import logging
logger = logging.getLogger("snagflash")
from snagflash.utils import cli_error, get_usb, reset_usb
from snagrecover.utils import parse_usb_addr, get_usb, cli_error, reset_usb
from usb.core import Device

def dfu_detach(dev: Device, altsetting: int = 0):
Expand Down Expand Up @@ -50,12 +50,9 @@ def dfu_reset(dev: Device):
def dfu_cli(args):
if args.dfu_config is None and not args.dfu_detach and not args.dfu_reset:
cli_error("missing command line argument --dfu-config")
if (args.port is None) or (":" not in args.port):
if (args.port is None):
cli_error("missing command line argument --port [vid:pid]")
dev_addr = args.port.split(":")
vid = int(dev_addr[0], 16)
pid = int(dev_addr[1], 16)
dev = get_usb(vid, pid)
dev = get_usb(parse_usb_addr(args.port))
dev.default_timeout = int(args.timeout)
altsetting = 0
if args.dfu_config:
Expand Down
11 changes: 4 additions & 7 deletions src/snagflash/fastboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from snagrecover.protocols import fastboot as fb
from snagflash.utils import get_usb
from snagrecover.utils import parse_usb_addr, get_usb
import sys
import logging
logger = logging.getLogger("snagflash")

def fastboot(args):
if (args.port is None) or (":" not in args.port):
print("Error: Missing command line argument --port [vid:pid]")
if (args.port is None):
print("Error: Missing command line argument --port vid:pid|bus-port1.port2.[...]")
sys.exit(-1)
dev_addr = args.port.split(":")
vid = int(dev_addr[0], 16)
pid = int(dev_addr[1], 16)
dev = get_usb(vid, pid)
dev = get_usb(parse_usb_addr(args.port))
dev.default_timeout = int(args.timeout)
fast = fb.Fastboot(dev)
# this is mostly there to dodge a linter error
Expand Down
44 changes: 0 additions & 44 deletions src/snagflash/utils.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/snagrecover/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def cli():
optional.add_argument("--netns", help="network namespace for AM335x USB recovery, defaults to 'snagbootnet'", default="snagbootnet")
optional.add_argument("--loglevel", help="set loglevel", choices=["silent","info","debug"], default="silent")
optional.add_argument("--logfile", help="set logfile", default="board_recovery.log")
optional.add_argument("--rom-usb", help="USB ID used by ROM code", metavar="vid:pid")
optional.add_argument("--rom-usb", help="USB ID used by ROM code", metavar="vid:pid|bus-port1.port2.[...]")
utilargs = parser.add_argument_group("Utilities")
utilargs.add_argument("--list-socs", help="list supported socs", action="store_true")
utilargs.add_argument("--version", help="show version", action="store_true")
Expand Down
4 changes: 2 additions & 2 deletions src/snagrecover/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import yaml
from snagrecover.utils import cli_error,parse_usb
from snagrecover.utils import cli_error, parse_usb_addr
import logging
logger = logging.getLogger("snagrecover")
import os
Expand Down Expand Up @@ -83,7 +83,7 @@ def init_config(args: list):
else:
recovery_config["rom_usb"] = default_usb_ids[soc_family]
else:
recovery_config["rom_usb"] = parse_usb(args.rom_usb)
recovery_config["rom_usb"] = parse_usb_addr(args.rom_usb)

fw_configs = {}
if args.firmware:
Expand Down
12 changes: 6 additions & 6 deletions src/snagrecover/recoveries/am62x.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
import logging
logger = logging.getLogger("snagrecover")
from snagrecover.firmware.firmware import run_firmware
from snagrecover.utils import get_usb,parse_usb
from snagrecover.utils import parse_usb_addr, get_usb
from snagrecover.config import recovery_config
import time

def main():
usb_vid = recovery_config["rom_usb"][0]
usb_pid = recovery_config["rom_usb"][1]
dev = get_usb(usb_vid, usb_pid)
usb_addr = recovery_config["rom_usb"]
dev = get_usb(usb_addr)
run_firmware(dev, "tiboot3")
# USB device should re-enumerate at this point
usb.util.dispose_resources(dev)
# without this delay, USB device will be present but not ready
time.sleep(1)
if "usb" in recovery_config["firmware"]["tiboot3"]:
(usb_vid,usb_pid) = parse_usb(recovery_config["firmware"]["tiboot3"]["usb"])
dev = get_usb(usb_vid, usb_pid)
usb_addr = parse_usb_addr(recovery_config["firmware"]["tiboot3"]["usb"])

dev = get_usb(usb_addr)
run_firmware(dev, "u-boot")
run_firmware(dev, "tispl")

34 changes: 23 additions & 11 deletions src/snagrecover/recoveries/sama5.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

import serial
import os.path
import glob
import time

from snagrecover.protocols import sambamon
from snagrecover.protocols import memory_ops
from snagrecover.firmware.firmware import run_firmware
from snagrecover.config import recovery_config
from snagrecover.utils import access_error,get_usb
from snagrecover.utils import get_usb, prettify_usb_addr
import logging

logger = logging.getLogger("snagrecover")
Expand Down Expand Up @@ -77,21 +77,33 @@ def check_id(memops: memory_ops.MemoryOps) -> bool:
check &= exid in cfg["EXID_VAL"]
return check

def get_serial_port_path(dev) -> str:
cfg = dev.get_active_configuration()
pretty_path = prettify_usb_addr((dev.bus, dev.port_numbers))
if cfg.bNumInterfaces == 0:
raise ValueError(f"Error: usb device at {pretty_path} has no active interfaces")
intf = cfg.interfaces()[0]
intf_path = "/sys/bus/usb/devices/"\
+ f"{pretty_path}:{cfg.bConfigurationValue}.{intf.bInterfaceNumber}"
tty_paths = glob.glob(intf_path + "/tty/tty*")
if len(tty_paths) == 0:
raise ValueError(f"Error: no tty devices were found at {intf_path}")

tty_path = tty_paths[0]
return f"/dev/{os.path.basename(tty_path)}"

def main():
# CONNECT TO SAM-BA MONITOR
print("Connecting to SAM-BA monitor...")
soc_model = recovery_config["soc_model"]
usb_vid = recovery_config["rom_usb"][0]
usb_pid = recovery_config["rom_usb"][1]
dev = get_usb(usb_vid, usb_pid)
dev.reset()# SAM-BA monitor needs a reset sometimes

port_path = f"/dev/serial/by-id/usb-{usb_vid:04x}_{usb_pid:04x}-if00"
t0 = time.time()
while not os.path.exists(port_path):
if time.time() - t0 > SERIAL_PORT_TIMEOUT:
access_error("serial port", f"{port_path}")
dev = get_usb(recovery_config["rom_usb"])

# SAM-BA monitor needs a reset sometimes
dev.reset()
time.sleep(1)

port_path = get_serial_port_path(dev)
with serial.Serial(os.path.realpath(port_path), baudrate=115200, timeout=5, write_timeout=5) as port:
monitor = sambamon.SambaMon(port)
memops = memory_ops.MemoryOps(monitor)
Expand Down
15 changes: 7 additions & 8 deletions src/snagrecover/recoveries/stm32mp1.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,17 @@
from snagrecover.recoveries import stm32_flashlayout as flashlayout
from snagrecover.firmware.firmware import run_firmware
from snagrecover.config import recovery_config
from snagrecover.utils import parse_usb,get_usb
from snagrecover.utils import parse_usb_addr, get_usb
import logging
logger = logging.getLogger("snagrecover")

USB_TIMEOUT = 10

###################### main ##################################

def main():
soc_model = recovery_config["soc_model"]
# USB ENUMERATION
usb_vid = recovery_config["rom_usb"][0]
usb_pid = recovery_config["rom_usb"][1]
dev = get_usb(usb_vid, usb_pid)
usb_addr = recovery_config["rom_usb"]
dev = get_usb(usb_addr)
cfg = dev.get_active_configuration()
logger.debug("USB config:")
for line in str(cfg).splitlines():
Expand All @@ -53,6 +50,8 @@ def main():
dfu_cmd.detach(phase_id)

# DOWNLOAD FLASH LAYOUT TO BEGINNING OF RAM
# Configuration of the flashing phase is out of scope for
# Snagrecover so we only download a dummy flash layout
if soc_model == "stm32mp15":
phase_id = dfu_cmd.stm32_get_phase()
part0 = dfu.search_partid(dev, "@Partition0", match_prefix=True)
Expand All @@ -68,15 +67,15 @@ def main():
time.sleep(1.5)
# We need to reset here, in the case where TF-A uses a different USB ID
if "usb" in recovery_config["firmware"]["tf-a"]:
(usb_vid,usb_pid) = parse_usb(recovery_config["firmware"]["tf-a"]["usb"])
usb_addr = parse_usb_addr(recovery_config["firmware"]["tf-a"]["usb"])
try:
dev.reset()
except usb.core.USBError:
# this should actually fail
pass
time.sleep(0.5)
usb.util.dispose_resources(dev)
dev = get_usb(usb_vid, usb_pid)
dev = get_usb(usb_addr)
cfg = dev.get_active_configuration()
dfu_cmd = dfu.DFU(dev)
run_firmware(dev, "fip")
Expand Down
26 changes: 18 additions & 8 deletions src/snagrecover/recoveries/sunxi.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,34 @@

import usb
import time
import functools
from snagrecover.protocols import fel
from snagrecover.firmware.firmware import run_firmware
from snagrecover.config import recovery_config
from snagrecover.utils import access_error
from snagrecover.utils import access_error, prettify_usb_addr, is_usb_path

USB_TIMEOUT = 5000
USB_RETRY = 5

def main():
# Try to reset device
usb_vid = recovery_config["rom_usb"][0]
usb_pid = recovery_config["rom_usb"][1]
# FEL devices seem to require a slightly special retry procedure
usb_addr = recovery_config["rom_usb"]
if is_usb_path(usb_addr):
find_usb = functools.partial(usb.core.find,
bus=usb_addr[0],
port_numbers=usb_addr[1])
else:
find_usb = functools.partial(usb.core.find,
idVendor=usb_addr[0],
idProduct=usb_addr[1])

# FEL devices seem to require a slightly special retry procedure, which
# is why we don't just call get_usb from utils.
for i in range(USB_RETRY):
dev = usb.core.find(idVendor=usb_vid, idProduct=usb_pid)
dev = find_usb()
if dev is None:
if i == USB_RETRY - 1:
access_error("USB FEL", f"{usb_vid:04x}:{usb_pid:04x}")
access_error("USB FEL", prettify_usb_addr(usb_addr))
print("Failed to find device, retrying...")
continue
try:
Expand All @@ -51,10 +61,10 @@ def main():

# Try to set device configuration
for i in range(USB_RETRY):
dev = usb.core.find(idVendor=usb_vid, idProduct=usb_pid)
dev = find_usb()
if dev is None:
if i == USB_RETRY - 1:
access_error("USB FEL", f"{usb_vid:04x}:{usb_pid:04x}")
access_error("USB FEL", prettify_usb_addr(usb_addr))
print("Failed to find device, retrying...")
continue
try:
Expand Down

0 comments on commit 2980889

Please sign in to comment.