Merge branch 'ofw_dev' into dev

This commit is contained in:
MX 2024-03-02 07:24:47 +03:00
commit 88b354b57e
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
8 changed files with 172 additions and 71 deletions

View file

@ -66,6 +66,7 @@ if GetOption("fullenv") or any(
# Target for self-update package # Target for self-update package
dist_basic_arguments = [ dist_basic_arguments = [
"${ARGS}",
"--bundlever", "--bundlever",
"${UPDATE_VERSION_STRING}", "${UPDATE_VERSION_STRING}",
] ]
@ -182,6 +183,7 @@ fap_deploy = distenv.PhonyTarget(
"send", "send",
"${SOURCE}", "${SOURCE}",
"/ext/apps", "/ext/apps",
"${ARGS}",
] ]
] ]
), ),
@ -208,7 +210,7 @@ distenv.Alias("jflash", firmware_jflash)
distenv.PhonyTarget( distenv.PhonyTarget(
"gdb_trace_all", "gdb_trace_all",
"$GDB $GDBOPTS $SOURCES $GDBFLASH", [["${GDB}", "${GDBOPTS}", "${SOURCES}", "${GDBFLASH}"]],
source=firmware_env["FW_ELF"], source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE}", GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}", GDBREMOTE="${OPENOCD_GDB_PIPE}",
@ -272,19 +274,35 @@ distenv.PhonyTarget(
# Just start OpenOCD # Just start OpenOCD
distenv.PhonyTarget( distenv.PhonyTarget(
"openocd", "openocd",
"${OPENOCDCOM}", [["${OPENOCDCOM}", "${ARGS}"]],
) )
# Linter # Linter
distenv.PhonyTarget( distenv.PhonyTarget(
"lint", "lint",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]], [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/lint.py",
"check",
"${LINT_SOURCES}",
"${ARGS}",
]
],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
distenv.PhonyTarget( distenv.PhonyTarget(
"format", "format",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]], [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/lint.py",
"format",
"${LINT_SOURCES}",
"${ARGS}",
]
],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
@ -307,7 +325,16 @@ firmware_env.Append(
) )
black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}" black_commandline = [
[
"@${PYTHON3}",
"-m",
"black",
"${PY_BLACK_ARGS}",
"${PY_LINT_SOURCES}",
"${ARGS}",
]
]
black_base_args = [ black_base_args = [
"--include", "--include",
'"(\\.scons|\\.py|SConscript|SConstruct|\\.fam)$"', '"(\\.scons|\\.py|SConscript|SConstruct|\\.fam)$"',
@ -333,12 +360,28 @@ distenv.PhonyTarget(
# Start Flipper CLI via PySerial's miniterm # Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget( distenv.PhonyTarget(
"cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]] "cli",
[
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/serial_cli.py",
"-p",
"${FLIP_PORT}",
"${ARGS}",
]
],
) )
# Update WiFi devboard firmware # Update WiFi devboard firmware with release channel
distenv.PhonyTarget( distenv.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]] "devboard_flash",
[
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/wifi_board.py",
"${ARGS}",
]
],
) )
@ -353,7 +396,7 @@ distenv.PhonyTarget(
distenv.PhonyTarget( distenv.PhonyTarget(
"get_stlink", "get_stlink",
distenv.Action( distenv.Action(
lambda **kw: distenv.GetDevices(), lambda **_: distenv.GetDevices(),
None, None,
), ),
) )

View file

@ -499,6 +499,8 @@ Canvas* gui_direct_draw_acquire(Gui* gui) {
gui->direct_draw = true; gui->direct_draw = true;
gui_unlock(gui); gui_unlock(gui);
canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
canvas_reset(gui->canvas); canvas_reset(gui->canvas);
canvas_commit(gui->canvas); canvas_commit(gui->canvas);

View file

@ -71,13 +71,13 @@ To use language servers other than the default VS Code C/C++ language server, us
- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded.
- `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. - `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB.
- `updater_debug` - attach GDB with the updater's `.elf` loaded. - `updater_debug` - attach GDB with the updater's `.elf` loaded.
- `devboard_flash` - update WiFi dev board with the latest firmware. - `devboard_flash` - Update WiFi dev board. Supports `ARGS="..."` to pass extra arguments to the update script, e.g. `ARGS="-c dev"`.
- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board).
- `openocd` - just start OpenOCD. - `openocd` - just start OpenOCD. You can pass extra arguments with `ARGS="..."`.
- `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. - `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration.
- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`. - `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`.
- `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. - `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. Supports `ARGS="..."` to pass extra arguments to clang-format.
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. - `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. Supports `ARGS="..."` to pass extra arguments to black.
- `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`. - `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`.
- `cli` - start a Flipper CLI session over USB. - `cli` - start a Flipper CLI session over USB.

View file

@ -150,6 +150,7 @@ def generate(env):
"--interface=${SWD_TRANSPORT}", "--interface=${SWD_TRANSPORT}",
"--serial=${SWD_TRANSPORT_SERIAL}", "--serial=${SWD_TRANSPORT_SERIAL}",
"${SOURCE}", "${SOURCE}",
"${ARGS}",
], ],
Touch("${TARGET}"), Touch("${TARGET}"),
] ]
@ -162,6 +163,7 @@ def generate(env):
"-p", "-p",
"${FLIP_PORT}", "${FLIP_PORT}",
"${UPDATE_BUNDLE_DIR}/update.fuf", "${UPDATE_BUNDLE_DIR}/update.fuf",
"${ARGS}",
], ],
Touch("${TARGET}"), Touch("${TARGET}"),
] ]
@ -180,6 +182,7 @@ def generate(env):
"--stack_type=${COPRO_STACK_TYPE}", "--stack_type=${COPRO_STACK_TYPE}",
"--stack_file=${COPRO_STACK_BIN}", "--stack_file=${COPRO_STACK_BIN}",
"--stack_addr=${COPRO_STACK_ADDR}", "--stack_addr=${COPRO_STACK_ADDR}",
"${ARGS}",
] ]
], ],
"${COPROCOMSTR}", "${COPROCOMSTR}",

View file

@ -315,26 +315,56 @@ else:
appenv.PhonyTarget( appenv.PhonyTarget(
"cli", "cli",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]], [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/serial_cli.py",
"-p",
"${FLIP_PORT}",
"${ARGS}",
]
],
) )
# Update WiFi devboard firmware # Update WiFi devboard firmware
dist_env.PhonyTarget( dist_env.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]] "devboard_flash",
[
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/wifi_board.py",
"${ARGS}",
]
],
) )
# Linter # Linter
dist_env.PhonyTarget( dist_env.PhonyTarget(
"lint", "lint",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]], [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/lint.py",
"check",
"${LINT_SOURCES}",
"${ARGS}",
]
],
source=original_app_dir.File(".clang-format"), source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir], LINT_SOURCES=[original_app_dir],
) )
dist_env.PhonyTarget( dist_env.PhonyTarget(
"format", "format",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]], [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/lint.py",
"format",
"${LINT_SOURCES}",
"${ARGS}",
]
],
source=original_app_dir.File(".clang-format"), source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir], LINT_SOURCES=[original_app_dir],
) )
@ -456,6 +486,7 @@ if dolphin_src_dir.exists():
"send", "send",
"${SOURCE}", "${SOURCE}",
"/ext/dolphin", "/ext/dolphin",
"${ARGS}",
] ]
], ],
source=ufbt_build_dir.Dir("dolphin"), source=ufbt_build_dir.Dir("dolphin"),

View file

@ -29,7 +29,8 @@ Flashing & debugging:
debug, debug_other, blackmagic: debug, debug_other, blackmagic:
Start GDB Start GDB
devboard_flash: devboard_flash:
Update WiFi dev board with the latest firmware Update WiFi dev board.
Supports ARGS="..." to pass extra arguments to the update script, e.g. ARGS="-c dev"
Other: Other:
cli: cli:

