Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Keep scripts LF so Docker/Linux builds work when the repo is cloned on Windows (core.autocrlf).
*.py text eol=lf
*.sh text eol=lf
hackrf/firmware/libopencm3/scripts/irq2nvic_h text eol=lf
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,26 @@ else()
math(EXPR FLASH_BYTES_LIMIT_SIZE "${FLASH_MB_LIMIT_SIZE} * 1024 * 1024")
endif()

# 1 MiB SPI pack (H4M / W25Q80): trim baseband modes in firmware/baseband/CMakeLists.txt.
if(FLASH_MB_LIMIT_SIZE EQUAL 1)
set(MAYHEM_SPI_1MB TRUE CACHE INTERNAL "1 MiB SPI image profile")
endif()

option(USE_CCACHE "Enable ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary" OFF)

project(portapack-h1)

# CMake's default `CMAKE_*_FLAGS_RELEASE` includes `-O3`, which is appended after ChibiOS
# `USE_OPT` (`-Os`) so the effective C/C++ optimization becomes -O3 (last flag wins). That
# routinely overflows the M0 `LD_FLASH_SIZE` limit with GCC 13 + G4 / tjpgd.
foreach(_lang C CXX)
if(DEFINED CMAKE_${_lang}_FLAGS_RELEASE)
string(REPLACE "-O3" "-Os" CMAKE_${_lang}_FLAGS_RELEASE "${CMAKE_${_lang}_FLAGS_RELEASE}")
set(CMAKE_${_lang}_FLAGS_RELEASE "${CMAKE_${_lang}_FLAGS_RELEASE}"
CACHE STRING "Mayhem release: size-oriented (-Os), not CMake default -O3" FORCE)
endif()
endforeach()

set(EXPECTED_GCC_VERSION "9.2.1")

set(VERSION "$ENV{VERSION_STRING}")
Expand Down
9 changes: 7 additions & 2 deletions dockerize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# The image will be automatically build if it
# does not exist, but if the dockerfile changes
# it need to be rebuilt manually.
#
#
# Advanced parameters:
# - Get a shell inside the build image to
# inspect problems: ./dockerize.sh shell
Expand Down Expand Up @@ -72,7 +72,12 @@ start_docker() {
build_image
fi

exec docker run -v "${DIR}:/havoc" -u "$(id -u):$(id -g)" -ti --rm "${IMAGE}" "$@"
# -t requires a real TTY; CI / scripted runs often have none (docker then errors).
run_opts=(-i)
if [ -t 1 ]; then
run_opts+=(-t)
fi
exec docker run -v "${DIR}:/havoc" -u "$(id -u):$(id -g)" "${run_opts[@]}" --rm "${IMAGE}" "$@"
}

if [ "$1" = 'shell' ]; then # open a shell into the container
Expand Down
19 changes: 15 additions & 4 deletions firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,22 @@ if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
set(GCC_VERSION_MISMATCH 1)
endif()

#generate h files
configure_file(flashsize.h.in flashsize.h)
add_compile_options(-include ${CMAKE_CURRENT_BINARY_DIR}/flashsize.h)
# flashsize.h must exist before application/baseband compile (-include is global for this tree).
set(FLASHSIZE_H "${CMAKE_CURRENT_BINARY_DIR}/flashsize.h")
configure_file(flashsize.h.in "${FLASHSIZE_H}" @ONLY)
add_custom_target(flashsize_h ALL DEPENDS "${FLASHSIZE_H}")
add_compile_options(-include "${FLASHSIZE_H}")

add_subdirectory(application)
add_subdirectory(baseband)
add_subdirectory(standalone)
add_subdirectory(test)

# program-external-apps reads baseband/<TAG>.bin; build baseband.img (and external chunks) first.
if(TARGET baseband AND TARGET application)
add_dependencies(application baseband)
endif()

