mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 23:09:26 -05:00
External apps (#1469)
* implemented external app build * added some ui stuff for testing * added pacman game * wired key to pacman game * fixed pacman drawing issue * changed afsk rx app to be external * fixed ui::NavigationView initialization for external apps * refactoring * refactoring * moved m4 image to external app * added script for external app deployment * refactoring * implemented dynamic app listing * added color to app icon * improved app loading * added external apps to sd card content * refactoring * review findings * typo * review findings * improved memory management of bitmaps
This commit is contained in:
parent
78713cc2af
commit
7fdb1af69d
4
.github/workflows/create_nightly_release.yml
vendored
4
.github/workflows/create_nightly_release.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||||
- name: Create Small SD Card ZIP - No World Map
|
- name: Create Small SD Card ZIP - No World Map
|
||||||
run: |
|
run: |
|
||||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && zip -r sdcard-no-map.zip sdcard
|
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
|
||||||
- name: Download world map
|
- name: Download world map
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||||
@ -61,7 +61,7 @@ jobs:
|
|||||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||||
- name: Create SD Card ZIP
|
- name: Create SD Card ZIP
|
||||||
run: |
|
run: |
|
||||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && zip -r sdcard.zip sdcard
|
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
|
||||||
- name: Create changelog
|
- name: Create changelog
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
4
.github/workflows/create_stable_release.yml
vendored
4
.github/workflows/create_stable_release.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||||
- name: Create Small SD Card ZIP - No World Map
|
- name: Create Small SD Card ZIP - No World Map
|
||||||
run: |
|
run: |
|
||||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && zip -r sdcard-no-map.zip sdcard
|
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
|
||||||
- name: Download world map
|
- name: Download world map
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||||
@ -45,7 +45,7 @@ jobs:
|
|||||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||||
- name: Create SD Card ZIP
|
- name: Create SD Card ZIP
|
||||||
run: |
|
run: |
|
||||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && zip -r sdcard.zip sdcard
|
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
|
||||||
- name: Create changelog
|
- name: Create changelog
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
@ -42,6 +42,12 @@ if ("$ENV{VERSION_STRING}" STREQUAL "")
|
|||||||
endif (GIT_VERSION_FOUND)
|
endif (GIT_VERSION_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND bash "-c" "echo ${VERSION} | md5sum | head -c 8"
|
||||||
|
OUTPUT_VARIABLE VERSION_MD5
|
||||||
|
)
|
||||||
|
set(VERSION_MD5 "0x${VERSION_MD5}")
|
||||||
|
|
||||||
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
|
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
|
||||||
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
|
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ set(CHIBIOS ${PROJECT_SOURCE_DIR}/chibios)
|
|||||||
set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
|
set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
|
||||||
|
|
||||||
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
|
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
|
||||||
|
set(EXPORT_EXTERNAL_APP_IMAGES ${PROJECT_SOURCE_DIR}/tools/export_external_apps.py)
|
||||||
set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
|
set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
|
||||||
set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
|
set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
|
||||||
set(LZ4 lz4)
|
set(LZ4 lz4)
|
||||||
@ -55,8 +56,15 @@ add_custom_target(
|
|||||||
program
|
program
|
||||||
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary)
|
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary)
|
||||||
COMMAND sleep 3s
|
COMMAND sleep 3s
|
||||||
COMMAND hackrf_spiflash -w ${FIRMWARE_FILENAME}
|
COMMAND hackrf_spiflash -i -R -w ${FIRMWARE_FILENAME}
|
||||||
DEPENDS ${FIRMWARE_FILENAME}
|
DEPENDS firmware ${FIRMWARE_FILENAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
program-external-apps
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND ${PROJECT_SOURCE_DIR}/tools/copy_external_apps.sh
|
||||||
|
DEPENDS program
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: Bad hack to fix location of LICENSE file for tar.
|
# TODO: Bad hack to fix location of LICENSE file for tar.
|
||||||
|
@ -32,7 +32,7 @@ include(CheckCXXCompilerFlag)
|
|||||||
project(application)
|
project(application)
|
||||||
|
|
||||||
# Compiler options here.
|
# Compiler options here.
|
||||||
set(USE_OPT "-Os -g --specs=nano.specs")
|
set(USE_OPT "-Os -g --specs=nano.specs -specs=nosys.specs")
|
||||||
|
|
||||||
# C specific options here (added to USE_OPT).
|
# C specific options here (added to USE_OPT).
|
||||||
set(USE_COPT "-std=gnu99")
|
set(USE_COPT "-std=gnu99")
|
||||||
@ -97,6 +97,7 @@ include(${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0/port.cmake)
|
|||||||
include(${CHIBIOS}/os/kernel/kernel.cmake)
|
include(${CHIBIOS}/os/kernel/kernel.cmake)
|
||||||
include(${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs.cmake)
|
include(${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs.cmake)
|
||||||
include(${CHIBIOS}/test/test.cmake)
|
include(${CHIBIOS}/test/test.cmake)
|
||||||
|
include(external/external.cmake)
|
||||||
|
|
||||||
# Define linker script file here
|
# Define linker script file here
|
||||||
set(LDSCRIPT ${PORTLD}/LPC43xx_M0.ld)
|
set(LDSCRIPT ${PORTLD}/LPC43xx_M0.ld)
|
||||||
@ -256,7 +257,6 @@ set(CPPSRC
|
|||||||
apps/ui_about_simple.cpp
|
apps/ui_about_simple.cpp
|
||||||
apps/ui_adsb_rx.cpp
|
apps/ui_adsb_rx.cpp
|
||||||
apps/ui_adsb_tx.cpp
|
apps/ui_adsb_tx.cpp
|
||||||
apps/ui_afsk_rx.cpp
|
|
||||||
apps/ui_aprs_rx.cpp
|
apps/ui_aprs_rx.cpp
|
||||||
apps/ui_aprs_tx.cpp
|
apps/ui_aprs_tx.cpp
|
||||||
apps/ui_bht_tx.cpp
|
apps/ui_bht_tx.cpp
|
||||||
@ -322,6 +322,9 @@ set(CPPSRC
|
|||||||
${CPLD_20150901_DATA_CPP}
|
${CPLD_20150901_DATA_CPP}
|
||||||
${CPLD_20170522_DATA_CPP}
|
${CPLD_20170522_DATA_CPP}
|
||||||
${HACKRF_CPLD_DATA_CPP}
|
${HACKRF_CPLD_DATA_CPP}
|
||||||
|
ui_external_items_menu_loader.cpp
|
||||||
|
|
||||||
|
${EXTCPPSRC}
|
||||||
)
|
)
|
||||||
|
|
||||||
# C sources to be compiled in ARM mode regardless of the global setting.
|
# C sources to be compiled in ARM mode regardless of the global setting.
|
||||||
@ -397,7 +400,7 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
|
|||||||
# List all default C defines here, like -D_DEBUG=1
|
# List all default C defines here, like -D_DEBUG=1
|
||||||
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
||||||
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
||||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"'")
|
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5}")
|
||||||
|
|
||||||
# List all default ASM defines here, like -D_DEBUG=1
|
# List all default ASM defines here, like -D_DEBUG=1
|
||||||
set(DADEFS)
|
set(DADEFS)
|
||||||
@ -466,12 +469,13 @@ set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
|
|||||||
add_definitions(${DEFS})
|
add_definitions(${DEFS})
|
||||||
include_directories(. ${INCDIR})
|
include_directories(. ${INCDIR})
|
||||||
link_directories(${LLIBDIR})
|
link_directories(${LLIBDIR})
|
||||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
target_link_libraries(${PROJECT_NAME}.elf ${LIBS} "-L${CMAKE_CURRENT_LIST_DIR}/external")
|
||||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_NAME}.bin
|
OUTPUT ${PROJECT_NAME}.bin
|
||||||
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
|
COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin --remove-section=.external_app_*
|
||||||
|
COMMAND ${EXPORT_EXTERNAL_APP_IMAGES} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_OBJCOPY} ${EXTAPPLIST}
|
||||||
DEPENDS ${PROJECT_NAME}.elf
|
DEPENDS ${PROJECT_NAME}.elf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -329,6 +329,30 @@ void run_image(const spi_flash::image_tag_t image_tag) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run_prepared_image(const uint32_t m4_code) {
|
||||||
|
if (baseband_image_running) {
|
||||||
|
chDbgPanic("BBRunning");
|
||||||
|
}
|
||||||
|
|
||||||
|
creg::m4txevent::clear();
|
||||||
|
shared_memory.clear_baseband_ready();
|
||||||
|
|
||||||
|
m4_init_prepared(m4_code, false);
|
||||||
|
baseband_image_running = true;
|
||||||
|
|
||||||
|
creg::m4txevent::enable();
|
||||||
|
|
||||||
|
if constexpr (enforce_core_sync) {
|
||||||
|
// Wait up to 3 seconds for baseband to start handling events.
|
||||||
|
auto count = 3'000u;
|
||||||
|
while (!shared_memory.baseband_ready && --count)
|
||||||
|
chThdSleepMilliseconds(1);
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
chDbgPanic("Baseband Sync Fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
if (!baseband_image_running) {
|
if (!baseband_image_running) {
|
||||||
return;
|
return;
|
||||||
|
@ -89,6 +89,7 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
|
|||||||
void request_beep();
|
void request_beep();
|
||||||
|
|
||||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||||
|
void run_prepared_image(const uint32_t m4_code);
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
void spectrum_streaming_start();
|
void spectrum_streaming_start();
|
||||||
|
@ -59,6 +59,19 @@ void m4_init(const spi_flash::image_tag_t image_tag, const memory::region_t to,
|
|||||||
chDbgPanic("NoImg");
|
chDbgPanic("NoImg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void m4_init_prepared(const uint32_t m4_code, const bool full_reset) {
|
||||||
|
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
|
||||||
|
* with its address space and RAM without concern.
|
||||||
|
*/
|
||||||
|
LPC_CREG->M4MEMMAP = m4_code;
|
||||||
|
|
||||||
|
/* Reset M4 core and optionally all peripherals */
|
||||||
|
LPC_RGU->RESET_CTRL[0] = (full_reset) ? (1 << 1) // PERIPH_RST
|
||||||
|
: (1 << 13); // M4_RST
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void m4_request_shutdown() {
|
void m4_request_shutdown() {
|
||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "spi_image.hpp"
|
#include "spi_image.hpp"
|
||||||
|
|
||||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
|
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
|
||||||
|
void m4_init_prepared(const uint32_t m4_code, const bool full_reset);
|
||||||
void m4_request_shutdown();
|
void m4_request_shutdown();
|
||||||
|
|
||||||
void m0_halt();
|
void m0_halt();
|
||||||
|
82
firmware/application/external/afsk_rx/main.cpp
vendored
Normal file
82
firmware/application/external/afsk_rx/main.cpp
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bernd Herzog
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_afsk_rx.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "external_app.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::afsk_rx {
|
||||||
|
void initialize_app(ui::NavigationView& nav) {
|
||||||
|
nav.push<AFSKRxView>();
|
||||||
|
}
|
||||||
|
} // namespace ui::external_app::afsk_rx
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
__attribute__((section(".external_app.app_afsk_rx.application_information"), used)) application_information_t _application_information_afsk_rx = {
|
||||||
|
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||||
|
/*.externalAppEntry = */ ui::external_app::afsk_rx::initialize_app,
|
||||||
|
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||||
|
/*.app_version = */ VERSION_MD5,
|
||||||
|
|
||||||
|
/*.app_name = */ "AFSK",
|
||||||
|
/*.bitmap_data = */ {
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xF8,
|
||||||
|
0x1F,
|
||||||
|
0x04,
|
||||||
|
0x20,
|
||||||
|
0x02,
|
||||||
|
0x40,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xAB,
|
||||||
|
0xDF,
|
||||||
|
0xAB,
|
||||||
|
0xDF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
},
|
||||||
|
/*.icon_color = */ ui::Color::yellow().v,
|
||||||
|
/*.menu_location = */ app_location_t::RX,
|
||||||
|
|
||||||
|
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
|
||||||
|
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||||
|
};
|
||||||
|
}
|
@ -32,20 +32,21 @@
|
|||||||
|
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
using namespace modems;
|
using namespace modems;
|
||||||
|
using namespace ui;
|
||||||
|
|
||||||
|
namespace ui::external_app::afsk_rx {
|
||||||
|
|
||||||
void AFSKLogger::log_raw_data(const std::string& data) {
|
void AFSKLogger::log_raw_data(const std::string& data) {
|
||||||
log_file.write_entry(data);
|
log_file.write_entry(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ui {
|
|
||||||
|
|
||||||
void AFSKRxView::focus() {
|
void AFSKRxView::focus() {
|
||||||
field_frequency.focus();
|
field_frequency.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
AFSKRxView::AFSKRxView(NavigationView& nav)
|
AFSKRxView::AFSKRxView(NavigationView& nav)
|
||||||
: nav_{nav} {
|
: nav_{nav} {
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
|
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||||
|
|
||||||
add_children({&rssi,
|
add_children({&rssi,
|
||||||
&channel,
|
&channel,
|
||||||
@ -147,4 +148,4 @@ AFSKRxView::~AFSKRxView() {
|
|||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} // namespace ui::external_app::afsk_rx
|
@ -33,6 +33,10 @@
|
|||||||
#include "log_file.hpp"
|
#include "log_file.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
using namespace ui;
|
||||||
|
|
||||||
|
namespace ui::external_app::afsk_rx {
|
||||||
|
|
||||||
class AFSKLogger {
|
class AFSKLogger {
|
||||||
public:
|
public:
|
||||||
Optional<File::Error> append(const std::string& filename) {
|
Optional<File::Error> append(const std::string& filename) {
|
||||||
@ -45,8 +49,6 @@ class AFSKLogger {
|
|||||||
LogFile log_file{};
|
LogFile log_file{};
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ui {
|
|
||||||
|
|
||||||
class AFSKRxView : public View {
|
class AFSKRxView : public View {
|
||||||
public:
|
public:
|
||||||
AFSKRxView(NavigationView& nav);
|
AFSKRxView(NavigationView& nav);
|
||||||
@ -116,6 +118,6 @@ class AFSKRxView : public View {
|
|||||||
}};
|
}};
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace ui */
|
} // namespace ui::external_app::afsk_rx
|
||||||
|
|
||||||
#endif /*__UI_AFSK_RX_H__*/
|
#endif /*__UI_AFSK_RX_H__*/
|
15
firmware/application/external/external.cmake
vendored
Normal file
15
firmware/application/external/external.cmake
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
set(EXTCPPSRC
|
||||||
|
|
||||||
|
#pacman
|
||||||
|
external/pacman/main.cpp
|
||||||
|
external/pacman/ui_pacman.cpp
|
||||||
|
|
||||||
|
#afsk_rx
|
||||||
|
external/afsk_rx/main.cpp
|
||||||
|
external/afsk_rx/ui_afsk_rx.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXTAPPLIST
|
||||||
|
pacman
|
||||||
|
afsk_rx
|
||||||
|
)
|
37
firmware/application/external/external.ld
vendored
Normal file
37
firmware/application/external/external.ld
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2023 Bernd Herzog
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* external apps: regions can't overlap so addresses are corrected after build */
|
||||||
|
ram_external_app_pacman (rwx) : org = 0xEEE90000, len = 40k
|
||||||
|
ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 40k
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.external_app_pacman : ALIGN(16) SUBALIGN(16)
|
||||||
|
{
|
||||||
|
KEEP(*(.external_app.app_pacman.application_information));
|
||||||
|
*(*ui*external_app*pacman*);
|
||||||
|
} > ram_external_app_pacman
|
||||||
|
|
||||||
|
.external_app_afsk_rx : ALIGN(16) SUBALIGN(16)
|
||||||
|
{
|
||||||
|
KEEP(*(.external_app.app_afsk_rx.application_information));
|
||||||
|
*(*ui*external_app*afsk_rx*);
|
||||||
|
} > ram_external_app_afsk_rx
|
||||||
|
}
|
66
firmware/application/external/pacman/DrawIndexedMap.h
vendored
Normal file
66
firmware/application/external/pacman/DrawIndexedMap.h
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* PACMAN GAME FOR ARDUINO DUE */
|
||||||
|
/* */
|
||||||
|
/******************************************************************************/
|
||||||
|
/* Copyright (c) 2014 Dr. NCX (mirracle.mxx@gmail.com) */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL */
|
||||||
|
/* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED */
|
||||||
|
/* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR */
|
||||||
|
/* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES */
|
||||||
|
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
|
||||||
|
/* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
|
||||||
|
/* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS */
|
||||||
|
/* SOFTWARE. */
|
||||||
|
/* */
|
||||||
|
/* MIT license, all text above must be included in any redistribution. */
|
||||||
|
|
||||||
|
// #include "ili9328.h"
|
||||||
|
|
||||||
|
typedef uint16_t ushort;
|
||||||
|
|
||||||
|
#define C16(_rr, _gg, _bb) ((ushort)(((_rr & 0xF8) << 8) | ((_gg & 0xFC) << 3) | ((_bb & 0xF8) >> 3)))
|
||||||
|
|
||||||
|
uint16_t _paletteW[] =
|
||||||
|
{
|
||||||
|
C16(0, 0, 0),
|
||||||
|
C16(255, 0, 0), // 1 red
|
||||||
|
C16(222, 151, 81), // 2 brown
|
||||||
|
C16(255, 0, 255), // 3 pink
|
||||||
|
|
||||||
|
C16(0, 0, 0),
|
||||||
|
C16(0, 255, 255), // 5 cyan
|
||||||
|
C16(71, 84, 255), // 6 mid blue
|
||||||
|
C16(255, 184, 81), // 7 lt brown
|
||||||
|
|
||||||
|
C16(0, 0, 0),
|
||||||
|
C16(255, 255, 0), // 9 yellow
|
||||||
|
C16(0, 0, 0),
|
||||||
|
C16(33, 33, 255), // 11 blue
|
||||||
|
|
||||||
|
C16(0, 255, 0), // 12 green
|
||||||
|
C16(71, 84, 174), // 13 aqua
|
||||||
|
C16(255, 184, 174), // 14 lt pink
|
||||||
|
C16(222, 222, 255), // 15 whiteish
|
||||||
|
};
|
||||||
|
|
||||||
|
void drawIndexedmap(uint8_t* indexmap, int16_t x, uint16_t y) {
|
||||||
|
ui::Painter painter;
|
||||||
|
|
||||||
|
byte i = 0;
|
||||||
|
word color = (word)_paletteW[indexmap[0]];
|
||||||
|
for (byte tmpY = 0; tmpY < 8; tmpY++) {
|
||||||
|
byte width = 1;
|
||||||
|
for (byte tmpX = 0; tmpX < 8; tmpX++) {
|
||||||
|
word next_color = (word)_paletteW[indexmap[++i]];
|
||||||
|
if ((color != next_color && width >= 1) || tmpX == 7) {
|
||||||
|
painter.draw_hline({x + tmpX - width + 1, y + tmpY}, width, ui::Color(color));
|
||||||
|
|
||||||
|
color = next_color;
|
||||||
|
width = 0;
|
||||||
|
}
|
||||||
|
width++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2462
firmware/application/external/pacman/PacmanTiles.h
vendored
Normal file
2462
firmware/application/external/pacman/PacmanTiles.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2858
firmware/application/external/pacman/crntsc.h
vendored
Normal file
2858
firmware/application/external/pacman/crntsc.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2342
firmware/application/external/pacman/crpal.h
vendored
Normal file
2342
firmware/application/external/pacman/crpal.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
82
firmware/application/external/pacman/main.cpp
vendored
Normal file
82
firmware/application/external/pacman/main.cpp
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bernd Herzog
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_pacman.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "external_app.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::pacman {
|
||||||
|
void initialize_app(ui::NavigationView& nav) {
|
||||||
|
nav.push<PacmanView>();
|
||||||
|
}
|
||||||
|
} // namespace ui::external_app::pacman
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
__attribute__((section(".external_app.app_pacman.application_information"), used)) application_information_t _application_information_pacman = {
|
||||||
|
/*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time
|
||||||
|
/*.externalAppEntry = */ ui::external_app::pacman::initialize_app,
|
||||||
|
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||||
|
/*.app_version = */ VERSION_MD5,
|
||||||
|
|
||||||
|
/*.app_name = */ "Pac-Man",
|
||||||
|
/*.bitmap_data = */ {
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xC0,
|
||||||
|
0x07,
|
||||||
|
0xE0,
|
||||||
|
0x0F,
|
||||||
|
0xF0,
|
||||||
|
0x1F,
|
||||||
|
0xF8,
|
||||||
|
0x07,
|
||||||
|
0xF8,
|
||||||
|
0x01,
|
||||||
|
0x78,
|
||||||
|
0x00,
|
||||||
|
0xF8,
|
||||||
|
0x01,
|
||||||
|
0xF8,
|
||||||
|
0x07,
|
||||||
|
0xF0,
|
||||||
|
0x1F,
|
||||||
|
0xE0,
|
||||||
|
0x0F,
|
||||||
|
0xC0,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
},
|
||||||
|
/*.icon_color = */ ui::Color::yellow().v,
|
||||||
|
/*.menu_location = */ app_location_t::UTILITIES,
|
||||||
|
|
||||||
|
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||||
|
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||||
|
};
|
||||||
|
}
|
1276
firmware/application/external/pacman/playfield.hpp
vendored
Normal file
1276
firmware/application/external/pacman/playfield.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
50
firmware/application/external/pacman/ui_pacman.cpp
vendored
Normal file
50
firmware/application/external/pacman/ui_pacman.cpp
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "ui_pacman.hpp"
|
||||||
|
#include "irq_controls.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::pacman {
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
// external code, so ignore warnings
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
|
#pragma GCC diagnostic ignored "-Wreturn-type"
|
||||||
|
#pragma GCC diagnostic ignored "-Weffc++"
|
||||||
|
#include "playfield.hpp"
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
Playfield _game;
|
||||||
|
|
||||||
|
PacmanView::PacmanView(NavigationView& nav)
|
||||||
|
: nav_(nav) {
|
||||||
|
add_children({&dummy});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacmanView::focus() {
|
||||||
|
dummy.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacmanView::paint(Painter& painter) {
|
||||||
|
(void)painter;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
_game.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto switches_debounced = get_switches_state().to_ulong();
|
||||||
|
|
||||||
|
but_RIGHT = (switches_debounced & 0x01) == 0x01;
|
||||||
|
but_LEFT = (switches_debounced & 0x02) == 0x02;
|
||||||
|
but_DOWN = (switches_debounced & 0x04) == 0x04;
|
||||||
|
but_UP = (switches_debounced & 0x08) == 0x08;
|
||||||
|
but_A = (switches_debounced & 0x10) == 0x10;
|
||||||
|
|
||||||
|
_game.Step();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacmanView::frame_sync() {
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui::external_app::pacman
|
59
firmware/application/external/pacman/ui_pacman.hpp
vendored
Normal file
59
firmware/application/external/pacman/ui_pacman.hpp
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bernd Herzog
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __UI_PACMAN_H__
|
||||||
|
#define __UI_PACMAN_H__
|
||||||
|
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "event_m0.hpp"
|
||||||
|
#include "message.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::pacman {
|
||||||
|
|
||||||
|
class PacmanView : public View {
|
||||||
|
public:
|
||||||
|
PacmanView(NavigationView& nav);
|
||||||
|
|
||||||
|
void focus() override;
|
||||||
|
|
||||||
|
std::string title() const override { return "Pac-Man"; };
|
||||||
|
|
||||||
|
void paint(Painter& painter) override;
|
||||||
|
void frame_sync();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initialized = false;
|
||||||
|
NavigationView& nav_;
|
||||||
|
|
||||||
|
Button dummy{
|
||||||
|
{240, 0, 0, 0},
|
||||||
|
""};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_sample{
|
||||||
|
Message::ID::DisplayFrameSync,
|
||||||
|
[this](const Message* const) {
|
||||||
|
this->frame_sync();
|
||||||
|
}};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui::external_app::pacman
|
||||||
|
|
||||||
|
#endif /*__UI_PACMAN_H__*/
|
@ -116,6 +116,12 @@ void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
|||||||
update_items();
|
update_items();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BtnGridView::add_item(GridItem new_item) {
|
||||||
|
menu_items.push_back(new_item);
|
||||||
|
|
||||||
|
update_items();
|
||||||
|
}
|
||||||
|
|
||||||
void BtnGridView::update_items() {
|
void BtnGridView::update_items() {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ class BtnGridView : public View {
|
|||||||
~BtnGridView();
|
~BtnGridView();
|
||||||
|
|
||||||
void add_items(std::initializer_list<GridItem> new_items);
|
void add_items(std::initializer_list<GridItem> new_items);
|
||||||
|
void add_item(GridItem new_item);
|
||||||
void set_max_rows(int rows);
|
void set_max_rows(int rows);
|
||||||
int rows();
|
int rows();
|
||||||
void clear();
|
void clear();
|
||||||
|
139
firmware/application/ui_external_items_menu_loader.cpp
Normal file
139
firmware/application/ui_external_items_menu_loader.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "ui_external_items_menu_loader.hpp"
|
||||||
|
|
||||||
|
#include "sd_card.hpp"
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
|
||||||
|
|
||||||
|
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
|
||||||
|
bitmaps.clear();
|
||||||
|
|
||||||
|
std::vector<GridItem> external_apps;
|
||||||
|
|
||||||
|
if (sd_card::status() != sd_card::Status::Mounted)
|
||||||
|
return external_apps;
|
||||||
|
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
|
||||||
|
auto filePath = u"/APPS/" + entry.path();
|
||||||
|
File app;
|
||||||
|
|
||||||
|
auto openError = app.open(filePath);
|
||||||
|
if (openError)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
application_information_t application_information = {};
|
||||||
|
|
||||||
|
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
||||||
|
if (!readResult)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (application_information.menu_location != app_location)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (application_information.header_version != CURRENT_HEADER_VERSION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool versionMatches = VERSION_MD5 == application_information.app_version;
|
||||||
|
|
||||||
|
GridItem gridItem = {};
|
||||||
|
gridItem.text = reinterpret_cast<char*>(&application_information.app_name[0]);
|
||||||
|
|
||||||
|
if (versionMatches) {
|
||||||
|
gridItem.color = Color((uint16_t)application_information.icon_color);
|
||||||
|
|
||||||
|
auto dyn_bmp = DynamicBitmap<16, 16>{application_information.bitmap_data};
|
||||||
|
gridItem.bitmap = dyn_bmp.bitmap();
|
||||||
|
bitmaps.push_back(std::move(dyn_bmp));
|
||||||
|
|
||||||
|
gridItem.on_select = [&nav, app_location, filePath]() {
|
||||||
|
run_external_app(nav, filePath);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
gridItem.color = Color::light_grey();
|
||||||
|
|
||||||
|
gridItem.bitmap = &bitmap_sd_card_error;
|
||||||
|
|
||||||
|
gridItem.on_select = [&nav]() {
|
||||||
|
nav.display_modal("Error", "The .ppma file in your APPS\nfolder is outdated. Please\nupdate your SD Card content.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
external_apps.push_back(gridItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return external_apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
||||||
|
File app;
|
||||||
|
|
||||||
|
auto openError = app.open(filePath);
|
||||||
|
if (openError)
|
||||||
|
chDbgPanic("file gone");
|
||||||
|
|
||||||
|
application_information_t application_information = {};
|
||||||
|
|
||||||
|
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
||||||
|
|
||||||
|
if (!readResult)
|
||||||
|
chDbgPanic("no data");
|
||||||
|
|
||||||
|
app.seek(0);
|
||||||
|
|
||||||
|
if (application_information.m4_app_offset != 0) {
|
||||||
|
// copy application image
|
||||||
|
for (size_t file_read_index = 0; file_read_index < application_information.m4_app_offset; file_read_index += std::filesystem::max_file_block_size) {
|
||||||
|
auto bytes_to_read = std::filesystem::max_file_block_size;
|
||||||
|
if (file_read_index + std::filesystem::max_file_block_size > application_information.m4_app_offset)
|
||||||
|
bytes_to_read = application_information.m4_app_offset - file_read_index;
|
||||||
|
|
||||||
|
if (bytes_to_read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
|
||||||
|
if (!readResult)
|
||||||
|
chDbgPanic("read error");
|
||||||
|
|
||||||
|
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy baseband image
|
||||||
|
for (size_t file_read_index = application_information.m4_app_offset;; file_read_index += readResult.value()) {
|
||||||
|
size_t bytes_to_read = std::filesystem::max_file_block_size;
|
||||||
|
|
||||||
|
// not aligned
|
||||||
|
if ((file_read_index % std::filesystem::max_file_block_size) != 0)
|
||||||
|
bytes_to_read = std::filesystem::max_file_block_size - (file_read_index % std::filesystem::max_file_block_size);
|
||||||
|
|
||||||
|
if (bytes_to_read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto target_memory = reinterpret_cast<void*>(portapack::memory::map::m4_code.base() + file_read_index - application_information.m4_app_offset);
|
||||||
|
|
||||||
|
readResult = app.read(target_memory, bytes_to_read);
|
||||||
|
if (!readResult)
|
||||||
|
chDbgPanic("read error #2");
|
||||||
|
|
||||||
|
if (readResult.value() != bytes_to_read)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// copy application image
|
||||||
|
for (size_t file_read_index = 0; file_read_index < 80 * std::filesystem::max_file_block_size; file_read_index += std::filesystem::max_file_block_size) {
|
||||||
|
auto bytes_to_read = std::filesystem::max_file_block_size;
|
||||||
|
|
||||||
|
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
|
||||||
|
if (!readResult)
|
||||||
|
chDbgPanic("read error #3");
|
||||||
|
|
||||||
|
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
application_information.externalAppEntry(nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
66
firmware/application/ui_external_items_menu_loader.hpp
Normal file
66
firmware/application/ui_external_items_menu_loader.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bernd Herzog
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EXTERNAL_ITEMS_MENU_LOADER_H__
|
||||||
|
#define __EXTERNAL_ITEMS_MENU_LOADER_H__
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "external_app.hpp"
|
||||||
|
|
||||||
|
#include "file.hpp"
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
template <size_t Width, size_t Height>
|
||||||
|
class DynamicBitmap {
|
||||||
|
public:
|
||||||
|
static constexpr size_t buffer_size = Width * Height / (sizeof(uint8_t) * 8); // one bit per pixel
|
||||||
|
|
||||||
|
DynamicBitmap(const uint8_t data[buffer_size])
|
||||||
|
: _buffer(buffer_size, 0),
|
||||||
|
_bitmap{new Bitmap{{Width, Height}, &_buffer[0]}} {
|
||||||
|
memcpy(&_buffer[0], data, buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bitmap* bitmap() { return _bitmap.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Allocating both members so the class is movable without invalidation.
|
||||||
|
std::vector<uint8_t> _buffer;
|
||||||
|
std::unique_ptr<Bitmap> _bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternalItemsMenuLoader {
|
||||||
|
public:
|
||||||
|
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
|
||||||
|
ExternalItemsMenuLoader() = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<DynamicBitmap<16, 16>> bitmaps;
|
||||||
|
|
||||||
|
static void run_external_app(ui::NavigationView&, std::filesystem::path);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
|
||||||
|
#endif /*__EXTERNAL_ITEMS_MENU_LOADER_H__*/
|
@ -34,7 +34,6 @@
|
|||||||
#include "ui_about_simple.hpp"
|
#include "ui_about_simple.hpp"
|
||||||
#include "ui_adsb_rx.hpp"
|
#include "ui_adsb_rx.hpp"
|
||||||
#include "ui_adsb_tx.hpp"
|
#include "ui_adsb_tx.hpp"
|
||||||
#include "ui_afsk_rx.hpp"
|
|
||||||
#include "ui_aprs_rx.hpp"
|
#include "ui_aprs_rx.hpp"
|
||||||
#include "ui_aprs_tx.hpp"
|
#include "ui_aprs_tx.hpp"
|
||||||
#include "ui_bht_tx.hpp"
|
#include "ui_bht_tx.hpp"
|
||||||
@ -80,6 +79,7 @@
|
|||||||
#include "ui_touchtunes.hpp"
|
#include "ui_touchtunes.hpp"
|
||||||
#include "ui_view_wav.hpp"
|
#include "ui_view_wav.hpp"
|
||||||
#include "ui_whipcalc.hpp"
|
#include "ui_whipcalc.hpp"
|
||||||
|
#include "ui_external_items_menu_loader.hpp"
|
||||||
|
|
||||||
// #include "acars_app.hpp"
|
// #include "acars_app.hpp"
|
||||||
#include "ais_app.hpp"
|
#include "ais_app.hpp"
|
||||||
@ -492,7 +492,6 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
|||||||
add_items({
|
add_items({
|
||||||
// {"ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }},
|
// {"ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }},
|
||||||
{"ADS-B", Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBRxView>(); }},
|
{"ADS-B", Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBRxView>(); }},
|
||||||
{"AFSK", Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push<AFSKRxView>(); }},
|
|
||||||
{"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push<AISAppView>(); }},
|
{"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push<AISAppView>(); }},
|
||||||
{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }},
|
{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }},
|
||||||
{"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSRXView>(); }},
|
{"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSRXView>(); }},
|
||||||
@ -512,6 +511,10 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
|||||||
// {"SSTV", Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push<NotImplementedView>(); }},
|
// {"SSTV", Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push<NotImplementedView>(); }},
|
||||||
// {"TETRA", Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push<NotImplementedView>(); }},
|
// {"TETRA", Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push<NotImplementedView>(); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::RX, nav)) {
|
||||||
|
add_item(gridItem);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TransmittersMenuView **************************************************/
|
/* TransmittersMenuView **************************************************/
|
||||||
@ -540,6 +543,10 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
|||||||
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
|
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
|
||||||
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::TX, nav)) {
|
||||||
|
add_item(gridItem);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UtilitiesMenuView *****************************************************/
|
/* UtilitiesMenuView *****************************************************/
|
||||||
@ -565,6 +572,10 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
|
|||||||
{"Wipe SD card", Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push<WipeSDView>(); }},
|
{"Wipe SD card", Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push<WipeSDView>(); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::UTILITIES, nav)) {
|
||||||
|
add_item(gridItem);
|
||||||
|
};
|
||||||
|
|
||||||
set_max_rows(2); // allow wider buttons
|
set_max_rows(2); // allow wider buttons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ class NavigationView : public View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void push(View* v);
|
void push(View* v);
|
||||||
|
View* push_view(std::unique_ptr<View> new_view);
|
||||||
void replace(View* v);
|
void replace(View* v);
|
||||||
void pop(bool trigger_update = true);
|
void pop(bool trigger_update = true);
|
||||||
|
|
||||||
@ -107,7 +108,6 @@ class NavigationView : public View {
|
|||||||
|
|
||||||
void free_view();
|
void free_view();
|
||||||
void update_view();
|
void update_view();
|
||||||
View* push_view(std::unique_ptr<View> new_view);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Holds widgets and grows dynamically toward the left.
|
/* Holds widgets and grows dynamically toward the left.
|
||||||
|
@ -265,6 +265,7 @@ include_directories(. ${INCDIR})
|
|||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
set(BASEBAND_IMAGES)
|
set(BASEBAND_IMAGES)
|
||||||
|
set(BASEBAND_EXTERNAL_IMAGES)
|
||||||
|
|
||||||
macro(DeclareTargets chunk_tag name)
|
macro(DeclareTargets chunk_tag name)
|
||||||
project("baseband_${name}")
|
project("baseband_${name}")
|
||||||
@ -279,18 +280,38 @@ macro(DeclareTargets chunk_tag name)
|
|||||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
||||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||||
|
|
||||||
|
if(add_to_firmware)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_NAME}.bin ${PROJECT_NAME}.img
|
OUTPUT ${chunk_tag}.bin ${PROJECT_NAME}.img
|
||||||
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
|
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${chunk_tag}.bin
|
||||||
COMMAND ${LZ4} -f -9 ${PROJECT_NAME}.bin ${PROJECT_NAME}.lz4
|
COMMAND ${LZ4} -f -9 ${chunk_tag}.bin ${PROJECT_NAME}.lz4
|
||||||
COMMAND ${MAKE_IMAGE_CHUNK} ${PROJECT_NAME}.lz4 ${chunk_tag} ${PROJECT_NAME}.img
|
COMMAND ${MAKE_IMAGE_CHUNK} ${PROJECT_NAME}.lz4 ${chunk_tag} ${PROJECT_NAME}.img
|
||||||
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
|
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||||
|
|
||||||
|
else()
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${chunk_tag}.bin
|
||||||
|
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${chunk_tag}.bin
|
||||||
|
DEPENDS ${PROJECT_NAME}.elf
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
set(BASEBAND_EXTERNAL_IMAGES ${BASEBAND_EXTERNAL_IMAGES} ${chunk_tag}.bin)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
set(add_to_firmware TRUE)
|
||||||
|
|
||||||
### ACARS RX
|
### ACARS RX
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
@ -312,20 +333,13 @@ set(MODE_CPPSRC
|
|||||||
)
|
)
|
||||||
DeclareTargets(PADT adsbtx)
|
DeclareTargets(PADT adsbtx)
|
||||||
|
|
||||||
### AFSK
|
### AFSK TX
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
proc_afsk.cpp
|
proc_afsk.cpp
|
||||||
)
|
)
|
||||||
DeclareTargets(PAFT afsktx)
|
DeclareTargets(PAFT afsktx)
|
||||||
|
|
||||||
### AFSK RX
|
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
|
||||||
proc_afskrx.cpp
|
|
||||||
)
|
|
||||||
DeclareTargets(PAFR afskrx)
|
|
||||||
|
|
||||||
### APRS RX
|
### APRS RX
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
@ -493,13 +507,6 @@ set(MODE_CPPSRC
|
|||||||
)
|
)
|
||||||
DeclareTargets(PSTX sstvtx)
|
DeclareTargets(PSTX sstvtx)
|
||||||
|
|
||||||
### Test
|
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
|
||||||
proc_test.cpp
|
|
||||||
)
|
|
||||||
DeclareTargets(PTST test)
|
|
||||||
|
|
||||||
### Tones
|
### Tones
|
||||||
|
|
||||||
set(MODE_CPPSRC
|
set(MODE_CPPSRC
|
||||||
@ -586,6 +593,24 @@ set(MODE_CPPSRC
|
|||||||
)
|
)
|
||||||
DeclareTargets(PUSB sd_over_usb)
|
DeclareTargets(PUSB sd_over_usb)
|
||||||
|
|
||||||
|
### Place external app and disabled images below so they don't get added to the firmware
|
||||||
|
set(add_to_firmware FALSE)
|
||||||
|
|
||||||
|
### AFSK RX
|
||||||
|
|
||||||
|
set(MODE_CPPSRC
|
||||||
|
proc_afskrx.cpp
|
||||||
|
)
|
||||||
|
DeclareTargets(PAFR afskrx)
|
||||||
|
|
||||||
|
### Test
|
||||||
|
|
||||||
|
set(MODE_CPPSRC
|
||||||
|
proc_test.cpp
|
||||||
|
)
|
||||||
|
DeclareTargets(PTST test)
|
||||||
|
|
||||||
|
|
||||||
### HackRF "factory" firmware
|
### HackRF "factory" firmware
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@ -616,7 +641,7 @@ project(baseband)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${PROJECT_NAME}.img
|
OUTPUT ${PROJECT_NAME}.img
|
||||||
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
|
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
|
||||||
DEPENDS ${BASEBAND_IMAGES}
|
DEPENDS ${BASEBAND_IMAGES} ${BASEBAND_EXTERNAL_IMAGES}
|
||||||
DEPENDS hackrf.img terminator.img
|
DEPENDS hackrf.img terminator.img
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
@ -33,6 +33,9 @@ __ram_end__ = __ram_start__ + __ram_size__;
|
|||||||
|
|
||||||
ENTRY(ResetHandler)
|
ENTRY(ResetHandler)
|
||||||
|
|
||||||
|
/* external apps */
|
||||||
|
INCLUDE external.ld
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0;
|
. = 0;
|
||||||
|
54
firmware/common/external_app.hpp
Normal file
54
firmware/common/external_app.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bernd Herzog
|
||||||
|
*
|
||||||
|
* This file is part of PortaPack.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EXTERNAL_APPS_H__
|
||||||
|
#define __EXTERNAL_APPS_H__
|
||||||
|
|
||||||
|
#include "ch.h"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "spi_image.hpp"
|
||||||
|
|
||||||
|
#define CURRENT_HEADER_VERSION 0x00000001
|
||||||
|
|
||||||
|
typedef void (*externalAppEntry_t)(ui::NavigationView& nav);
|
||||||
|
|
||||||
|
enum app_location_t : uint32_t {
|
||||||
|
UTILITIES = 0,
|
||||||
|
RX,
|
||||||
|
TX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct application_information_t {
|
||||||
|
uint8_t* memory_location;
|
||||||
|
externalAppEntry_t externalAppEntry;
|
||||||
|
uint32_t header_version;
|
||||||
|
uint32_t app_version;
|
||||||
|
|
||||||
|
uint8_t app_name[16];
|
||||||
|
uint8_t bitmap_data[32];
|
||||||
|
uint32_t icon_color;
|
||||||
|
app_location_t menu_location;
|
||||||
|
|
||||||
|
portapack::spi_flash::image_tag_t m4_app_tag;
|
||||||
|
uint32_t m4_app_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*__EXTERNAL_APPS_H__*/
|
53
firmware/tools/copy_external_apps.sh
Normal file
53
firmware/tools/copy_external_apps.sh
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023 Bernd Herzog
|
||||||
|
#
|
||||||
|
# This file is part of PortaPack.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This script will copy compiled external apps to the Portapack.
|
||||||
|
|
||||||
|
try=1
|
||||||
|
while [ $try -le 180 ]
|
||||||
|
do
|
||||||
|
if ls /dev/disk/by-id/*Portapack*part1 1> /dev/null 2>&1; then
|
||||||
|
disk=$(ls /dev/disk/by-id/*Portapack*part1)
|
||||||
|
part=$(readlink -f $disk)
|
||||||
|
mountpoint=$(findmnt -f -o TARGET --noheadings $part)
|
||||||
|
if [[ ! -z "$mountpoint" ]]; then
|
||||||
|
|
||||||
|
echo "Copying external applications to" $mountpoint
|
||||||
|
mkdir -p $mountpoint/APPS
|
||||||
|
cp application/*.ppma $mountpoint/APPS
|
||||||
|
|
||||||
|
echo "Unmounting" $mountpoint
|
||||||
|
umount $mountpoint
|
||||||
|
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(( $try %5 ))" -eq "1" ]; then
|
||||||
|
echo "Please start SD over USB app ..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
try=$(( $try + 1 ))
|
||||||
|
done
|
131
firmware/tools/export_external_apps.py
Normal file
131
firmware/tools/export_external_apps.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023 Bernd Herzog
|
||||||
|
#
|
||||||
|
# This file is part of PortaPack.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
usage_message = """
|
||||||
|
PortaPack external app image creator
|
||||||
|
This script is used in the build process and should never be run manually.
|
||||||
|
See firmware/application/CMakeLists.txt > COMMAND ${EXPORT_EXTERNAL_APP_IMAGES}
|
||||||
|
|
||||||
|
Usage: <command> <project source dir> <binary dir> <cmake objcopy path> <list of external image prefixes>
|
||||||
|
"""
|
||||||
|
|
||||||
|
maximum_application_size = 40*1024
|
||||||
|
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
print(usage_message)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
def read_image(path):
|
||||||
|
f = open(path, 'rb')
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_image(data, path):
|
||||||
|
f = open(path, 'wb')
|
||||||
|
f.write(data)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def patch_image(image_data, search_address, replace_address):
|
||||||
|
if (len(image_data) % 4) != 0:
|
||||||
|
print("file size not divideable by 4")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
external_application_image = bytearray()
|
||||||
|
|
||||||
|
for x in range(int(len(image_data)/4)):
|
||||||
|
snippet = image_data[x*4:4*(x+1)]
|
||||||
|
val = int.from_bytes(snippet, byteorder='little')
|
||||||
|
|
||||||
|
# in firmware/application/external/external.ld the origin is set to something like search_address=0xEEE90000
|
||||||
|
# if the value is above the search_address and inside a 40kb window (maximum size of an app) we replace it
|
||||||
|
# with replace_address=(0x1008000 + m4 size) where the app will actually be located. The reason we do this instead just
|
||||||
|
# using the right address in external.ld is gcc does not permit to use the same memory range multiple times.
|
||||||
|
if val > search_address and (val - search_address) < maximum_application_size:
|
||||||
|
relative_address = val - search_address
|
||||||
|
new_address = replace_address + relative_address
|
||||||
|
|
||||||
|
new_snippet = new_address.to_bytes(4, byteorder='little')
|
||||||
|
external_application_image += new_snippet
|
||||||
|
else:
|
||||||
|
external_application_image += snippet
|
||||||
|
|
||||||
|
return external_application_image
|
||||||
|
|
||||||
|
project_source_dir = sys.argv[1] #/portapack-mayhem/firmware/application
|
||||||
|
binary_dir = sys.argv[2] #/portapack-mayhem/build/firmware/application
|
||||||
|
cmake_objcopy = sys.argv[3]
|
||||||
|
|
||||||
|
memory_location_header_position = 0
|
||||||
|
externalAppEntry_header_position = 4
|
||||||
|
m4_app_tag_header_position = 72
|
||||||
|
m4_app_offset_header_position = 76
|
||||||
|
|
||||||
|
for external_image_prefix in sys.argv[4:]:
|
||||||
|
|
||||||
|
# COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}_ext_pacman.bin --only-section=.external_app_pacman
|
||||||
|
himg = "{}/external_app_{}.himg".format(binary_dir, external_image_prefix)
|
||||||
|
subprocess.run([cmake_objcopy, "-v", "-O", "binary", "{}/application.elf".format(binary_dir), himg, "--only-section=.external_app_{}".format(external_image_prefix)])
|
||||||
|
|
||||||
|
external_application_image = read_image(himg)
|
||||||
|
|
||||||
|
#m4 image @ 0x44
|
||||||
|
chunk_data = external_application_image[m4_app_tag_header_position:m4_app_tag_header_position+4]
|
||||||
|
|
||||||
|
# skip m4 if not set
|
||||||
|
if (chunk_data[0] == 0 and chunk_data[1] == 0 and chunk_data[2] == 0 and chunk_data[3] == 0):
|
||||||
|
replace_address = 0x10080000
|
||||||
|
search_address = int.from_bytes(external_application_image[externalAppEntry_header_position:externalAppEntry_header_position+4], byteorder='little') & 0xFFFF0000
|
||||||
|
external_application_image = patch_image(external_application_image, search_address, replace_address)
|
||||||
|
external_application_image[memory_location_header_position:memory_location_header_position+4] = replace_address.to_bytes(4, byteorder='little')
|
||||||
|
|
||||||
|
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
|
||||||
|
continue
|
||||||
|
|
||||||
|
chunk_tag = chunk_data.decode("utf-8")
|
||||||
|
m4_image = read_image("{}/../baseband/{}.bin".format(binary_dir, chunk_tag))
|
||||||
|
app_image_len = len(external_application_image)
|
||||||
|
external_application_image += m4_image
|
||||||
|
|
||||||
|
if (len(m4_image) % 4) != 0:
|
||||||
|
print("m4 file size not divideable by 4")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
replace_address = 0x10080000 + len(m4_image)
|
||||||
|
search_address = int.from_bytes(external_application_image[externalAppEntry_header_position:externalAppEntry_header_position+4], byteorder='little') & 0xFFFF0000
|
||||||
|
external_application_image = patch_image(external_application_image, search_address, replace_address)
|
||||||
|
|
||||||
|
external_application_image[memory_location_header_position:memory_location_header_position+4] = replace_address.to_bytes(4, byteorder='little')
|
||||||
|
external_application_image[m4_app_offset_header_position:m4_app_offset_header_position+4] = app_image_len.to_bytes(4, byteorder='little')
|
||||||
|
|
||||||
|
if (len(external_application_image) > maximum_application_size) != 0:
|
||||||
|
print("application {} can not exceed 40kb: {} bytes used".format(external_image_prefix, len(external_application_image)))
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
# write .ppma (portapack mayhem application)
|
||||||
|
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
|
Loading…
Reference in New Issue
Block a user