View file

@ -1,16 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from flipper.app import App import json
from serial.tools.list_ports_common import ListPortInfo
import logging import logging
import os import os
import tempfile
import subprocess import subprocess
import serial.tools.list_ports as list_ports
import json
import requests
import tarfile import tarfile
import tempfile
import requests
import serial.tools.list_ports as list_ports
from flipper.app import App
from serial.tools.list_ports_common import ListPortInfo
class UpdateDownloader: class UpdateDownloader:
@ -29,15 +29,15 @@ class UpdateDownloader:
def __init__(self): def __init__(self):
self.logger = logging.getLogger() self.logger = logging.getLogger()
def download(self, channel_id: str, dir: str) -> bool: def download(self, channel_id: str, target_dir: str) -> bool:
# Aliases # Aliases
if channel_id in self.CHANNEL_ID_ALIAS: if channel_id in self.CHANNEL_ID_ALIAS:
channel_id = self.CHANNEL_ID_ALIAS[channel_id] channel_id = self.CHANNEL_ID_ALIAS[channel_id]
# Make directory # Make directory
if not os.path.exists(dir): if not os.path.exists(target_dir):
self.logger.info(f"Creating directory {dir}") self.logger.info(f"Creating directory {target_dir}")
os.makedirs(dir) os.makedirs(target_dir)
# Download json index # Download json index
self.logger.info(f"Downloading {self.UPDATE_INDEX}") self.logger.info(f"Downloading {self.UPDATE_INDEX}")
@ -79,19 +79,14 @@ class UpdateDownloader:
self.logger.info(f"Using version '{version['version']}'") self.logger.info(f"Using version '{version['version']}'")
# Get changelog # Get changelog
changelog = None if changelog := version.get("changelog"):
try:
changelog = version["changelog"]
except Exception as e:
self.logger.error(f"Failed to get changelog: {e}")
# print changelog
if changelog is not None:
self.logger.info(f"Changelog:") self.logger.info(f"Changelog:")
for line in changelog.split("\n"): for line in changelog.split("\n"):
if line.strip() == "": if line.strip() == "":
continue continue
self.logger.info(f" {line}") self.logger.info(f" {line}")
else:
self.logger.warning(f"Changelog not found")
# Find file # Find file
file_url = None file_url = None
@ -106,7 +101,7 @@ class UpdateDownloader:
# Make file path # Make file path
file_name = file_url.split("/")[-1] file_name = file_url.split("/")[-1]
file_path = os.path.join(dir, file_name) file_path = os.path.join(target_dir, file_name)
# Download file # Download file
self.logger.info(f"Downloading {file_url} to {file_path}") self.logger.info(f"Downloading {file_url} to {file_path}")
@ -117,7 +112,7 @@ class UpdateDownloader:
# Unzip tgz # Unzip tgz
self.logger.info(f"Unzipping {file_path}") self.logger.info(f"Unzipping {file_path}")
with tarfile.open(file_path, "r") as tar: with tarfile.open(file_path, "r") as tar:
tar.extractall(dir) tar.extractall(target_dir)
return True return True
@ -133,16 +128,24 @@ class Main(App):
# logging # logging
self.logger = logging.getLogger() self.logger = logging.getLogger()
def find_wifi_board(self) -> bool: @staticmethod
def _grep_ports(regexp: str) -> list[ListPortInfo]:
# idk why, but python thinks that list_ports.grep returns tuple[str, str, str] # idk why, but python thinks that list_ports.grep returns tuple[str, str, str]
blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore return list(list_ports.grep(regexp)) # type: ignore
daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore
return len(blackmagics) > 0 or len(daps) > 0 def is_wifi_board_connected(self) -> bool:
return (
len(self._grep_ports("ESP32-S2")) > 0
or len(self._grep_ports("CMSIS-DAP")) > 0
)
def find_wifi_board_bootloader(self): @staticmethod
# idk why, but python thinks that list_ports.grep returns tuple[str, str, str] def is_windows() -> bool:
ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore return os.name == "nt"
@classmethod
def find_port(cls, regexp: str) -> str:
ports: list[ListPortInfo] = cls._grep_ports(regexp)
if len(ports) == 0: if len(ports) == 0:
# Blackmagic probe serial port not found, will be handled later # Blackmagic probe serial port not found, will be handled later
@ -151,27 +154,28 @@ class Main(App):
raise Exception("More than one WiFi board found") raise Exception("More than one WiFi board found")
else: else:
port = ports[0] port = ports[0]
if os.name == "nt": return f"\\\\.\\{port.device}" if cls.is_windows() else port.device
port.device = f"\\\\.\\{port.device}"
return port.device def find_wifi_board_bootloader_port(self):
return self.find_port("ESP32-S2")
def find_wifi_board_bootloader_port_damn_windows(self):
self.logger.info("Trying to find WiFi board using VID:PID")
return self.find_port("VID:PID=303A:0002")
def update(self): def update(self):
try: try:
port = self.find_wifi_board_bootloader() port = self.find_wifi_board_bootloader_port()
# Damn windows fix
if port is None and self.is_windows():
port = self.find_wifi_board_bootloader_port_damn_windows()
except Exception as e: except Exception as e:
self.logger.error(f"{e}") self.logger.error(f"{e}")
return 1 return 1
if self.args.port != "auto":
port = self.args.port
available_ports = [p[0] for p in list(list_ports.comports())]
if port not in available_ports:
self.logger.error(f"Port {port} not found")
return 1
if port is None: if port is None:
if self.find_wifi_board(): if self.is_wifi_board_connected():
self.logger.error("WiFi board found, but not in bootloader mode.") self.logger.error("WiFi board found, but not in bootloader mode.")
self.logger.info("Please hold down BOOT button and press RESET button") self.logger.info("Please hold down BOOT button and press RESET button")
else: else:
@ -179,6 +183,13 @@ class Main(App):
self.logger.info( self.logger.info(
"Please connect WiFi board to your computer, hold down BOOT button and press RESET button" "Please connect WiFi board to your computer, hold down BOOT button and press RESET button"
) )
if not self.is_windows():
self.logger.info(
"If you are using Linux, you may need to add udev rules to access the device"
)
self.logger.info(
"Check out 41-flipper.rules & README in scripts/debug folder"
)
return 1 return 1
# get temporary dir # get temporary dir
@ -197,24 +208,29 @@ class Main(App):
with open(os.path.join(temp_dir, "flash.command"), "r") as f: with open(os.path.join(temp_dir, "flash.command"), "r") as f:
flash_command = f.read() flash_command = f.read()
flash_command = flash_command.replace("\n", "").replace("\r", "") replacements = (
flash_command = flash_command.replace("(PORT)", port) ("\n", ""),
("\r", ""),
# We can't reset the board after flashing via usb ("(PORT)", port),
flash_command = flash_command.replace( # We can't reset the board after flashing via usb
"--after hard_reset", "--after no_reset_stub" ("--after hard_reset", "--after no_reset_stub"),
) )
args = flash_command.split(" ")[0:] # hellish toolchain fix
args = list(filter(None, args)) if self.is_windows():
replacements += (("esptool.py", "python -m esptool"),)
else:
replacements += (("esptool.py", "python3 -m esptool"),)
esptool_params = [] for old, new in replacements:
esptool_params.extend(args) flash_command = flash_command.replace(old, new)
args = list(filter(None, flash_command.split()))
self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"')
process = subprocess.Popen( process = subprocess.Popen(
esptool_params, args,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
cwd=temp_dir, cwd=temp_dir,

View file

@ -279,6 +279,11 @@ vars.AddVariables(
help="Enable strict import check for .faps", help="Enable strict import check for .faps",
default=True, default=True,
), ),
(
"ARGS",
"Extra arguments to pass to certain scripts supporting it",
"",
),
) )
Return("vars") Return("vars")