if(BOARD STREQUAL "PRALINE")
math(EXPR PRALINE_FINAL_SIZE "4 * 1024 * 1024")
set(PRALINE_FPGA_BIN ${CMAKE_CURRENT_SOURCE_DIR}/../hackrf/firmware/fpga/build/praline_fpga.bin)
Expand Down Expand Up @@ -126,7 +133,11 @@ add_custom_command(
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
COMMAND mkdir -p firmware_tar/APPS
COMMAND cp application/*.ppma firmware_tar/APPS
COMMAND cp standalone/*/*.ppmp firmware_tar/APPS
COMMAND rm -f firmware_tar/APPS/meteor_capture.ppma
COMMAND cp application/meteor_capture.ppma firmware_tar/APPS/meteor-capture.ppma
COMMAND cp standalone/pacman/*.ppmp standalone/digitalrain/*.ppmp firmware_tar/APPS
COMMAND cp standalone/meteor_lrpt_decode/meteor_lrpt_decode_app.ppmp firmware_tar/APPS/meteor-decode.ppmp
COMMAND cp standalone/meteor_lrpt_view/meteor_lrpt_view_app.ppmp firmware_tar/APPS/meteor-view.ppmp
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
DEPENDS firmware ${FIRMWARE_FILENAME} standalone_apps
# Dont use VERBATIM here as it prevents usage of globbing (*)
Expand Down
59 changes: 55 additions & 4 deletions firmware/application/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ set(USE_LINK_GC yes)
# Linker extra options here.
set(USE_LDOPT)

# Enable this if you want link time optimizations (LTO) - this flag affects chibios only
# LTO on application breaks ChibiOS link (chThdExit / firmware_info); keep off even for 1 MiB SPI.
set(USE_LTO no)

# If enabled, this option allows to compile the application in THUMB mode.
Expand Down Expand Up @@ -137,6 +137,8 @@ set(CSRC
#look for all i2cdev_ files
file(GLOB I2CDEV_SOURCES ${COMMON}/i2cdev_*.cpp)

include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/MeteorLrptDeintSources.cmake)

# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
Expand Down Expand Up @@ -335,7 +337,35 @@ set(CPPSRC
view_factory_base.cpp
)

set_source_files_properties(${CPPSRC} PROPERTIES COMPILE_FLAGS -flto) # Add lto flag to the non-external sources only
# 1 MiB SPI: baseband omits PSON/PWTH/PSGD — drop matching built-in RX apps to save M0 flash.
if(MAYHEM_SPI_1MB)
list(REMOVE_ITEM CPPSRC
apps/ui_sonde.cpp
apps/ui_weatherstation.cpp
apps/ui_subghzd.cpp
${COMMON}/sonde_packet.cpp
meteor_lrpt_g4/third_party/tjpgd/tjpgd.c
)
list(APPEND CPPSRC meteor_lrpt_capture_stub.cpp)
else()
list(APPEND CPPSRC
meteor_lrpt_sector_file_ring.cpp
meteor_lrpt_deinterleave_dispatch_m0.cpp
meteor_lrpt_deint_service.cpp
meteor_lrpt_msumr.cpp
meteor_lrpt_g4/msumr_demux.cpp
meteor_lrpt_g4/jpeg_decode.cpp
meteor_lrpt_g4_service.cpp
${METEOR_LRPT_DEINT_CPPSRC_APPLICATION}
)
endif()

# Historically we forced `-flto` on internal C++ objects even when ChibiOS `USE_LTO` was `no`.
# That spikes compiler RAM (LTO streaming) and breaks lean Docker builds with misleading errors
# like "Cannot allocate memory" while opening standard headers. Only enable when LTO is on.
if(USE_LTO STREQUAL "yes")
set_source_files_properties(${CPPSRC} PROPERTIES COMPILE_FLAGS -flto)
endif()

list (APPEND CPPSRC ${EXTCPPSRC}) # Append external sources after setting lto flag to internal ones

Expand Down Expand Up @@ -365,9 +395,10 @@ set(ASMSRC
lz4.S
)

# FatFs lives under .../ext/fatfs/src — keep it off the normal `-I` list so `#include <ext/...>`
# from libstdc++ is never resolved under the FatFs tree. Added as `-idirafter` on the target.
set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC}
${HALINC} ${PLATFORMINC} ${BOARDINC}
${FATFSINC}
${CHIBIOS}/os/various
${HACKRF_PATH}/firmware/libopencm3/include
${HACKRF_PATH}/firmware/common
Expand All @@ -378,6 +409,10 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
protocols
bmps
)
# M0 Meteor LRPT: `meteor_lrpt` headers + shared deinterleave sources (see ../cmake/MeteorLrptDeintSources.cmake).
list(APPEND INCDIR ${CMAKE_CURRENT_SOURCE_DIR}/../baseband)
list(APPEND INCDIR ${CMAKE_CURRENT_SOURCE_DIR}/meteor_lrpt_g4)
list(APPEND INCDIR ${CMAKE_CURRENT_SOURCE_DIR}/meteor_lrpt_g4/third_party/tjpgd)

#
# Project, sources and paths
Expand Down Expand Up @@ -442,7 +477,11 @@ set(DLIBS)
#

# List all user C define here, like -D_DEBUG=1
set(UDEFS)
if(MAYHEM_SPI_1MB)
set(UDEFS -DMAYHEM_SPI_1MB=1)
else()
set(UDEFS)
endif()

# Define ASM defines here
set(UADEFS)
Expand Down Expand Up @@ -490,6 +529,18 @@ add_custom_command(
)

add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
if(TARGET flashsize_h)
add_dependencies(${PROJECT_NAME}.elf flashsize_h)
endif()
if(NOT MAYHEM_SPI_1MB)
set_source_files_properties(
meteor_lrpt_g4/third_party/tjpgd/tjpgd.c
PROPERTIES COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:C>:-Os>"
)
endif()
target_compile_options(${PROJECT_NAME}.elf PRIVATE
"$<$<COMPILE_LANGUAGE:C,CXX>:-idirafter${CHIBIOS_PORTAPACK}/ext/fatfs/src>"
)
target_link_options(${PROJECT_NAME}.elf PRIVATE
"LINKER:--defsym,LD_FLASH_SIZE=${FLASH_BYTES_LIMIT_SIZE}"
)
Expand Down
3 changes: 1 addition & 2 deletions firmware/application/app_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,8 @@ SettingsManager::SettingsManager(
settings_.mode = mode;
settings_.options = options;

// Pre-alloc enough for app settings and additional settings.
additional_settings.reserve(COMMON_APP_SETTINGS_COUNT + additional_settings.size());
bindings_ = std::move(additional_settings);
bindings_.reserve(COMMON_APP_SETTINGS_COUNT + bindings_.size());

// Settings should always be loaded because apps now rely
// on being able to store UI settings, config, etc.
Expand Down
60 changes: 48 additions & 12 deletions firmware/application/apps/ui_flash_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "portapack_shared_memory.hpp"
#include "file_path.hpp"

#include <cstring>

namespace ui {

// Firmware image validation
Expand All @@ -34,6 +36,14 @@ static const char* hackrf_magic = "HACKRFFW";
Thread* FlashUtilityView::thread{nullptr};
static constexpr size_t max_filename_length = 60; // max length of filename

static std::filesystem::path::string_type normalize_firmware_path(std::filesystem::path::string_type path) {
if (path.empty())
return path;
if (path.front() != u'/')
return std::u16string(u"/") + path;
return path;
}

bool valid_firmware_file(std::filesystem::path::string_type path) {
File firmware_file;
bool require_checksum{false};
Expand All @@ -42,14 +52,16 @@ bool valid_firmware_file(std::filesystem::path::string_type path) {

static_assert((FIRMWARE_INFO_AREA_OFFSET % sizeof(read_buffer)) == 0, "Read buffer size must divide evenly into FIRMWARE_INFO_AREA_OFFSET");

path = normalize_firmware_path(std::move(path));

// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
auto result = firmware_file.open(path.c_str());
if (!result.is_valid()) {
uint64_t file_size = firmware_file.size();
if (file_size > FLASH_ROM_SIZE) {
// Firmware file is larger than the flash size for this device
const uint64_t file_size = firmware_file.size();
if (file_size != FLASH_ROM_SIZE) {
return false;
}
require_checksum = true;
checksum = 0;
for (uint64_t offset = 0; offset < FLASH_ROM_SIZE && offset < file_size; offset += sizeof(read_buffer)) {
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
Expand Down Expand Up @@ -107,20 +119,32 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
};

add_firmware_items(firmware_dir, u"*.bin", ui::Theme::getInstance()->fg_red->foreground);
add_firmware_items(firmware_dir, u"*.tar", ui::Theme::getInstance()->fg_cyan->foreground);
/* OCI package: portapack-mayhem_OCI.ppfw.tar (do not also glob *.tar — duplicates menu entries). */
add_firmware_items(firmware_dir, u"*.ppfw.tar", ui::Theme::getInstance()->fg_cyan->foreground);

// add_firmware_items(user_firmware_folder,u"*.bin", ui::Theme::getInstance()->fg_cyan->foreground);
}

void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
const bool spi_1mb_limit = ((uint32_t)FLASH_SIZE_LIMIT_MB == 1);
const char* const body = spi_1mb_limit
? "Experimental on 1MiB SPI:\nuse PC hackrf_spiflash\nwhen possible.\n\n"
"This will replace your\ncurrent firmware.\n\n"
"If things go wrong, recover\nwith DFU + mayhem_flasher."
: "This will replace your\ncurrent firmware.\n\n"
"If things go wrong you are\nrequired to flash manually\nwith dfu.";

nav_.push<ModalMessageView>(
"Warning!",
"This will replace your\ncurrent firmware.\n\nIf things go wrong you are\nrequired to flash manually\nwith dfu.",
body,
YESNO,
[this, path](bool choice) {
if (choice) {
std::filesystem::path::string_type full_path = firmware_dir.native() + u"/" + path;
this->flash_firmware(full_path);
std::filesystem::path::string_type full_path =
(path.find(u'/') != std::filesystem::path::string_type::npos)
? path
: (firmware_dir.native() + u"/" + path);
this->flash_firmware(normalize_firmware_path(std::move(full_path)));
}
});
}
Expand Down Expand Up @@ -157,12 +181,13 @@ std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::strin

bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
ui::Painter painter;
path = normalize_firmware_path(std::move(path));

