This commit is contained in:
Willy-JL
2023-08-27 22:14:33 +02:00
229 changed files with 1352 additions and 894 deletions
+7 -1
View File
@@ -2,9 +2,15 @@ import os
import re
from dataclasses import dataclass, field
from enum import Enum
from fbt.util import resolve_real_dir_node
from typing import Callable, ClassVar, List, Optional, Tuple, Union
try:
from fbt.util import resolve_real_dir_node
except ImportError:
# When running outside of SCons, we don't have access to SCons.Node
def resolve_real_dir_node(node):
return node
class FlipperManifestException(Exception):
pass
+1
View File
@@ -237,6 +237,7 @@ class SdkCache:
removed_entries = known_set - new_set
if removed_entries:
print(f"Removed: {removed_entries}")
self.loaded_dirty_version = True
known_set -= removed_entries
# If any of removed entries was a part of active API, that's a major bump
if update_version and any(
+2 -2
View File
@@ -38,7 +38,7 @@ from SCons.Tool.cxx import CXXSuffixes
from SCons.Tool.cc import CSuffixes
from SCons.Tool.asm import ASSuffixes, ASPPSuffixes
# TODO: Is there a better way to do this than this global? Right now this exists so that the
# TODO FL-3542: Is there a better way to do this than this global? Right now this exists so that the
# emitter we add can record all of the things it emits, so that the scanner for the top level
# compilation database can access the complete list, and also so that the writer has easy
# access to write all of the files. But it seems clunky. How can the emitter and the scanner
@@ -91,7 +91,7 @@ def make_emit_compilation_DB_entry(comstr):
__COMPILATIONDB_ENV=env,
)
# TODO: Technically, these next two lines should not be required: it should be fine to
# TODO FL-3541: Technically, these next two lines should not be required: it should be fine to
# cache the entries. However, they don't seem to update properly. Since they are quick
# to re-generate disable caching and sidestep this problem.
env.AlwaysBuild(entry)
+3 -1
View File
@@ -2,6 +2,8 @@ import subprocess
import gdb
import objdump
import shutil
import strip
from SCons.Action import _subproc
from SCons.Errors import StopError
@@ -11,7 +13,7 @@ from SCons.Tool import ar, asm, gcc, gnulink, gxx
def prefix_commands(env, command_prefix, cmd_list):
for command in cmd_list:
if command in env:
env[command] = command_prefix + env[command]
env[command] = shutil.which(command_prefix + env[command])
def _get_tool_version(env, tool):
+1 -1
View File
@@ -21,7 +21,7 @@ def generate(env, **kw):
FBT_DEBUG_DIR="${FBT_SCRIPT_DIR}/debug",
)
if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto":
if (adapter_serial := env.subst("$SWD_TRANSPORT_SERIAL")) != "auto":
env.Append(
OPENOCD_OPTS=[
"-c",
+42 -25
View File
@@ -52,22 +52,16 @@ def AddFwProject(env, base_env, fw_type, fw_env_key):
return project_env
def AddOpenOCDFlashTarget(env, targetenv, **kw):
openocd_target = env.OpenOCDFlash(
"#build/oocd-${BUILD_CFG}-flash.flag",
targetenv["FW_BIN"],
OPENOCD_COMMAND=[
"-c",
"program ${SOURCE.posix} reset exit ${BASE_ADDRESS}",
],
BUILD_CFG=targetenv.subst("$FIRMWARE_BUILD_CFG"),
BASE_ADDRESS=targetenv.subst("$IMAGE_BASE_ADDRESS"),
def AddFwFlashTarget(env, targetenv, **kw):
fwflash_target = env.FwFlash(
"#build/flash.flag",
targetenv["FW_ELF"],
**kw,
)
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target)
env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), fwflash_target)
if env["FORCE"]:
env.AlwaysBuild(openocd_target)
return openocd_target
env.AlwaysBuild(fwflash_target)
return fwflash_target
def AddJFlashTarget(env, targetenv, **kw):
@@ -115,7 +109,7 @@ def generate(env):
env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}")
env.AddMethod(AddFwProject)
env.AddMethod(DistCommand)
env.AddMethod(AddOpenOCDFlashTarget)
env.AddMethod(AddFwFlashTarget)
env.AddMethod(GetProjectDirName)
env.AddMethod(AddJFlashTarget)
env.AddMethod(AddUsbFlashTarget)
@@ -125,30 +119,53 @@ def generate(env):
SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py",
DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py",
COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py",
FW_FLASH_SCRIPT="${FBT_SCRIPT_DIR}/fwflash.py",
)
env.Append(
BUILDERS={
"FwFlash": Builder(
action=[
[
"${PYTHON3}",
"${FW_FLASH_SCRIPT}",
"-d" if env["VERBOSE"] else "",
"--interface=${SWD_TRANSPORT}",
"--serial=${SWD_TRANSPORT_SERIAL}",
"${SOURCE}",
],
Touch("${TARGET}"),
]
),
"UsbInstall": Builder(
action=[
Action(
'${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf'
),
[
"${PYTHON3}",
"${SELFUPDATE_SCRIPT}",
"-p",
"${FLIP_PORT}",
"${UPDATE_BUNDLE_DIR}/update.fuf",
],
Touch("${TARGET}"),
]
),
"CoproBuilder": Builder(
action=Action(
[
'${PYTHON3} "${COPRO_ASSETS_SCRIPT}" '
"copro ${COPRO_CUBE_DIR} "
"${TARGET} ${COPRO_MCU_FAMILY} "
"--cube_ver=${COPRO_CUBE_VERSION} "
"--stack_type=${COPRO_STACK_TYPE} "
'--stack_file="${COPRO_STACK_BIN}" '
"--stack_addr=${COPRO_STACK_ADDR} ",
[
"${PYTHON3}",
"${COPRO_ASSETS_SCRIPT}",
"copro",
"${COPRO_CUBE_DIR}",
"${TARGET}",
"${COPRO_MCU_FAMILY}",
"--cube_ver=${COPRO_CUBE_VERSION}",
"--stack_type=${COPRO_STACK_TYPE}",
"--stack_file=${COPRO_STACK_BIN}",
"--stack_addr=${COPRO_STACK_ADDR}",
]
],
"$COPROCOMSTR",
"${COPROCOMSTR}",
)
),
}
+5
View File
@@ -53,6 +53,11 @@ class AppBuilder:
FAP_SRC_DIR=self.app._appdir,
FAP_WORK_DIR=self.app_work_dir,
)
self.app_env.Append(
CPPDEFINES=[
("FAP_VERSION", f'"{".".join(map(str, self.app.fap_version))}"')
],
)
self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False)
def _build_external_files(self):
+2 -2
View File
@@ -16,8 +16,8 @@ Firmware & apps:
Flashing & debugging:
flash, flash_blackmagic, jflash:
Flash firmware to target using debug probe
flash, jflash:
Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL
flash_usb, flash_usb_full:
Install firmware using self-update package
debug, debug_other, blackmagic:
+1 -2
View File
@@ -15,10 +15,9 @@ class App:
# Application specific initialization
self.init()
def __call__(self, args=None, skip_logger_init=False):
def __call__(self, args=None):
self.args, self.other_args = self.parser.parse_known_args(args=args)
# configure log output
# if skip_logger_init:
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
self.logger.setLevel(self.log_level)
if not self.logger.hasHandlers():
+2 -2
View File
@@ -150,7 +150,7 @@ class FlipperStorage:
for line in lines:
try:
# TODO: better decoding, considering non-ascii characters
# TODO FL-3539: better decoding, considering non-ascii characters
line = line.decode("ascii")
except Exception:
continue
@@ -193,7 +193,7 @@ class FlipperStorage:
for line in lines:
try:
# TODO: better decoding, considering non-ascii characters
# TODO FL-3539: better decoding, considering non-ascii characters
line = line.decode("ascii")
except Exception:
continue
+2 -2
View File
@@ -78,7 +78,7 @@ class OpenOCD:
def _wait_for_openocd_tcl(self):
"""Wait for OpenOCD to start"""
# TODO: timeout
# TODO Fl-3538: timeout
while True:
stderr = self.process.stderr
if not stderr:
@@ -128,7 +128,7 @@ class OpenOCD:
def _recv(self):
"""Read from the stream until the token (\x1a) was received."""
# TODO: timeout
# TODO FL-3538: timeout
data = bytes()
while True:
chunk = self.socket.recv(4096)
+1 -1
View File
@@ -247,7 +247,7 @@ class STM32WB55:
def flash_wait_for_operation(self):
# Wait for flash operation to complete
# TODO: timeout
# TODO FL-3537: timeout
while True:
self.FLASH_SR.load()
if self.FLASH_SR.BSY == 0:
+186 -152
View File
@@ -1,19 +1,24 @@
#!/usr/bin/env python3
import logging
import os
import re
import socket
import subprocess
import time
import typing
from abc import ABC, abstractmethod
from dataclasses import dataclass
from dataclasses import dataclass, field
from flipper.app import App
# When adding an interface, also add it to SWD_TRANSPORT in fbt/ufbt options
class Programmer(ABC):
root_logger = logging.getLogger("Programmer")
@abstractmethod
def flash(self, bin: str) -> bool:
def flash(self, file_path: str, do_verify: bool) -> bool:
pass
@abstractmethod
@@ -28,28 +33,46 @@ class Programmer(ABC):
def set_serial(self, serial: str):
pass
@classmethod
def _spawn_and_await(cls, process_params, show_progress: bool = False):
cls.root_logger.debug(f"Launching: {' '.join(process_params)}")
process = subprocess.Popen(
process_params,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
if show_progress:
while process.poll() is None:
time.sleep(0.25)
print(".", end="", flush=True)
print()
else:
process.wait()
return process
@dataclass
class OpenOCDInterface:
name: str
file: str
config_file: str
serial_cmd: str
additional_args: typing.Optional[list[str]] = None
additional_args: typing.Optional[list[str]] = field(default_factory=list)
class OpenOCDProgrammer(Programmer):
def __init__(self, interface: OpenOCDInterface):
self.interface = interface
self.logger = logging.getLogger("OpenOCD")
self.logger = self.root_logger.getChild("OpenOCD")
self.serial: typing.Optional[str] = None
def _add_file(self, params: list[str], file: str):
params.append("-f")
params.append(file)
params += ["-f", file]
def _add_command(self, params: list[str], command: str):
params.append("-c")
params.append(command)
params += ["-c", command]
def _add_serial(self, params: list[str], serial: str):
self._add_command(params, f"{self.interface.serial_cmd} {serial}")
@@ -57,22 +80,27 @@ class OpenOCDProgrammer(Programmer):
def set_serial(self, serial: str):
self.serial = serial
def flash(self, bin: str) -> bool:
i = self.interface
def flash(self, file_path: str, do_verify: bool) -> bool:
if os.altsep:
bin = bin.replace(os.sep, os.altsep)
file_path = file_path.replace(os.sep, os.altsep)
openocd_launch_params = ["openocd"]
self._add_file(openocd_launch_params, i.file)
self._add_file(openocd_launch_params, self.interface.config_file)
if self.serial:
self._add_serial(openocd_launch_params, self.serial)
if i.additional_args:
for a in i.additional_args:
self._add_command(openocd_launch_params, a)
for additional_arg in self.interface.additional_args:
self._add_command(openocd_launch_params, additional_arg)
self._add_file(openocd_launch_params, "target/stm32wbx.cfg")
self._add_command(openocd_launch_params, "init")
self._add_command(openocd_launch_params, f"program {bin} reset exit 0x8000000")
program_params = [
"program",
f'"{file_path}"',
"verify" if do_verify else "",
"reset",
"exit",
"0x8000000" if file_path.endswith(".bin") else "",
]
self._add_command(openocd_launch_params, " ".join(program_params))
# join the list of parameters into a string, but add quote if there are spaces
openocd_launch_params_string = " ".join(
@@ -81,17 +109,7 @@ class OpenOCDProgrammer(Programmer):
self.logger.debug(f"Launching: {openocd_launch_params_string}")
process = subprocess.Popen(
openocd_launch_params,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
while process.poll() is None:
time.sleep(0.25)
print(".", end="", flush=True)
print()
process = self._spawn_and_await(openocd_launch_params, True)
success = process.returncode == 0
if not success:
@@ -102,35 +120,41 @@ class OpenOCDProgrammer(Programmer):
return success
def probe(self) -> bool:
i = self.interface
openocd_launch_params = ["openocd"]
self._add_file(openocd_launch_params, i.file)
self._add_file(openocd_launch_params, self.interface.config_file)
if self.serial:
self._add_serial(openocd_launch_params, self.serial)
if i.additional_args:
for a in i.additional_args:
self._add_command(openocd_launch_params, a)
for additional_arg in self.interface.additional_args:
self._add_command(openocd_launch_params, additional_arg)
self._add_file(openocd_launch_params, "target/stm32wbx.cfg")
self._add_command(openocd_launch_params, "init")
self._add_command(openocd_launch_params, "exit")
self.logger.debug(f"Launching: {' '.join(openocd_launch_params)}")
process = self._spawn_and_await(openocd_launch_params)
success = process.returncode == 0
process = subprocess.Popen(
openocd_launch_params,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
)
output = process.stdout.read().decode("utf-8").strip() if process.stdout else ""
self.logger.debug(output)
# Find target voltage using regex
if match := re.search(r"Target voltage: (\d+\.\d+)", output):
voltage = float(match.group(1))
if not success:
if voltage < 1:
self.logger.warning(
f"Found {self.get_name()}, but device is not connected"
)
else:
self.logger.warning(
f"Device is connected, but {self.get_name()} failed to attach. Is System>Debug enabled?"
)
# Wait for OpenOCD to end and get the return code
process.wait()
found = process.returncode == 0
if "cannot read IDR" in output:
self.logger.warning(
f"Found {self.get_name()}, but failed to attach. Is device connected and is System>Debug enabled?"
)
success = False
if process.stdout:
self.logger.debug(process.stdout.read().decode("utf-8").strip())
return found
return success
def get_name(self) -> str:
return self.interface.name
@@ -187,7 +211,7 @@ def _resolve_hostname(hostname):
def blackmagic_find_networked(serial: str):
if not serial:
if not serial or serial == "auto":
serial = "blackmagic.local"
# remove the tcp: prefix if it's there
@@ -212,7 +236,7 @@ class BlackmagicProgrammer(Programmer):
):
self.port_resolver = port_resolver
self.name = name
self.logger = logging.getLogger("BlackmagicUSB")
self.logger = self.root_logger.getChild(f"Blackmagic{name}")
self.port: typing.Optional[str] = None
def _add_command(self, params: list[str], command: str):
@@ -234,7 +258,15 @@ class BlackmagicProgrammer(Programmer):
else:
self.port = serial
def flash(self, bin: str) -> bool:
def _get_gdb_core_params(self) -> list[str]:
gdb_launch_params = ["arm-none-eabi-gdb"]
self._add_command(gdb_launch_params, f"target extended-remote {self.port}")
self._add_command(gdb_launch_params, "set pagination off")
self._add_command(gdb_launch_params, "set confirm off")
self._add_command(gdb_launch_params, "monitor swdp_scan")
return gdb_launch_params
def flash(self, file_path: str, do_verify: bool) -> bool:
if not self.port:
if not self.probe():
return False
@@ -242,12 +274,14 @@ class BlackmagicProgrammer(Programmer):
# We can convert .bin to .elf with objcopy:
# arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf
# But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor.
elf = bin.replace(".bin", ".elf")
if not os.path.exists(elf):
self.logger.error(
f"Sorry, but Blackmagic can't flash .bin file, and {elf} doesn't exist"
)
return False
if file_path.endswith(".bin"):
file_path = file_path[:-4] + ".elf"
if not os.path.exists(file_path):
self.logger.error(
f"Sorry, but Blackmagic can't flash .bin file, and {file_path} doesn't exist"
)
return False
# arm-none-eabi-gdb build/f7-firmware-D/firmware.bin
# -ex 'set pagination off'
@@ -260,42 +294,25 @@ class BlackmagicProgrammer(Programmer):
# -ex 'compare-sections'
# -ex 'quit'
gdb_launch_params = ["arm-none-eabi-gdb", elf]
self._add_command(gdb_launch_params, f"target extended-remote {self.port}")
self._add_command(gdb_launch_params, "set pagination off")
self._add_command(gdb_launch_params, "set confirm off")
self._add_command(gdb_launch_params, "monitor swdp_scan")
gdb_launch_params = self._get_gdb_core_params()
self._add_command(gdb_launch_params, "attach 1")
self._add_command(gdb_launch_params, "set mem inaccessible-by-default off")
self._add_command(gdb_launch_params, "load")
self._add_command(gdb_launch_params, "compare-sections")
if do_verify:
self._add_command(gdb_launch_params, "compare-sections")
self._add_command(gdb_launch_params, "quit")
gdb_launch_params.append(file_path)
self.logger.debug(f"Launching: {' '.join(gdb_launch_params)}")
process = subprocess.Popen(
gdb_launch_params,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
while process.poll() is None:
time.sleep(0.5)
print(".", end="", flush=True)
print()
process = self._spawn_and_await(gdb_launch_params, True)
if not process.stdout:
return False
output = process.stdout.read().decode("utf-8").strip()
flashed = "Loading section .text," in output
# Check flash verification
if "MIS-MATCHED!" in output:
flashed = False
if "target image does not match the loaded file" in output:
flashed = False
flashed = (
"Loading section .text," in output
and "MIS-MATCHED!" not in output
and "target image does not match the loaded file" not in output
)
if not flashed:
self.logger.error("Blackmagic failed to flash")
@@ -308,13 +325,29 @@ class BlackmagicProgrammer(Programmer):
return False
self.port = port
gdb_launch_params = self._get_gdb_core_params()
self._add_command(gdb_launch_params, "quit")
process = self._spawn_and_await(gdb_launch_params)
if not process.stdout or process.returncode != 0:
return False
output = process.stdout.read().decode("utf-8").strip()
if "SW-DP scan failed!" in output:
self.logger.warning(
f"Found {self.get_name()} at {self.port}, but failed to attach. Is device connected and is System>Debug enabled?"
)
return False
return True
def get_name(self) -> str:
return self.name
programmers: list[Programmer] = [
####################
local_flash_interfaces: list[Programmer] = [
OpenOCDProgrammer(
OpenOCDInterface(
"cmsis-dap",
@@ -325,47 +358,66 @@ programmers: list[Programmer] = [
),
OpenOCDProgrammer(
OpenOCDInterface(
"stlink", "interface/stlink.cfg", "hla_serial", ["transport select hla_swd"]
"stlink",
"interface/stlink.cfg",
"hla_serial",
["transport select hla_swd"],
),
),
BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"),
]
network_programmers = [
network_flash_interfaces: list[Programmer] = [
BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi")
]
all_flash_interfaces = [*local_flash_interfaces, *network_flash_interfaces]
####################
class Main(App):
AUTO_INTERFACE = "auto"
def init(self):
self.subparsers = self.parser.add_subparsers(help="sub-command help")
self.parser_flash = self.subparsers.add_parser("flash", help="Flash a binary")
self.parser_flash.add_argument(
"bin",
Programmer.root_logger = self.logger
self.parser.add_argument(
"filename",
type=str,
help="Binary to flash",
help="File to flash",
)
interfaces = [i.get_name() for i in programmers]
interfaces.extend([i.get_name() for i in network_programmers])
self.parser_flash.add_argument(
self.parser.add_argument(
"--verify",
"-v",
action="store_true",
help="Verify flash after programming",
default=False,
)
self.parser.add_argument(
"--interface",
choices=interfaces,
choices=(
self.AUTO_INTERFACE,
*[i.get_name() for i in all_flash_interfaces],
),
type=str,
default=self.AUTO_INTERFACE,
help="Interface to use",
)
self.parser_flash.add_argument(
self.parser.add_argument(
"--serial",
type=str,
default=self.AUTO_INTERFACE,
help="Serial number or port of the programmer",
)
self.parser_flash.set_defaults(func=self.flash)
self.parser.set_defaults(func=self.flash)
def _search_interface(self, serial: typing.Optional[str]) -> list[Programmer]:
def _search_interface(self, interface_list: list[Programmer]) -> list[Programmer]:
found_programmers = []
for p in programmers:
for p in interface_list:
name = p.get_name()
if serial:
if (serial := self.args.serial) != self.AUTO_INTERFACE:
p.set_serial(serial)
self.logger.debug(f"Trying {name} with {serial}")
else:
@@ -373,29 +425,7 @@ class Main(App):
if p.probe():
self.logger.debug(f"Found {name}")
found_programmers += [p]
else:
self.logger.debug(f"Failed to probe {name}")
return found_programmers
def _search_network_interface(
self, serial: typing.Optional[str]
) -> list[Programmer]:
found_programmers = []
for p in network_programmers:
name = p.get_name()
if serial:
p.set_serial(serial)
self.logger.debug(f"Trying {name} with {serial}")
else:
self.logger.debug(f"Trying {name}")
if p.probe():
self.logger.debug(f"Found {name}")
found_programmers += [p]
found_programmers.append(p)
else:
self.logger.debug(f"Failed to probe {name}")
@@ -403,55 +433,59 @@ class Main(App):
def flash(self):
start_time = time.time()
bin_path = os.path.abspath(self.args.bin)
file_path = os.path.abspath(self.args.filename)
if not os.path.exists(bin_path):
self.logger.error(f"Binary file not found: {bin_path}")
if not os.path.exists(file_path):
self.logger.error(f"Binary file not found: {file_path}")
return 1
if self.args.interface:
i_name = self.args.interface
interfaces = [p for p in programmers if p.get_name() == i_name]
if len(interfaces) == 0:
interfaces = [p for p in network_programmers if p.get_name() == i_name]
else:
self.logger.info("Probing for interfaces...")
interfaces = self._search_interface(self.args.serial)
if self.args.interface != self.AUTO_INTERFACE:
available_interfaces = list(
filter(
lambda p: p.get_name() == self.args.interface,
all_flash_interfaces,
)
)
if len(interfaces) == 0:
else:
self.logger.info("Probing for local interfaces...")
available_interfaces = self._search_interface(local_flash_interfaces)
if not available_interfaces:
# Probe network blackmagic
self.logger.info("Probing for network interfaces...")
interfaces = self._search_network_interface(self.args.serial)
available_interfaces = self._search_interface(network_flash_interfaces)
if len(interfaces) == 0:
self.logger.error("No interface found")
if not available_interfaces:
self.logger.error("No availiable interfaces")
return 1
if len(interfaces) > 1:
self.logger.error("Multiple interfaces found: ")
elif len(available_interfaces) > 1:
self.logger.error("Multiple interfaces found:")
self.logger.error(
f"Please specify '--interface={[i.get_name() for i in interfaces]}'"
f"Please specify '--interface={[i.get_name() for i in available_interfaces]}'"
)
return 1
interface = interfaces[0]
interface = available_interfaces.pop(0)
if self.args.serial:
if self.args.serial != self.AUTO_INTERFACE:
interface.set_serial(self.args.serial)
self.logger.info(
f"Flashing {bin_path} via {interface.get_name()} with {self.args.serial}"
)
self.logger.info(f"Using {interface.get_name()} with {self.args.serial}")
else:
self.logger.info(f"Flashing {bin_path} via {interface.get_name()}")
self.logger.info(f"Using {interface.get_name()}")
self.logger.info(f"Flashing {file_path}")
if not interface.flash(bin_path):
if not interface.flash(file_path, self.args.verify):
self.logger.error(f"Failed to flash via {interface.get_name()}")
return 1
flash_time = time.time() - start_time
bin_size = os.path.getsize(bin_path)
self.logger.info(f"Flashed successfully in {flash_time:.2f}s")
self.logger.info(f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s")
if file_path.endswith(".bin"):
bin_size = os.path.getsize(file_path)
self.logger.info(
f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s"
)
return 0
+23 -8
View File
@@ -1,5 +1,8 @@
#!/usr/bin/env python3
import time
from typing import Optional
from flipper.app import App
from flipper.storage import FlipperStorage
from flipper.utils.cdc import resolve_port
@@ -27,8 +30,20 @@ class Main(App):
)
self.parser_reboot2dfu.set_defaults(func=self.reboot2dfu)
def _get_flipper(self):
if not (port := resolve_port(self.logger, self.args.port)):
def _get_flipper(self, retry_count: Optional[int] = 1):
port = None
self.logger.info(f"Attempting to find flipper with {retry_count} attempts.")
for i in range(retry_count):
time.sleep(1)
self.logger.info(f"Attempting to find flipper #{i}.")
if port := resolve_port(self.logger, self.args.port):
self.logger.info(f"Found flipper at {port}")
break
if not port:
self.logger.info(f"Failed to find flipper")
return None
flipper = FlipperStorage(port)
@@ -36,28 +51,28 @@ class Main(App):
return flipper
def power_off(self):
if not (flipper := self._get_flipper()):
if not (flipper := self._get_flipper(retry_count=10)):
return 1
self.logger.debug("Powering off")
self.logger.info("Powering off")
flipper.send("power off" + "\r")
flipper.stop()
return 0
def reboot(self):
if not (flipper := self._get_flipper()):
if not (flipper := self._get_flipper(retry_count=10)):
return 1
self.logger.debug("Rebooting")
self.logger.info("Rebooting")
flipper.send("power reboot" + "\r")
flipper.stop()
return 0
def reboot2dfu(self):
if not (flipper := self._get_flipper()):
if not (flipper := self._get_flipper(retry_count=10)):
return 1
self.logger.debug("Rebooting to DFU")
self.logger.info("Rebooting to DFU")
flipper.send("power reboot2dfu" + "\r")
flipper.stop()
+3 -3
View File
@@ -22,14 +22,14 @@ def flp_serial_by_name(flp_name):
if os.path.exists(flp_serial):
return flp_serial
else:
logging.info(f"Couldn't find {logging.info} on this attempt.")
logging.info(f"Couldn't find {flp_name} on this attempt.")
if os.path.exists(flp_name):
return flp_name
else:
return ""
UPDATE_TIMEOUT = 60 * 4 # 4 minutes
UPDATE_TIMEOUT = 30 * 4 # 4 minutes
def main():
@@ -50,7 +50,7 @@ def main():
if flipper == "":
logging.error("Flipper not found!")
sys.exit(1)
exit(1)
logging.info(f"Found Flipper at {flipper}")
+1 -3
View File
@@ -20,14 +20,12 @@ def main():
logging.error("Flipper not found!")
sys.exit(1)
with serial.Serial(flp_serial, timeout=10) as flipper:
with serial.Serial(flp_serial, timeout=150) as flipper:
logging.info(f"Found Flipper at {flp_serial}")
flipper.baudrate = 230400
flipper.flushOutput()
flipper.flushInput()
flipper.timeout = 300
flipper.read_until(b">: ").decode("utf-8")
flipper.write(b"unit_tests\r")
data = flipper.read_until(b">: ").decode("utf-8")
@@ -1,7 +1,7 @@
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
# TODO: fix
# TODO FL-3536: fix path to download_dir
$download_dir = (Get-Item "$PSScriptRoot\..\..").FullName
$toolchain_version = $args[0]
$toolchain_target_path = $args[1]
+6 -25
View File
@@ -144,24 +144,20 @@ dist_env = env.Clone(
],
)
openocd_target = dist_env.OpenOCDFlash(
flash_target = dist_env.FwFlash(
dist_env["UFBT_STATE_DIR"].File("flash"),
dist_env["FW_BIN"],
OPENOCD_COMMAND=[
"-c",
"program ${SOURCE.posix} reset exit 0x08000000",
],
dist_env["FW_ELF"],
)
dist_env.Alias("firmware_flash", openocd_target)
dist_env.Alias("flash", openocd_target)
dist_env.Alias("firmware_flash", flash_target)
dist_env.Alias("flash", flash_target)
if env["FORCE"]:
env.AlwaysBuild(openocd_target)
env.AlwaysBuild(flash_target)
firmware_jflash = dist_env.JFlash(
dist_env["UFBT_STATE_DIR"].File("jflash"),
dist_env["FW_BIN"],
JFLASHADDR="0x20000000",
JFLASHADDR="0x08000000",
)
dist_env.Alias("firmware_jflash", firmware_jflash)
dist_env.Alias("jflash", firmware_jflash)
@@ -213,21 +209,6 @@ dist_env.PhonyTarget(
GDBPYOPTS=debug_other_opts,
)
dist_env.PhonyTarget(
"flash_blackmagic",
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
source=dist_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBFLASH=[
"-ex",
"load",
"-ex",
"quit",
],
)
flash_usb_full = dist_env.UsbInstall(
dist_env["UFBT_STATE_DIR"].File("usbinstall"),
[],
+14 -2
View File
@@ -55,9 +55,21 @@ vars.AddVariables(
"Blackmagic probe location",
"auto",
),
EnumVariable(
"SWD_TRANSPORT",
help="SWD interface adapter type",
default="auto",
allowed_values=[
"auto",
"cmsis-dap",
"stlink",
"blackmagic_usb",
"blackmagic_wifi",
],
),
(
"OPENOCD_ADAPTER_SERIAL",
"OpenOCD adapter serial number",
"SWD_TRANSPORT_SERIAL",
"SWD interface adapter serial number",
"auto",
),
(
+2 -2
View File
@@ -20,8 +20,8 @@ Building:
Build FAP app with appid={APPID}; upload & start it over USB
Flashing & debugging:
flash, flash_blackmagic, *jflash:
Flash firmware to target using debug probe
flash, *jflash:
Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL
flash_usb, flash_usb_full:
Install firmware using self-update package
debug, debug_other, blackmagic: