mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-02 04:49:01 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d93c914dd3 | |||
| 715a2dd448 | |||
| 58bf60695d | |||
| fbe7954f2e | |||
| d122a8fc3b | |||
| 3998dc124a | |||
| 3d2da9c0db | |||
| 3189d3af4c | |||
| 1b3a99cc5e | |||
| 935c43e88f | |||
| 2893c031ab | |||
| 94cdb16ca9 |
+1
-1
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
custom: # https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
|
||||
@@ -56,6 +56,9 @@ jobs:
|
||||
- name: Unzip world map
|
||||
run: |
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
- name: Create Firmware TAR
|
||||
run: |
|
||||
mkdir -p firmware_tar/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin firmware_tar/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p firmware_tar/APPS && cp build/firmware/application/*.ppma firmware_tar/APPS && cd firmware_tar && tar -cvaf ../portapack-mayhem.ppfw.tar *
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
@@ -64,7 +67,7 @@ jobs:
|
||||
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
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/changelog.py)
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
@@ -88,6 +91,16 @@ jobs:
|
||||
${{ steps.changelog.outputs.content }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
- name: Upload Firmware TAR Asset
|
||||
id: upload-firmware-tar-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./portapack-mayhem.ppfw.tar
|
||||
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}.ppfw.tar
|
||||
asset_content_type: application/x-tar
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
@@ -40,6 +40,9 @@ jobs:
|
||||
- name: Unzip world map
|
||||
run: |
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
- name: Create Firmware TAR
|
||||
run: |
|
||||
mkdir -p firmware_tar/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin firmware_tar/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p firmware_tar/APPS && cp build/firmware/application/*.ppma firmware_tar/APPS && cd firmware_tar && tar -cvaf ../portapack-mayhem.ppfw.tar *
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
@@ -48,7 +51,7 @@ jobs:
|
||||
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
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/changelog.py ${{ steps.past_version.outputs.past_version }})
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
@@ -74,13 +77,24 @@ jobs:
|
||||
**Full Changelog**: https://github.com/eried/portapack-mayhem/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
|
||||
|
||||
## Installation
|
||||
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
|
||||
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
|
||||
__Warning:__ Since release 1.8.0, some applications has been moved to the SD card as we ran out of flash space.
|
||||
|
||||
### MicroSD card files
|
||||
|
||||
For certain functionality, like the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
|
||||
For certain functionality, like external apps, the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
|
||||
draft: true
|
||||
prerelease: false
|
||||
- name: Upload Firmware TAR Asset
|
||||
id: upload-firmware-tar-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./portapack-mayhem.ppfw.tar
|
||||
asset_name: mayhem_${{ steps.version.outputs.version }}.ppfw.tar
|
||||
asset_content_type: application/x-tar
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
cache: apt
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
global:
|
||||
- PROJECT_NAME=PortaPack-HAVOC
|
||||
- SHORT_COMMIT_HASH=`git rev-parse --short HEAD`
|
||||
- VERSION_STRING=nightly-$SHORT_COMMIT_HASH
|
||||
- BUILD_DATE="`date +%Y-%m-%d`"
|
||||
- BUILD_NAME="$PROJECT_NAME-$BUILD_DATE-$SHORT_COMMIT_HASH"
|
||||
- ARTEFACT_BASE=$TRAVIS_BUILD_DIR/artefacts/
|
||||
- ARTEFACT_PATH=$ARTEFACT_BASE/$BUILD_NAME
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y
|
||||
- sudo apt-get update -q
|
||||
- sudo apt-get install gcc-arm-embedded -y
|
||||
|
||||
script:
|
||||
# TODO: Introduce top-level Makefile, this is lame.
|
||||
- sed -e "s/\#set(VERSION.*/set(VERSION \"$VERSION_STRING\")/" -i".bak" CMakeLists.txt
|
||||
- mkdir build/
|
||||
- pushd build/
|
||||
- cmake ..
|
||||
- make firmware
|
||||
- popd
|
||||
|
||||
after_success:
|
||||
- mkdir -p $ARTEFACT_PATH
|
||||
# Copy firmware to firmware-bin directory
|
||||
- cd $TRAVIS_BUILD_DIR/build
|
||||
- cp firmware/portapack-h1-havoc.bin $ARTEFACT_PATH/
|
||||
- cp hackrf/firmware/hackrf_usb/hackrf_usb.dfu $ARTEFACT_PATH/
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- cp LICENSE $ARTEFACT_PATH/
|
||||
# Build the archive
|
||||
- cd $ARTEFACT_BASE
|
||||
- tar -cJvf $ARTEFACT_BASE/$BUILD_NAME.tar.xz $BUILD_NAME
|
||||
- md5sum --binary $BUILD_NAME.tar.xz >MD5SUMS
|
||||
- sha256sum --binary $BUILD_NAME.tar.xz >SHA256SUMS
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- coreutils
|
||||
- tar
|
||||
- sed
|
||||
- cmake
|
||||
- dfu-util
|
||||
@@ -25,6 +25,8 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex
|
||||
|
||||
project(portapack-h1)
|
||||
|
||||
set(EXPECTED_GCC_VERSION "9.2.1")
|
||||
|
||||
set(VERSION "$ENV{VERSION_STRING}")
|
||||
if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
execute_process(
|
||||
|
||||
@@ -34,6 +34,17 @@ set(LZ4 lz4)
|
||||
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
|
||||
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
|
||||
|
||||
# In our current build container cmake need a little help to get the version :)
|
||||
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
|
||||
execute_process(COMMAND bash "-c" "arm-none-eabi-g++ -v 2>&1 | grep 'gcc version' | awk '{print $3}'" OUTPUT_VARIABLE CMAKE_CXX_COMPILER_VERSION)
|
||||
string(STRIP ${CMAKE_CXX_COMPILER_VERSION} CMAKE_CXX_COMPILER_VERSION)
|
||||
endif()
|
||||
|
||||
set(GCC_VERSION_MISMATCH 0)
|
||||
if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
|
||||
set(GCC_VERSION_MISMATCH 1)
|
||||
endif()
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(baseband)
|
||||
add_subdirectory(test)
|
||||
@@ -52,6 +63,15 @@ add_custom_target(
|
||||
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
|
||||
)
|
||||
|
||||
if(${GCC_VERSION_MISMATCH})
|
||||
set(COMPILER_MISMATCH_MESSAGE "WARNING: Compiler version mismatch, please use the official compiler version ${EXPECTED_GCC_VERSION} when sharing builds! Current compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(${COMPILER_MISMATCH_MESSAGE})
|
||||
add_custom_command(
|
||||
TARGET firmware POST_BUILD
|
||||
COMMAND echo ${COMPILER_MISMATCH_MESSAGE}
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
program
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf
|
||||
|
||||
@@ -122,7 +122,6 @@ set(CSRC
|
||||
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
|
||||
${HACKRF_PATH}/firmware/common/usb_request.c
|
||||
${HACKRF_PATH}/firmware/common/usb_standard_request.c
|
||||
${CHIBIOS}/os/various/shell.c
|
||||
${CHIBIOS}/os/various/chprintf.c
|
||||
)
|
||||
|
||||
@@ -130,6 +129,7 @@ set(CSRC
|
||||
# setting.
|
||||
set(CPPSRC
|
||||
main.cpp
|
||||
shell.cpp
|
||||
${COMMON}/acars_packet.cpp
|
||||
${COMMON}/adsb.cpp
|
||||
${COMMON}/adsb_frame.cpp
|
||||
@@ -424,7 +424,7 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
|
||||
# List all default C defines here, like -D_DEBUG=1
|
||||
# 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
|
||||
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}")
|
||||
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} -D'GCC_VERSION_MISMATCH=${GCC_VERSION_MISMATCH}'")
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
set(DADEFS)
|
||||
|
||||
@@ -54,24 +54,31 @@ std::string id(ID value) {
|
||||
}
|
||||
|
||||
std::string consumption(Consumption value) {
|
||||
return to_string_dec_uint(value, 10);
|
||||
return to_string_dec_uint(value, 8);
|
||||
}
|
||||
|
||||
std::string commodity_type(CommodityType value) {
|
||||
return to_string_dec_uint(value, 2);
|
||||
}
|
||||
|
||||
std::string tamper_flags(TamperFlags value) {
|
||||
return to_string_hex(value & 0xFFFF, 4); // Note: ignoring bits 32-47 of tamper flags in IDM type due to screen width
|
||||
}
|
||||
|
||||
std::string tamper_flags_scm(TamperFlags value) {
|
||||
return " " + to_string_hex(value & 0x0F, 1) + "/" + to_string_hex(value >> 4, 1); // Physical/Encoder flags
|
||||
}
|
||||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
void ERTLogger::on_packet(const ert::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors + " ID:" + to_string_dec_uint(packet.id(), 1);
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
@@ -81,6 +88,8 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
|
||||
received_count++;
|
||||
|
||||
last_consumption = packet.consumption();
|
||||
last_tamper_flags = packet.tamper_flags();
|
||||
packet_type = packet.type();
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
@@ -91,13 +100,10 @@ void RecentEntriesTable<ERTRecentEntries>::draw(
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style) {
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption);
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " ";
|
||||
|
||||
if (entry.received_count > 999) {
|
||||
line += " +++";
|
||||
} else {
|
||||
line += " " + to_string_dec_uint(entry.received_count, 3);
|
||||
}
|
||||
line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags);
|
||||
line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3);
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
|
||||
@@ -72,11 +72,12 @@ struct ERTRecentEntry {
|
||||
|
||||
ert::ID id{ert::invalid_id};
|
||||
ert::CommodityType commodity_type{ert::invalid_commodity_type};
|
||||
ert::Consumption last_consumption{};
|
||||
ert::TamperFlags last_tamper_flags{};
|
||||
ert::Packet::Type packet_type{};
|
||||
|
||||
size_t received_count{0};
|
||||
|
||||
ert::Consumption last_consumption{};
|
||||
|
||||
ERTRecentEntry(
|
||||
const Key& key)
|
||||
: id{key.id},
|
||||
@@ -137,9 +138,10 @@ class ERTAppView : public View {
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"ID", 10},
|
||||
{"Tp", 2},
|
||||
{"Consumpt", 10},
|
||||
{"Cnt", 3},
|
||||
{"Ty", 2},
|
||||
{"Consumpt", 8},
|
||||
{"Tamp", 4},
|
||||
{"Ct", 2},
|
||||
}};
|
||||
ERTRecentEntriesView recent_entries_view{columns, recent};
|
||||
|
||||
|
||||
@@ -50,6 +50,17 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.ppfw.tar")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::purple(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
||||
@@ -65,7 +76,43 @@ void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path
|
||||
});
|
||||
}
|
||||
|
||||
bool FlashUtilityView::endsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||
if (str.length() >= suffix.length()) {
|
||||
std::u16string endOfString = str.substr(str.length() - suffix.length());
|
||||
return endOfString == suffix;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::string_type path) {
|
||||
//
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
ui::Color::black());
|
||||
painter.draw_string({12, 24}, this->nav_.style(), "Unpacking TAR file...");
|
||||
|
||||
auto res = UnTar::untar(path, [this](const std::string fileName) {
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||
painter.draw_string({0, 60}, this->nav_.style(), fileName);
|
||||
});
|
||||
if (res.string().empty()) {
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||
painter.draw_string({0, 60}, this->nav_.style(), "BAD TAR FILE");
|
||||
chThdSleepMilliseconds(5000);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
||||
if (endsWith(path, u".ppfw.tar")) {
|
||||
// extract, then update
|
||||
path = extract_tar(u'/' + path).native();
|
||||
if (path.empty()) return;
|
||||
}
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "ff.h"
|
||||
#include "baseband_api.hpp"
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "untar.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
@@ -55,8 +55,10 @@ class FlashUtilityView : public View {
|
||||
{0, 2 * 8, 240, 26 * 8},
|
||||
true};
|
||||
|
||||
std::filesystem::path extract_tar(std::filesystem::path::string_type path); // extracts the tar file, and returns the firmware.bin path from it. empty string if no fw
|
||||
void firmware_selected(std::filesystem::path::string_type path);
|
||||
void flash_firmware(std::filesystem::path::string_type path);
|
||||
bool endsWith(const std::u16string& str, const std::u16string& suffix);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -102,7 +102,7 @@ void ReconView::set_loop_config(bool v) {
|
||||
persistent_memory::set_recon_continuous(continuous);
|
||||
}
|
||||
|
||||
void ReconView::recon_stop_recording() {
|
||||
void ReconView::recon_stop_recording(bool exiting) {
|
||||
if (is_recording) {
|
||||
if (field_mode.selected_index_value() == SPEC_MODULATION)
|
||||
button_audio_app.set_text("RAW");
|
||||
@@ -113,14 +113,14 @@ void ReconView::recon_stop_recording() {
|
||||
button_config.set_style(&Styles::white);
|
||||
is_recording = false;
|
||||
// repeater mode
|
||||
if (persistent_memory::recon_repeat_recorded()) {
|
||||
if (!exiting && persistent_memory::recon_repeat_recorded()) {
|
||||
start_repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReconView::clear_freqlist_for_ui_action() {
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
// flag to detect and reload frequency_list
|
||||
@@ -308,9 +308,16 @@ void ReconView::focus() {
|
||||
}
|
||||
|
||||
ReconView::~ReconView() {
|
||||
recon_stop_recording();
|
||||
if (recon_tx) {
|
||||
replay_thread.reset();
|
||||
}
|
||||
|
||||
recon_stop_recording(true);
|
||||
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
|
||||
transmitter_model.disable();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -542,7 +549,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
};
|
||||
|
||||
button_manual_recon.on_select = [this](Button&) {
|
||||
button_remove.set_text("DELETE");
|
||||
button_remove.set_text("<DELETE>");
|
||||
button_add.hidden(false);
|
||||
scanner_mode = false;
|
||||
manual_mode = true;
|
||||
@@ -636,12 +643,12 @@ ReconView::ReconView(NavigationView& nav)
|
||||
scanner_mode = false;
|
||||
button_scanner_mode.set_style(&Styles::blue);
|
||||
button_scanner_mode.set_text("RECON");
|
||||
button_remove.set_text("REMOVE");
|
||||
button_remove.set_text("<REMOVE>");
|
||||
} else {
|
||||
scanner_mode = true;
|
||||
button_scanner_mode.set_style(&Styles::red);
|
||||
button_scanner_mode.set_text("SCAN");
|
||||
button_remove.set_text("DELETE");
|
||||
button_remove.set_text("<DELETE>");
|
||||
}
|
||||
frequency_file_load();
|
||||
if (autostart) {
|
||||
@@ -844,7 +851,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (status != 1) {
|
||||
status = 1;
|
||||
if (wait != 0) {
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
}
|
||||
@@ -1145,7 +1152,7 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
|
||||
return 0;
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (record_view != nullptr) {
|
||||
remove_child(record_view.get());
|
||||
record_view.reset();
|
||||
@@ -1377,10 +1384,31 @@ void ReconView::start_repeat() {
|
||||
repeat_file_error(rawfile, "Can't open file to send to thread");
|
||||
return;
|
||||
}
|
||||
repeat_ready_signal = true;
|
||||
repeat_cur_rep++;
|
||||
// wait for TX if needed (hackish, direct screen update since the UI will be blocked)
|
||||
if (persistent_memory::recon_repeat_delay() > 0) {
|
||||
uint8_t delay = persistent_memory::recon_repeat_delay();
|
||||
Painter p;
|
||||
while (delay > 0) {
|
||||
std::string delay_message = "TX DELAY: " + to_string_dec_uint(delay) + "s";
|
||||
|
||||
// update display information
|
||||
p.fill_rectangle({0, (SCREEN_H / 2) - 16, SCREEN_W, 64}, Color::light_grey());
|
||||
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, Styles::red, delay_message);
|
||||
|
||||
// sleep 1 second
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
// decre delay
|
||||
if (delay > 0)
|
||||
delay = delay - 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ReplayThread starts immediately on construction; must be set before creating.
|
||||
repeat_ready_signal = true;
|
||||
repeat_cur_rep++;
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
/* read_size */ repeat_read_size,
|
||||
|
||||
@@ -110,7 +110,7 @@ class ReconView : public View {
|
||||
void load_persisted_settings();
|
||||
bool recon_save_freq(const std::filesystem::path& path, size_t index, bool warn_if_exists);
|
||||
// placeholder for possible void recon_start_recording();
|
||||
void recon_stop_recording();
|
||||
void recon_stop_recording(bool exiting);
|
||||
|
||||
// Returns true if 'current_index' is in bounds of frequency_list.
|
||||
bool current_is_valid();
|
||||
|
||||
@@ -106,6 +106,7 @@ void ReconSetupViewMore::save() {
|
||||
persistent_memory::set_recon_repeat_nb(field_repeat_nb.value());
|
||||
persistent_memory::set_recon_repeat_amp(checkbox_repeat_amp.value());
|
||||
persistent_memory::set_recon_repeat_gain(field_repeat_gain.value());
|
||||
persistent_memory::set_recon_repeat_delay(field_repeat_delay.value());
|
||||
};
|
||||
|
||||
void ReconSetupViewMain::focus() {
|
||||
@@ -128,7 +129,9 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
&field_repeat_nb,
|
||||
&checkbox_repeat_amp,
|
||||
&text_repeat_gain,
|
||||
&field_repeat_gain});
|
||||
&field_repeat_gain,
|
||||
&text_repeat_delay,
|
||||
&field_repeat_delay});
|
||||
|
||||
// tx options have to be in yellow to inform the users that activating them will make the device transmit
|
||||
checkbox_repeat_recorded.set_style(&Styles::yellow);
|
||||
@@ -137,6 +140,8 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
checkbox_repeat_amp.set_style(&Styles::yellow);
|
||||
text_repeat_gain.set_style(&Styles::yellow);
|
||||
field_repeat_gain.set_style(&Styles::yellow);
|
||||
text_repeat_delay.set_style(&Styles::yellow);
|
||||
field_repeat_delay.set_style(&Styles::yellow);
|
||||
|
||||
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
|
||||
checkbox_load_repeaters.set_value(persistent_memory::recon_load_repeaters());
|
||||
@@ -148,6 +153,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
checkbox_repeat_amp.set_value(persistent_memory::recon_repeat_amp());
|
||||
field_repeat_nb.set_value(persistent_memory::recon_repeat_nb());
|
||||
field_repeat_gain.set_value(persistent_memory::recon_repeat_gain());
|
||||
field_repeat_delay.set_value(persistent_memory::recon_repeat_delay());
|
||||
|
||||
// tx warning modal
|
||||
checkbox_repeat_recorded.on_select = [this, &nav](Checkbox&, bool v) {
|
||||
|
||||
@@ -158,7 +158,7 @@ class ReconSetupViewMore : public View {
|
||||
"nb:"};
|
||||
|
||||
NumberField field_repeat_nb{
|
||||
{18 * 8, 165},
|
||||
{17 * 8, 165},
|
||||
2,
|
||||
{1, 99},
|
||||
1,
|
||||
@@ -171,16 +171,28 @@ class ReconSetupViewMore : public View {
|
||||
"AMP,"};
|
||||
|
||||
Text text_repeat_gain{
|
||||
{10 * 8, 196, 5 * 8, 22},
|
||||
{9 * 8, 196, 5 * 8, 22},
|
||||
"GAIN:"};
|
||||
|
||||
NumberField field_repeat_gain{
|
||||
{16 * 8, 196},
|
||||
{14 * 8, 196},
|
||||
2,
|
||||
{0, 47},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
Text text_repeat_delay{
|
||||
{16 * 8, 196, 8 * 8, 22},
|
||||
", delay:"};
|
||||
|
||||
NumberField field_repeat_delay{
|
||||
{24 * 8, 196},
|
||||
3,
|
||||
{0, 254},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
};
|
||||
|
||||
class ReconSetupView : public View {
|
||||
|
||||
@@ -149,23 +149,10 @@ SetRadioView::SetRadioView(
|
||||
});
|
||||
}
|
||||
|
||||
std::string source_name("---");
|
||||
switch (reference.source) {
|
||||
case ClockManager::ReferenceSource::Xtal:
|
||||
source_name = "HackRF";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::PortaPack:
|
||||
source_name = "PortaPack";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::External:
|
||||
source_name = "External";
|
||||
break;
|
||||
}
|
||||
std::string source_name = clock_manager.get_source();
|
||||
|
||||
value_source.set(source_name);
|
||||
value_source_frequency.set(
|
||||
to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
|
||||
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz");
|
||||
value_source_frequency.set(clock_manager.get_freq());
|
||||
|
||||
// Make these Text controls look like Labels.
|
||||
label_source.set_style(&Styles::light_grey);
|
||||
|
||||
@@ -217,6 +217,27 @@ ClockManager::Reference ClockManager::get_reference() const {
|
||||
return reference;
|
||||
}
|
||||
|
||||
std::string ClockManager::get_source() {
|
||||
std::string source_name("---");
|
||||
switch (reference.source) {
|
||||
case ClockManager::ReferenceSource::Xtal:
|
||||
source_name = "HackRF";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::PortaPack:
|
||||
source_name = "PortaPack";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::External:
|
||||
source_name = "External";
|
||||
break;
|
||||
}
|
||||
return source_name;
|
||||
}
|
||||
|
||||
std::string ClockManager::get_freq() {
|
||||
return to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
|
||||
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz";
|
||||
}
|
||||
|
||||
static void portapack_tcxo_enable() {
|
||||
portapack::io.reference_oscillator(true);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef __CLOCK_MANAGER_H__
|
||||
#define __CLOCK_MANAGER_H__
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
@@ -74,6 +75,8 @@ class ClockManager {
|
||||
uint32_t get_frequency_monitor_measurement_in_hertz();
|
||||
|
||||
Reference get_reference() const;
|
||||
std::string get_source();
|
||||
std::string get_freq();
|
||||
|
||||
void enable_clock_output(bool enable);
|
||||
|
||||
|
||||
@@ -238,6 +238,10 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventDispatcher::emulateTouch(ui::TouchEvent event) {
|
||||
on_touch_event(event);
|
||||
}
|
||||
|
||||
void EventDispatcher::on_touch_event(ui::TouchEvent event) {
|
||||
/* TODO: Capture widget receiving the Start event, send Move and
|
||||
* End events to the same widget.
|
||||
|
||||
@@ -86,6 +86,8 @@ class EventDispatcher {
|
||||
events_flag(EVT_MASK_LOCAL);
|
||||
}
|
||||
|
||||
void emulateTouch(ui::TouchEvent event);
|
||||
|
||||
private:
|
||||
static Thread* thread_event_loop;
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ static void event_loop() {
|
||||
[&event_dispatcher](const Message* const) {
|
||||
event_dispatcher.set_display_sleep(true);
|
||||
}};
|
||||
|
||||
portapack::setEventDispatcherToUSBSerial(&event_dispatcher);
|
||||
event_dispatcher.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -542,4 +542,8 @@ void shutdown(const bool leave_screen_on) {
|
||||
shutdown_base();
|
||||
}
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt) {
|
||||
usb_serial.setEventDispatcher(evt);
|
||||
}
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -68,6 +68,8 @@ bool get_antenna_bias();
|
||||
bool init();
|
||||
void shutdown(const bool leave_screen_on = false);
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt);
|
||||
|
||||
Backlight* backlight();
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.c
|
||||
* @brief Simple CLI shell code.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "shell.hpp"
|
||||
#include "chprintf.h"
|
||||
#include "portapack.hpp"
|
||||
|
||||
/**
|
||||
* @brief Shell termination event source.
|
||||
*/
|
||||
EventSource shell_terminated;
|
||||
|
||||
static char* _strtok(char* str, const char* delim, char** saveptr) {
|
||||
char* token;
|
||||
if (str)
|
||||
*saveptr = str;
|
||||
token = *saveptr;
|
||||
|
||||
if (!token)
|
||||
return NULL;
|
||||
|
||||
token += strspn(token, delim);
|
||||
*saveptr = strpbrk(token, delim);
|
||||
if (*saveptr)
|
||||
*(*saveptr)++ = '\0';
|
||||
|
||||
return *token ? token : NULL;
|
||||
}
|
||||
|
||||
static void usage(BaseSequentialStream* chp, char* p) {
|
||||
chprintf(chp, "Usage: %s\r\n", p);
|
||||
}
|
||||
|
||||
static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
|
||||
while (scp->sc_name != NULL) {
|
||||
chprintf(chp, "%s ", scp->sc_name);
|
||||
scp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("info"));
|
||||
return;
|
||||
}
|
||||
|
||||
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
|
||||
#ifdef CH_COMPILER_NAME
|
||||
chprintf(chp, "Compiler: %s\r\n", CH_COMPILER_NAME);
|
||||
#endif
|
||||
chprintf(chp, "Architecture: %s\r\n", CH_ARCHITECTURE_NAME);
|
||||
#ifdef CH_CORE_VARIANT_NAME
|
||||
chprintf(chp, "Core Variant: %s\r\n", CH_CORE_VARIANT_NAME);
|
||||
#endif
|
||||
#ifdef CH_PORT_INFO
|
||||
chprintf(chp, "Port Info: %s\r\n", CH_PORT_INFO);
|
||||
#endif
|
||||
#ifdef PLATFORM_NAME
|
||||
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
|
||||
#endif
|
||||
#ifdef BOARD_NAME
|
||||
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
|
||||
#endif
|
||||
#ifdef VERSION_STRING
|
||||
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
|
||||
#endif
|
||||
chprintf(chp, "HackRF Board Rev: %s\r\n", hackrf_r9 ? "R9" : "R1-R8");
|
||||
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
|
||||
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
|
||||
#ifdef __DATE__
|
||||
#ifdef __TIME__
|
||||
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cmd_systime(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("systime"));
|
||||
return;
|
||||
}
|
||||
chprintf(chp, "%lu\r\n", (unsigned long)chTimeNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Array of the default commands.
|
||||
*/
|
||||
static ShellCommand local_commands[] = {
|
||||
{"info", cmd_info},
|
||||
{"systime", cmd_systime},
|
||||
{NULL, NULL}};
|
||||
|
||||
static bool_t cmdexec(const ShellCommand* scp, BaseSequentialStream* chp, char* name, int argc, char* argv[]) {
|
||||
while (scp->sc_name != NULL) {
|
||||
if (strcmp(scp->sc_name, name) == 0) {
|
||||
scp->sc_function(chp, argc, argv);
|
||||
return FALSE;
|
||||
}
|
||||
scp++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell thread function.
|
||||
*
|
||||
* @param[in] p pointer to a @p BaseSequentialStream object
|
||||
* @return Termination reason.
|
||||
* @retval RDY_OK terminated by command.
|
||||
* @retval RDY_RESET terminated by reset condition on the I/O channel.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static msg_t shell_thread(void* p) {
|
||||
int n;
|
||||
BaseSequentialStream* chp = ((ShellConfig*)p)->sc_channel;
|
||||
const ShellCommand* scp = ((ShellConfig*)p)->sc_commands;
|
||||
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
|
||||
char* args[SHELL_MAX_ARGUMENTS + 1];
|
||||
|
||||
chRegSetThreadName("shell");
|
||||
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
|
||||
while (TRUE) {
|
||||
chprintf(chp, "ch> ");
|
||||
if (shellGetLine(chp, line, sizeof(line))) {
|
||||
chprintf(chp, "\r\nlogout");
|
||||
break;
|
||||
}
|
||||
lp = _strtok(line, " \t", &tokp);
|
||||
cmd = lp;
|
||||
n = 0;
|
||||
while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
|
||||
if (n >= SHELL_MAX_ARGUMENTS) {
|
||||
chprintf(chp, "too many arguments\r\n");
|
||||
cmd = NULL;
|
||||
break;
|
||||
}
|
||||
args[n++] = lp;
|
||||
}
|
||||
args[n] = NULL;
|
||||
if (cmd != NULL) {
|
||||
if (strcmp(cmd, "exit") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("exit"));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} else if (strcmp(cmd, "help") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("help"));
|
||||
continue;
|
||||
}
|
||||
chprintf(chp, "Commands: help exit ");
|
||||
list_commands(chp, local_commands);
|
||||
if (scp != NULL)
|
||||
list_commands(chp, scp);
|
||||
chprintf(chp, "\r\n");
|
||||
} else if (cmdexec(local_commands, chp, cmd, n, args) &&
|
||||
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
|
||||
chprintf(chp, "%s", cmd);
|
||||
chprintf(chp, " ?\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
shellExit(RDY_OK);
|
||||
/* Never executed, silencing a warning.*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell manager initialization.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellInit(void) {
|
||||
chEvtInit(&shell_terminated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates the shell.
|
||||
* @note Must be invoked from the command handlers.
|
||||
* @note Does not return.
|
||||
*
|
||||
* @param[in] msg shell exit code
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellExit(msg_t msg) {
|
||||
/* Atomically broadcasting the event source and terminating the thread,
|
||||
there is not a chSysUnlock() because the thread terminates upon return.*/
|
||||
chSysLock();
|
||||
chEvtBroadcastI(&shell_terminated);
|
||||
chThdExitS(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spawns a new shell.
|
||||
* @pre @p CH_USE_HEAP and @p CH_USE_DYNAMIC must be enabled.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] size size of the shell working area to be allocated
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
* @retval NULL thread creation failed because memory allocation.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#if CH_USE_HEAP && CH_USE_DYNAMIC
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio) {
|
||||
return chThdCreateFromHeap(NULL, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create statically allocated shell thread.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] wsp pointer to a working area dedicated to the shell thread stack
|
||||
* @param[in] size size of the shell working area
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio) {
|
||||
return chThdCreateStatic(wsp, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a whole line from the input channel.
|
||||
*
|
||||
* @param[in] chp pointer to a @p BaseSequentialStream object
|
||||
* @param[in] line pointer to the line buffer
|
||||
* @param[in] size buffer maximum length
|
||||
* @return The operation status.
|
||||
* @retval TRUE the channel was reset or CTRL-D pressed.
|
||||
* @retval FALSE operation successful.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size) {
|
||||
char* p = line;
|
||||
|
||||
while (TRUE) {
|
||||
char c;
|
||||
|
||||
if (chSequentialStreamRead(chp, (uint8_t*)&c, 1) == 0)
|
||||
return TRUE;
|
||||
if (c == 4) {
|
||||
chprintf(chp, "^D");
|
||||
return TRUE;
|
||||
}
|
||||
if ((c == 8) || (c == 127)) {
|
||||
if (p != line) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
chSequentialStreamPut(chp, 0x20);
|
||||
chSequentialStreamPut(chp, c);
|
||||
p--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == '\r') {
|
||||
chprintf(chp, "\r\n");
|
||||
*p = 0;
|
||||
return FALSE;
|
||||
}
|
||||
if (c < 0x20)
|
||||
continue;
|
||||
if (p < line + size - 1) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
*p++ = (char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.h
|
||||
* @brief Simple CLI shell header.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SHELL_H_
|
||||
#define _SHELL_H_
|
||||
|
||||
/**
|
||||
* @brief Shell maximum input line length.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_LINE_LENGTH) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_LINE_LENGTH 64
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Shell maximum arguments per command.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_ARGUMENTS) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_ARGUMENTS 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Command handler function type.
|
||||
*/
|
||||
typedef void (*shellcmd_t)(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief Custom command entry type.
|
||||
*/
|
||||
typedef struct {
|
||||
const char* sc_name; /**< @brief Command name. */
|
||||
shellcmd_t sc_function; /**< @brief Command function. */
|
||||
} ShellCommand;
|
||||
|
||||
/**
|
||||
* @brief Shell descriptor type.
|
||||
*/
|
||||
typedef struct {
|
||||
BaseSequentialStream* sc_channel; /**< @brief I/O channel associated
|
||||
to the shell. */
|
||||
const ShellCommand* sc_commands; /**< @brief Shell extra commands
|
||||
table. */
|
||||
} ShellConfig;
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern EventSource shell_terminated;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void shellInit(void);
|
||||
void shellExit(msg_t msg);
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio);
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio);
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SHELL_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -441,7 +441,17 @@ InformationView::InformationView(
|
||||
&version,
|
||||
<ime});
|
||||
|
||||
#if GCC_VERSION_MISMATCH
|
||||
static constexpr Style style_gcc_warning{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 33, 33},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
version.set_style(&style_gcc_warning);
|
||||
#else
|
||||
version.set_style(&style_infobar);
|
||||
#endif
|
||||
|
||||
ltime.set_style(&style_infobar);
|
||||
refresh();
|
||||
set_dirty();
|
||||
|
||||
@@ -32,7 +32,7 @@ void USBSerial::dispatch() {
|
||||
|
||||
if (shell_created == false) {
|
||||
shell_created = true;
|
||||
create_shell();
|
||||
create_shell(_eventDispatcher);
|
||||
}
|
||||
|
||||
bulk_out_receive();
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
class EventDispatcher;
|
||||
|
||||
namespace portapack {
|
||||
|
||||
class USBSerial {
|
||||
@@ -32,6 +34,7 @@ class USBSerial {
|
||||
void dispatch();
|
||||
void on_channel_opened();
|
||||
void on_channel_closed();
|
||||
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
|
||||
|
||||
private:
|
||||
void enable_xtal();
|
||||
@@ -43,6 +46,8 @@ class USBSerial {
|
||||
|
||||
bool connected{false};
|
||||
bool shell_created{false};
|
||||
|
||||
EventDispatcher* _eventDispatcher = NULL;
|
||||
};
|
||||
|
||||
} // namespace portapack
|
||||
@@ -27,19 +27,35 @@
|
||||
#include "png_writer.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "jtag_target_gpio.hpp"
|
||||
#include "cpld_max5.hpp"
|
||||
#include "portapack_cpld_data.hpp"
|
||||
#include "crc.hpp"
|
||||
#include "hackrf_cpld_data.hpp"
|
||||
|
||||
#include "usb_serial_io.h"
|
||||
#include "ff.h"
|
||||
#include "chprintf.h"
|
||||
#include "chqueues.h"
|
||||
#include "untar.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <libopencm3/lpc43xx/wwdt.h>
|
||||
|
||||
#define SHELL_WA_SIZE THD_WA_SIZE(1024 * 3)
|
||||
#define palOutputPad(port, pad) (LPC_GPIO->DIR[(port)] |= 1 << (pad))
|
||||
|
||||
static EventDispatcher* _eventDispatcherInstance = NULL;
|
||||
static EventDispatcher* getEventDispatcherInstance() {
|
||||
return _eventDispatcherInstance;
|
||||
}
|
||||
|
||||
// queue handler from ch
|
||||
static msg_t qwait(GenericQueue* qp, systime_t time) {
|
||||
if (TIME_IMMEDIATE == time)
|
||||
@@ -146,6 +162,15 @@ std::filesystem::path path_from_string8(char* path) {
|
||||
return conv.from_bytes(path);
|
||||
}
|
||||
|
||||
bool strEndsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||
if (str.length() >= suffix.length()) {
|
||||
std::u16string endOfString = str.substr(str.length() - suffix.length());
|
||||
return endOfString == suffix;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
|
||||
@@ -153,15 +178,37 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
size_t filename_length = strlen(argv[0]);
|
||||
|
||||
if (!std::filesystem::file_exists(path)) {
|
||||
chprintf(chp, "file not found.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (filename_length + 1) * 2);
|
||||
|
||||
// check file extensions
|
||||
if (strEndsWith(path.native(), u".ppfw.tar")) {
|
||||
// extract tar
|
||||
chprintf(chp, "Extracting TAR file.\r\n");
|
||||
auto res = UnTar::untar(
|
||||
path.native(), [chp](const std::string fileName) {
|
||||
chprintf(chp, fileName.c_str());
|
||||
chprintf(chp, "\r\n");
|
||||
});
|
||||
if (res.empty()) {
|
||||
chprintf(chp, "error bad TAR file.\r\n");
|
||||
return;
|
||||
}
|
||||
path = res; // it will contain the last bin file in tar
|
||||
} else if (strEndsWith(path.native(), u".bin")) {
|
||||
// nothing to do for this case yet.
|
||||
} else {
|
||||
chprintf(chp, "error only .bin or .ppfw.tar files canbe flashed.\r\n");
|
||||
return;
|
||||
}
|
||||
chprintf(chp, "Flashing: ");
|
||||
chprintf(chp, path.string().c_str());
|
||||
chprintf(chp, "\r\n");
|
||||
chThdSleepMilliseconds(50);
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.native().c_str(), (path.native().length() + 1) * 2);
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(50);
|
||||
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
|
||||
@@ -296,6 +343,28 @@ static void cmd_button(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_touch(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
chprintf(chp, "usage: touch x y\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int x = (int)strtol(argv[0], NULL, 10);
|
||||
int y = (int)strtol(argv[1], NULL, 10);
|
||||
if (x < 0 || x > ui::screen_width || y < 0 || y > ui::screen_height) {
|
||||
chprintf(chp, "usage: touch x y\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
if (evtd == NULL) {
|
||||
chprintf(chp, "error\r\n");
|
||||
}
|
||||
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::Start});
|
||||
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::End});
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: ls /\r\n");
|
||||
@@ -476,6 +545,314 @@ static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cpld_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage =
|
||||
"usage: cpld_info <device>\r\n"
|
||||
" supported modes:\r\n"
|
||||
" cpld_info hackrf\r\n"
|
||||
" cpld_info portapack\r\n";
|
||||
if (argc != 1) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(argv[0], "hackrf", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
{
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
hackrf_cpld.prepare_read_eeprom();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
|
||||
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
crc.process_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_eeprom();
|
||||
chprintf(chp, "CPLD eeprom firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
}
|
||||
|
||||
{
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
hackrf_cpld.prepare_read_sram();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_sram(block);
|
||||
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
crc.process_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
|
||||
chprintf(chp, "CPLD sram firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
}
|
||||
|
||||
} else if (strncmp(argv[0], "portapack", 5) == 0) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
portapack::cpld::CPLD cpld{jtag};
|
||||
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
uint32_t idcode = cpld.get_idcode();
|
||||
|
||||
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
|
||||
|
||||
if (idcode == 0x20A50DD) {
|
||||
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
cpld.sample();
|
||||
cpld.bypass();
|
||||
cpld.enable();
|
||||
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
cpld.prepare_read(0x0000);
|
||||
|
||||
for (size_t i = 0; i < 3328; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
cpld.prepare_read(0x0001);
|
||||
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else if (idcode == 0x00025610) {
|
||||
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
|
||||
|
||||
if (cpld.AGM_enter_maintenance_mode() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpld.AGM_enter_read_mode();
|
||||
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
for (size_t i = 0; i < 2048; i++) {
|
||||
uint32_t data = cpld.AGM_read(i);
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
cpld.AGM_exit_maintenance_mode();
|
||||
|
||||
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else {
|
||||
chprintf(chp, "CPLD Model: unknown\r\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
chprintf(chp, usage);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage =
|
||||
"usage: cpld_read <device> <target>\r\n"
|
||||
" device can be: hackrf, portapack\r\n"
|
||||
" target can be: sram (hackrf only), eeprom\r\n";
|
||||
|
||||
if (argc != 2) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(argv[0], "hackrf", 5) == 0) {
|
||||
if (strncmp(argv[1], "eeprom", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
|
||||
hackrf_cpld.prepare_read_eeprom();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
|
||||
|
||||
chprintf(chp, "bank %04X: ", block.id);
|
||||
uint32_t n = 6;
|
||||
uint8_t byte = 0;
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
byte |= bit << (7 - (n % 8));
|
||||
if (n % 8 == 7) {
|
||||
chprintf(chp, "%02X ", byte);
|
||||
byte = 0;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
chprintf(chp, "\r\n");
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_eeprom();
|
||||
}
|
||||
|
||||
else if (strncmp(argv[1], "sram", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
|
||||
hackrf_cpld.prepare_read_sram();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_sram(block);
|
||||
|
||||
chprintf(chp, "bank %04X: ", block.id);
|
||||
uint32_t n = 6;
|
||||
uint8_t byte = 0;
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
byte |= bit << (7 - (n % 8));
|
||||
if (n % 8 == 7) {
|
||||
chprintf(chp, "%02X ", byte);
|
||||
byte = 0;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
chprintf(chp, "\r\n");
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
|
||||
}
|
||||
} else if (strncmp(argv[0], "portapack", 5) == 0) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
portapack::cpld::CPLD cpld{jtag};
|
||||
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
uint32_t idcode = cpld.get_idcode();
|
||||
|
||||
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
|
||||
|
||||
if (idcode == 0x20A50DD) {
|
||||
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
cpld.sample();
|
||||
cpld.bypass();
|
||||
cpld.enable();
|
||||
|
||||
cpld.prepare_read(0x0000);
|
||||
|
||||
for (size_t i = 0; i < 3328; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
chprintf(chp, "%d: 0x%04X\r\n", i, data);
|
||||
}
|
||||
|
||||
cpld.prepare_read(0x0001);
|
||||
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
chprintf(chp, "%d: 0x%04X\r\n", i, data);
|
||||
}
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else if (idcode == 0x00025610) {
|
||||
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
|
||||
|
||||
if (cpld.AGM_enter_maintenance_mode() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpld.AGM_enter_read_mode();
|
||||
|
||||
for (size_t i = 0; i < 2048; i++) {
|
||||
uint32_t data = cpld.AGM_read(i);
|
||||
if (i % 4 == 0)
|
||||
chprintf(chp, "%5d: ", i * 4);
|
||||
|
||||
chprintf(chp, "0x%08X", data);
|
||||
|
||||
if (i % 4 == 3)
|
||||
chprintf(chp, "\r\n");
|
||||
else
|
||||
chprintf(chp, " ");
|
||||
}
|
||||
|
||||
cpld.AGM_exit_maintenance_mode();
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else {
|
||||
chprintf(chp, "CPLD Model: unknown\r\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
chprintf(chp, usage);
|
||||
}
|
||||
}
|
||||
|
||||
static const ShellCommand commands[] = {
|
||||
{"reboot", cmd_reboot},
|
||||
{"dfu", cmd_dfu},
|
||||
@@ -488,6 +865,7 @@ static const ShellCommand commands[] = {
|
||||
{"write_memory", cmd_write_memory},
|
||||
{"read_memory", cmd_read_memory},
|
||||
{"button", cmd_button},
|
||||
{"touch", cmd_touch},
|
||||
{"ls", cmd_sd_list_dir},
|
||||
{"rm", cmd_sd_delete},
|
||||
{"open", cmd_sd_open},
|
||||
@@ -496,12 +874,15 @@ static const ShellCommand commands[] = {
|
||||
{"read", cmd_sd_read},
|
||||
{"write", cmd_sd_write},
|
||||
{"filesize", cmd_sd_filesize},
|
||||
{"cpld_info", cpld_info},
|
||||
{"cpld_read", cmd_cpld_read},
|
||||
{NULL, NULL}};
|
||||
|
||||
static const ShellConfig shell_cfg1 = {
|
||||
(BaseSequentialStream*)&SUSBD1,
|
||||
commands};
|
||||
|
||||
void create_shell() {
|
||||
void create_shell(EventDispatcher* evtd) {
|
||||
_eventDispatcherInstance = evtd;
|
||||
shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
|
||||
}
|
||||
|
||||
@@ -26,4 +26,6 @@
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
void create_shell(void);
|
||||
class EventDispatcher;
|
||||
|
||||
void create_shell(EventDispatcher* evtd);
|
||||
|
||||
@@ -147,9 +147,12 @@ void CPLD::sector_select(const uint16_t id) {
|
||||
}
|
||||
|
||||
bool CPLD::idcode_ok() {
|
||||
return (get_idcode() == idcode);
|
||||
}
|
||||
|
||||
uint32_t CPLD::get_idcode() {
|
||||
shift_ir(instruction_t::IDCODE);
|
||||
const auto idcode_read = jtag.shift_dr(idcode_length, 0);
|
||||
return (idcode_read == idcode);
|
||||
return jtag.shift_dr(idcode_length, 0);
|
||||
}
|
||||
|
||||
std::array<uint16_t, 5> CPLD::read_silicon_id() {
|
||||
@@ -208,6 +211,16 @@ void CPLD::program_block(
|
||||
}
|
||||
}
|
||||
|
||||
void CPLD::prepare_read(uint16_t block) {
|
||||
sector_select(block);
|
||||
shift_ir(instruction_t::ISC_READ);
|
||||
jtag.runtest_tck(93); // 5us
|
||||
}
|
||||
|
||||
uint32_t CPLD::read() {
|
||||
return jtag.shift_dr(16, 0xffff) & 0xfbff;
|
||||
}
|
||||
|
||||
bool CPLD::verify_block(
|
||||
const uint16_t id,
|
||||
const uint16_t* const data,
|
||||
@@ -265,5 +278,59 @@ bool CPLD::is_blank() {
|
||||
return block_0_blank && block_1_blank;
|
||||
}
|
||||
|
||||
bool CPLD::AGM_enter_maintenance_mode() {
|
||||
shift_ir(instruction_t::AGM_STAGE_1);
|
||||
jtag.runtest_tck(100);
|
||||
shift_ir(instruction_t::AGM_STAGE_2);
|
||||
jtag.runtest_tck(100);
|
||||
shift_ir(instruction_t::AGM_STAGE_1);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_SET_REGISTER);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(8, 0x0);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_PROGRAM);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(32, 0x203f0044uL, 0x80000000);
|
||||
|
||||
shift_ir(instruction_t::IDCODE);
|
||||
jtag.runtest_tck(100);
|
||||
auto idcode = jtag.shift_dr(idcode_length, 0);
|
||||
|
||||
return idcode == 0x00025610;
|
||||
}
|
||||
|
||||
void CPLD::AGM_exit_maintenance_mode() {
|
||||
shift_ir(instruction_t::AGM_RESET);
|
||||
jtag.runtest_tck(100);
|
||||
}
|
||||
|
||||
void CPLD::AGM_enter_read_mode() {
|
||||
shift_ir(instruction_t::AGM_SET_REGISTER);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(8, 0xf0);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_READ);
|
||||
jtag.runtest_tck(100);
|
||||
}
|
||||
|
||||
uint32_t CPLD::AGM_encode_address(uint32_t address, uint32_t trailer) {
|
||||
uint32_t p = trailer;
|
||||
for (size_t i = 0; i < 18; i++) {
|
||||
auto address_bit = (address >> i) & 0x01;
|
||||
p |= address_bit << (31 - i);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
uint32_t CPLD::AGM_read(uint32_t address) {
|
||||
auto encoded_address = AGM_encode_address(address * 4, 0xC0);
|
||||
return jtag.shift_dr(32, encoded_address, 0x0);
|
||||
}
|
||||
|
||||
} /* namespace max5 */
|
||||
} /* namespace cpld */
|
||||
|
||||
@@ -60,6 +60,7 @@ class CPLD {
|
||||
}
|
||||
|
||||
bool idcode_ok();
|
||||
uint32_t get_idcode();
|
||||
|
||||
void enable();
|
||||
|
||||
@@ -90,6 +91,15 @@ class CPLD {
|
||||
|
||||
std::pair<bool, uint8_t> boundary_scan();
|
||||
|
||||
void prepare_read(uint16_t block);
|
||||
uint32_t read();
|
||||
|
||||
bool AGM_enter_maintenance_mode();
|
||||
void AGM_exit_maintenance_mode();
|
||||
void AGM_enter_read_mode();
|
||||
uint32_t AGM_encode_address(uint32_t address, uint32_t trailer);
|
||||
uint32_t AGM_read(uint32_t address);
|
||||
|
||||
private:
|
||||
using idcode_t = uint32_t;
|
||||
static constexpr size_t idcode_length = 32;
|
||||
@@ -115,6 +125,13 @@ class CPLD {
|
||||
ISC_ADDRESS_SHIFT = 0b1000000011, // 0x203
|
||||
ISC_READ = 0b1000000101, // 0x205
|
||||
ISC_NOOP = 0b1000010000, // 0x210
|
||||
AGM_RESET = 0x3f7,
|
||||
AGM_STAGE_1 = 0x3f8,
|
||||
AGM_STAGE_2 = 0x3f9,
|
||||
AGM_PROGRAM = 0x3fa,
|
||||
AGM_SET_REGISTER = 0x3fc,
|
||||
AGM_READ = 0x3fd,
|
||||
AGM_ERASE = 0x3fe,
|
||||
};
|
||||
|
||||
void shift_ir(const instruction_t instruction) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "cpld_xilinx.hpp"
|
||||
|
||||
namespace cpld {
|
||||
@@ -47,6 +48,25 @@ void XC2C64A::write_sram(const verify_blocks_t& blocks) {
|
||||
}
|
||||
|
||||
bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
prepare_read_sram();
|
||||
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
|
||||
auto error = false;
|
||||
for (const auto& block : blocks) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, nullptr);
|
||||
}
|
||||
|
||||
finalize_read_sram(blocks[0].id);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
void XC2C64A::prepare_read_sram() {
|
||||
tap.set_repeat(0);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
@@ -61,17 +81,25 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
tap.shift(empty_row, false);
|
||||
}
|
||||
|
||||
auto error = false;
|
||||
for (const auto& block : blocks) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
std::array<bool, 274> XC2C64A::read_block_sram(verify_block_t block) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false);
|
||||
}
|
||||
// Redundant operation to finish the row.
|
||||
tap.shift({&blocks[0].id, block_id_length}, true);
|
||||
tap.state(state_t::shift_dr);
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
std::vector<bool> from_device;
|
||||
|
||||
tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, &from_device);
|
||||
|
||||
std::array<bool, block_length> ret;
|
||||
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XC2C64A::finalize_read_sram(block_id_t id) {
|
||||
tap.shift({&id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
|
||||
@@ -79,8 +107,6 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
bypass();
|
||||
|
||||
tap.state(state_t::test_logic_reset);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool XC2C64A::verify_eeprom(const verify_blocks_t& blocks) {
|
||||
@@ -133,6 +159,44 @@ void XC2C64A::init_from_eeprom() {
|
||||
tap.state(state_t::test_logic_reset);
|
||||
}
|
||||
|
||||
void XC2C64A::prepare_read_eeprom() {
|
||||
tap.set_repeat(0);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
|
||||
reset();
|
||||
bypass();
|
||||
enable();
|
||||
|
||||
shift_ir(instruction_t::ISC_READ);
|
||||
}
|
||||
|
||||
std::array<bool, 274> XC2C64A::read_block_eeprom(block_id_t id) {
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
|
||||
tap.set_end_dr(state_t::pause_dr);
|
||||
tap.shift_dr({&id, block_id_length});
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.wait(state_t::pause_dr, state_t::pause_dr, 20);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
|
||||
|
||||
std::vector<bool> from_device = tap.shift_dr_read(empty_row);
|
||||
|
||||
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
|
||||
|
||||
std::array<bool, block_length> ret;
|
||||
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XC2C64A::finalize_read_eeprom() {
|
||||
disable();
|
||||
bypass();
|
||||
|
||||
tap.state(state_t::test_logic_reset);
|
||||
}
|
||||
|
||||
bool XC2C64A::shift_ir(const instruction_t instruction) {
|
||||
const ir_t ir_buffer = toUType(instruction);
|
||||
const jtag::tap::bits_t bits{&ir_buffer, ir_length};
|
||||
|
||||
@@ -68,6 +68,13 @@ class XC2C64A {
|
||||
bool verify_eeprom(const verify_blocks_t& blocks);
|
||||
void init_from_eeprom();
|
||||
|
||||
void prepare_read_eeprom();
|
||||
void prepare_read_sram();
|
||||
std::array<bool, block_length> read_block_eeprom(block_id_t id);
|
||||
std::array<bool, block_length> read_block_sram(verify_block_t block);
|
||||
void finalize_read_eeprom();
|
||||
void finalize_read_sram(block_id_t id);
|
||||
|
||||
private:
|
||||
static constexpr size_t idcode_length = 32;
|
||||
using idcode_t = uint32_t;
|
||||
|
||||
@@ -82,6 +82,19 @@ CommodityType Packet::commodity_type() const {
|
||||
return invalid_commodity_type;
|
||||
}
|
||||
|
||||
TamperFlags Packet::tamper_flags() const {
|
||||
if (type() == Type::SCM) {
|
||||
return (reader_.read(9, 2) << 4) | reader_.read(3, 2); // Physical/Encoder tamper flags in lower/upper nibbles
|
||||
}
|
||||
if (type() == Type::SCMPLUS) {
|
||||
return reader_.read(10 * 8, 16);
|
||||
}
|
||||
if (type() == Type::IDM) {
|
||||
return reader_.read(11 * 8, 48);
|
||||
}
|
||||
return invalid_tamper_flags;
|
||||
}
|
||||
|
||||
FormattedSymbols Packet::symbols_formatted() const {
|
||||
return format_symbols(decoder_);
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ namespace ert {
|
||||
using ID = uint32_t;
|
||||
using Consumption = uint32_t;
|
||||
using CommodityType = uint32_t;
|
||||
using TamperFlags = uint32_t;
|
||||
|
||||
constexpr ID invalid_id = 0;
|
||||
constexpr CommodityType invalid_commodity_type = -1;
|
||||
constexpr Consumption invalid_consumption = 0;
|
||||
constexpr TamperFlags invalid_tamper_flags = 0;
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
@@ -67,6 +69,7 @@ class Packet {
|
||||
ID id() const;
|
||||
CommodityType commodity_type() const;
|
||||
Consumption consumption() const;
|
||||
TamperFlags tamper_flags() const;
|
||||
|
||||
FormattedSymbols symbols_formatted() const;
|
||||
|
||||
|
||||
@@ -37,4 +37,15 @@ uint32_t JTAG::shift(const size_t count, uint32_t value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t JTAG::shift_header(const size_t count, uint32_t value) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const auto tdo = target.clock(
|
||||
0,
|
||||
value & 1);
|
||||
value >>= 1;
|
||||
value |= tdo << (count - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} /* namespace jtag */
|
||||
|
||||
@@ -89,10 +89,29 @@ class JTAG {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t shift_dr(const size_t count, const uint32_t address, const uint32_t value) {
|
||||
/* Run-Test/Idle -> Select-DR-Scan */
|
||||
target.clock(1, 0);
|
||||
/* Scan -> Capture -> Shift */
|
||||
target.clock(0, 0);
|
||||
target.clock(0, 0);
|
||||
|
||||
shift_header(count, address);
|
||||
const auto result = shift(count, value);
|
||||
|
||||
/* Exit1 -> Update */
|
||||
target.clock(1, 0);
|
||||
/* Update -> Run-Test/Idle */
|
||||
target.clock(0, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Target& target;
|
||||
|
||||
uint32_t shift(const size_t count, uint32_t value);
|
||||
uint32_t shift_header(const size_t count, uint32_t value);
|
||||
};
|
||||
|
||||
} /* namespace jtag */
|
||||
|
||||
@@ -191,6 +191,10 @@ bool TAPMachine::shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected, c
|
||||
return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test);
|
||||
}
|
||||
|
||||
std::vector<bool> TAPMachine::shift_dr_read(const bits_t& tdi_value) {
|
||||
return shift_data_read(tdi_value, state_t::shift_dr, _end_dr, _run_test);
|
||||
}
|
||||
|
||||
void TAPMachine::state(const state_t state) {
|
||||
if (state == state_t::test_logic_reset) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -227,7 +231,7 @@ void TAPMachine::shift_start(const state_t state) {
|
||||
advance_to_state(state);
|
||||
}
|
||||
|
||||
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms) {
|
||||
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device) {
|
||||
if (tdo_expected.length() != tdo_mask.length()) {
|
||||
return false;
|
||||
}
|
||||
@@ -239,6 +243,10 @@ bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits
|
||||
for (uint32_t i = 0; i < tdi.length(); i++) {
|
||||
const auto tms = end_tms & (i == (tdi.length() - 1));
|
||||
const auto tdo = clock(tms, tdi[i]);
|
||||
|
||||
if (from_device != nullptr)
|
||||
from_device->push_back(tdo);
|
||||
|
||||
if (tdo_expected && tdo_mask) {
|
||||
tdo_error |= (tdo & tdo_mask[i]) != (tdo_expected[i] & tdo_mask[i]);
|
||||
}
|
||||
@@ -258,7 +266,15 @@ void TAPMachine::shift_end(const state_t end_state, const uint32_t end_delay) {
|
||||
|
||||
bool TAPMachine::shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay) {
|
||||
shift_start(state);
|
||||
const auto result = shift(tdi, tdo_expected, tdo_mask, true);
|
||||
const auto result = shift(tdi, tdo_expected, tdo_mask, true, nullptr);
|
||||
shift_end(end_state, end_delay);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<bool> TAPMachine::shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay) {
|
||||
shift_start(state);
|
||||
std::vector<bool> result;
|
||||
shift(tdi, {}, {}, true, &result);
|
||||
shift_end(end_state, end_delay);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace jtag {
|
||||
namespace tap {
|
||||
@@ -113,13 +114,14 @@ class TAPMachine {
|
||||
void set_end_dr(const state_t state);
|
||||
|
||||
bool shift(const bits_t& tdi, const bool end_tms) {
|
||||
return shift(tdi, {}, {}, end_tms);
|
||||
return shift(tdi, {}, {}, end_tms, nullptr);
|
||||
}
|
||||
|
||||
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms);
|
||||
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device);
|
||||
|
||||
bool shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
|
||||
bool shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
|
||||
std::vector<bool> shift_dr_read(const bits_t& tdi_value);
|
||||
|
||||
void state(const state_t state);
|
||||
|
||||
@@ -142,6 +144,7 @@ class TAPMachine {
|
||||
void shift_end(const state_t end_state, const uint32_t end_delay);
|
||||
|
||||
bool shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay);
|
||||
std::vector<bool> shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay);
|
||||
};
|
||||
|
||||
} /* namespace tap */
|
||||
|
||||
@@ -197,6 +197,7 @@ struct data_t {
|
||||
uint64_t recon_config;
|
||||
int8_t recon_repeat_nb;
|
||||
int8_t recon_repeat_gain;
|
||||
uint8_t recon_repeat_delay;
|
||||
|
||||
// enable or disable converter
|
||||
bool converter;
|
||||
@@ -263,6 +264,7 @@ struct data_t {
|
||||
recon_config(0),
|
||||
recon_repeat_nb(0),
|
||||
recon_repeat_gain(0),
|
||||
recon_repeat_delay(0),
|
||||
|
||||
converter(false),
|
||||
updown_converter(false),
|
||||
@@ -405,6 +407,7 @@ void defaults() {
|
||||
set_recon_repeat_amp(false);
|
||||
set_recon_repeat_gain(35);
|
||||
set_recon_repeat_nb(3);
|
||||
set_recon_repeat_delay(1);
|
||||
|
||||
set_config_sdcard_high_speed_io(false, true);
|
||||
}
|
||||
@@ -766,6 +769,9 @@ int8_t recon_repeat_nb() {
|
||||
int8_t recon_repeat_gain() {
|
||||
return data->recon_repeat_gain;
|
||||
}
|
||||
uint8_t recon_repeat_delay() {
|
||||
return data->recon_repeat_delay;
|
||||
}
|
||||
bool recon_repeat_amp() {
|
||||
return (data->recon_config & 0x00100000UL) ? true : false;
|
||||
}
|
||||
@@ -812,6 +818,9 @@ void set_recon_repeat_nb(const int8_t v) {
|
||||
void set_recon_repeat_gain(const int8_t v) {
|
||||
data->recon_repeat_gain = v;
|
||||
}
|
||||
void set_recon_repeat_delay(const uint8_t v) {
|
||||
data->recon_repeat_delay = v;
|
||||
}
|
||||
void set_recon_repeat_amp(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00100000UL) | (v << 20);
|
||||
}
|
||||
@@ -1028,6 +1037,9 @@ bool debug_dump() {
|
||||
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix));
|
||||
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config));
|
||||
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(data->recon_repeat_nb, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_gain:" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_delay:" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter));
|
||||
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter));
|
||||
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction));
|
||||
|
||||
@@ -255,6 +255,7 @@ int8_t recon_repeat_gain();
|
||||
bool recon_repeat_amp();
|
||||
bool recon_load_hamradios();
|
||||
bool recon_match_mode();
|
||||
uint8_t recon_repeat_delay();
|
||||
void set_recon_autosave_freqs(const bool v);
|
||||
void set_recon_autostart_recon(const bool v);
|
||||
void set_recon_continuous(const bool v);
|
||||
@@ -270,6 +271,7 @@ void set_recon_repeat_amp(const bool v);
|
||||
void set_recon_load_hamradios(const bool v);
|
||||
void set_recon_load_repeaters(const bool v);
|
||||
void set_recon_match_mode(const bool v);
|
||||
void set_recon_repeat_delay(const uint8_t v);
|
||||
|
||||
/* UI Config 2 */
|
||||
bool ui_hide_speaker();
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#ifndef __UNTAR
|
||||
#define __UNTAR
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "string_format.hpp"
|
||||
#include "file.hpp"
|
||||
|
||||
class UnTar {
|
||||
public:
|
||||
static std::filesystem::path untar(std::u16string tar, std::function<void(const std::string)> cb = NULL) {
|
||||
File tf;
|
||||
auto result = tf.open(tar, true, false);
|
||||
if (!result.value().ok()) return "";
|
||||
return untar_int(&tf, cb);
|
||||
}
|
||||
|
||||
private:
|
||||
static int parseoct(const char* p, size_t n) {
|
||||
int i = 0;
|
||||
while (*p < '0' || *p > '7') {
|
||||
++p;
|
||||
--n;
|
||||
}
|
||||
while (*p >= '0' && *p <= '7' && n > 0) {
|
||||
i *= 8;
|
||||
i += *p - '0';
|
||||
++p;
|
||||
--n;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static bool is_end_of_archive(const char* p) {
|
||||
for (int n = 511; n >= 0; --n)
|
||||
if (p[n] != '\0') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t strnlen(const char* s, size_t maxlen) {
|
||||
for (size_t i = 0; i < maxlen; i++) {
|
||||
if (s[i] == '\0')
|
||||
return i;
|
||||
}
|
||||
return maxlen;
|
||||
}
|
||||
|
||||
static bool isValidName(char* name) {
|
||||
size_t pathStrLen = strnlen(name, 100);
|
||||
if (pathStrLen == 0 || pathStrLen >= 100) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_dir(char* pathname) {
|
||||
char* p;
|
||||
std::filesystem::filesystem_error r;
|
||||
|
||||
if (!isValidName(pathname)) return false;
|
||||
/* Strip trailing '/' */
|
||||
if (pathname[strlen(pathname) - 1] == '/')
|
||||
pathname[strlen(pathname) - 1] = '\0';
|
||||
|
||||
/* Try creating the directory. */
|
||||
std::string dirnameStr = u'/' + pathname;
|
||||
std::filesystem::path dirname = dirnameStr;
|
||||
|
||||
r = make_new_directory(dirname);
|
||||
|
||||
if (!r.ok()) {
|
||||
/* On failure, try creating parent directory. */
|
||||
p = strrchr(pathname, '/');
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
create_dir(pathname);
|
||||
*p = '/';
|
||||
r = make_new_directory(dirname);
|
||||
}
|
||||
}
|
||||
return (r.ok());
|
||||
}
|
||||
|
||||
static bool verify_checksum(const char* p) {
|
||||
int n, u = 0;
|
||||
for (n = 0; n < 512; ++n) {
|
||||
if (n < 148 || n > 155)
|
||||
/* Standard tar checksum adds unsigned bytes. */
|
||||
u += ((unsigned char*)p)[n];
|
||||
else
|
||||
u += 0x20;
|
||||
}
|
||||
return (u == parseoct(p + 148, 8));
|
||||
}
|
||||
|
||||
static std::filesystem::path untar_int(File* a, std::function<void(const std::string)> cb = NULL) {
|
||||
char buff[512];
|
||||
UINT bytes_read;
|
||||
std::string binfile = "";
|
||||
std::string fn = "";
|
||||
int filesize;
|
||||
for (;;) {
|
||||
auto readres = a->read(buff, 512);
|
||||
if (!readres.is_ok()) return "";
|
||||
bytes_read = readres.value();
|
||||
if (bytes_read < 512) {
|
||||
return "";
|
||||
}
|
||||
if (is_end_of_archive(buff)) {
|
||||
return binfile;
|
||||
}
|
||||
if (!verify_checksum(buff)) {
|
||||
return "";
|
||||
}
|
||||
filesize = parseoct(buff + 124, 12);
|
||||
switch (buff[156]) {
|
||||
case '1':
|
||||
break;
|
||||
case '2':
|
||||
break;
|
||||
case '3':
|
||||
break;
|
||||
case '4':
|
||||
break;
|
||||
case '5':
|
||||
create_dir(buff);
|
||||
filesize = 0;
|
||||
break;
|
||||
case '6':
|
||||
break;
|
||||
default:
|
||||
if (isValidName(buff)) {
|
||||
fn = buff;
|
||||
if (fn.length() > 5 && fn.substr(fn.length() - 4) == ".bin") {
|
||||
binfile = fn;
|
||||
}
|
||||
if (cb != NULL) cb(fn);
|
||||
} else {
|
||||
return ""; // bad tar
|
||||
}
|
||||
break;
|
||||
}
|
||||
File f;
|
||||
if (filesize > 0) {
|
||||
delete_file(fn);
|
||||
auto fres = f.open(fn, false, true);
|
||||
if (!fres.value().ok()) return "";
|
||||
}
|
||||
while (filesize > 0) {
|
||||
readres = a->read(buff, 512);
|
||||
if (!readres.is_ok()) return "";
|
||||
bytes_read = readres.value();
|
||||
if (bytes_read < 512) {
|
||||
return "";
|
||||
}
|
||||
if (filesize < 512)
|
||||
bytes_read = filesize;
|
||||
auto fwres = f.write(buff, bytes_read);
|
||||
if (!fwres.is_ok()) return "";
|
||||
filesize -= bytes_read;
|
||||
f.sync();
|
||||
}
|
||||
}
|
||||
return binfile;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
f=132475000,m=AM,bw=DSB 6k,d=ATIS - Civil
|
||||
f=130550000,m=AM,bw=DSB 6k,d=AbelagUnicom - Civil
|
||||
f=118250000,m=AM,bw=DSB 6k,d=Approach- Civil
|
||||
f=120100000,m=AM,bw=DSB 6k,d=Approach - Civil
|
||||
f=122500000,m=AM,bw=DSB 6k,d=Approach Military Crossing
|
||||
f=121950000,m=AM,bw=DSB 6k,d=Clearance Delivery - Civil
|
||||
f=126625000,m=AM,bw=DSB 6k,d=Departure - Civil
|
||||
f=127575000,m=AM,bw=DSB 6k,d=Final 25L - Civil
|
||||
f=129725000,m=AM,bw=DSB 6k,d=Final 25R - Civil
|
||||
f=118050000,m=AM,bw=DSB 6k,d=Ground - Civik
|
||||
f=121700000,m=AM,bw=DSB 6k,d=Ground - Civil
|
||||
f=121875000,m=AM,bw=DSB 6k,d=Ground - Civil
|
||||
f=118600000,m=AM,bw=DSB 6k,d=Tower VHF - Civil
|
||||
f=120775000,m=AM,bw=DSB 6k,d=Tower - Civil
|
||||
f=127150000,m=AM,bw=DSB 6k,d=Tower Spare - Civil
|
||||
f=240400000,m=AM,bw=DSB 6k,d=Air to Air - Military
|
||||
f=241000000,m=AM,bw=DSB 6k,d=Air to Air - Military
|
||||
f=289400000,m=AM,bw=DSB 6k,d=Military Approach
|
||||
f=362300000,m=AM,bw=DSB 6k,d=Approach Military Crossing - Military
|
||||
f=389300000,m=AM,bw=DSB 6k,d=Approach Military Crossing - Military
|
||||
f=138250000,m=AM,bw=DSB 6k,d=Inflight Safety - Military
|
||||
f=140575000,m=AM,bw=DSB 6k,d=Melsbroek Ops - Military
|
||||
f=367950000,m=AM,bw=DSB 6k,d=Melsbroek Ops - Military
|
||||
f=257800000,m=AM,bw=DSB 6k,d=Tower - Military
|
||||
f=388500000,m=AM,bw=DSB 6k,d=Tower - Military
|
||||
f=388525000,m=AM,bw=DSB 6k,d=Tower - Military
|
||||
f=121725000,m=AM,bw=DSB 6k,d=Wing Ops - Military
|
||||
f=336500000,m=AM,bw=DSB 6k,d=Wing Ops UHF - Military
|
||||
Reference in New Issue
Block a user