if (endsWith(path, u".tar")) {
// extract, then update
path = extract_tar(u'/' + path, painter).native();
path = normalize_firmware_path(extract_tar(path, painter).native());
}

if (path.empty() || !valid_firmware_file(path.c_str())) {
if (path.empty() || !valid_firmware_file(path)) {
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, Theme::getInstance()->bg_darkest->background);
painter.draw_string({0, 60}, *Theme::getInstance()->fg_red, "BAD FIRMWARE FILE OR W/R ERR");
chThdSleepMilliseconds(5000);
Expand All @@ -172,12 +197,23 @@ bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
{0, 0, portapack::display.width(), portapack::display.height()},
Theme::getInstance()->bg_darkest->background);

painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
painter.draw_string({12, 24}, this->nav_.style(), "This may take several minutes.");
if ((uint32_t)FLASH_SIZE_LIMIT_MB >= 2) {
painter.draw_string({12, 44}, this->nav_.style(), "(2MB images need extra time.)");
}
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LED RX");
painter.draw_string({12, 84}, this->nav_.style(), "is on and TX is flashing.");
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");

std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
const size_t path_bytes = (path.length() + 1) * sizeof(std::filesystem::path::value_type);
if (path_bytes > SharedMemory::flash_utility_path_bytes) {
painter.draw_string({0, 60}, *Theme::getInstance()->fg_red, "FIRMWARE PATH TOO LONG");
chThdSleepMilliseconds(3000);
return false;
}

std::memset(shared_memory.bb_data.data, 0, sizeof(shared_memory.bb_data.data));
std::memcpy(shared_memory.bb_data.data, path.c_str(), path_bytes);
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
m0_halt();
return true; // fixes compiler warning (line should not be reached due to halt)
Expand Down
2 changes: 1 addition & 1 deletion firmware/application/apps/ui_flash_utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include "untar.hpp"
#include <cstdint>

#define FLASH_ROM_SIZE (FLASH_SIZE_MB * 1024 * 1024)
#define FLASH_ROM_SIZE ((uint32_t)FLASH_SIZE_LIMIT_MB * 1024U * 1024U)
#define FLASH_STARTING_ADDRESS 0x00000000
#define FLASH_EXPECTED_CHECKSUM 0x00000000
#define FLASH_CHECKSUM_ERROR 0xFFFFFFFF
Expand Down
Loading
Loading