Compare commits

...

6 Commits

Author SHA1 Message Date
Totoo a2f6d14b15 ISS for aprs, for the request #1810 (#1814) 2024-01-25 21:10:43 +01:00
Mark Thompson 6a6c6d6502 Add Checksums to Firmware & External App images (#1809)
* Pad image to 1MB and add simple checksum

* Test code to verify firmware checksum

* Comment out unneeded zlib

* Add files via upload

* Print space remaining in ROM

* Append checksum to external apps too

* Check external app checksums when loading

* Is it 2024 already?!

* Validate firmware checksum before flashing

* Add files via upload

* Added flash error warning to nav screen

* Clang

* Replaced some hard-coded values with #defines

* Check FW checksum before USB serial flash too

* Add files via upload
2024-01-24 16:37:21 -06:00
Mark Thompson 2d98c5d311 Resolve Soundboard tone key issue (#1806)
* Resolve Soundboard tone key issue
2024-01-23 20:53:04 +01:00
sommermoregentraum 03b13f8ab0 prevent crash when nothing to broadcast in soundbrd app (#1807)
* prevent crash when nothing to broadcast in soundbrd app

* format
2024-01-22 22:15:37 -06:00
E.T 6e74ad942f Gh workflow changelog formatting fix (#1803)
* Remove replacement of newlines with hex codes

Assuming that its unnecessary as we are no longer outputting these via stdout but pipe it into some binary that could handle newlines in its stdin therefore don't need and support this transformation

* Attempt 2 after reading gh docs :D

* Fix changelog for stable release too
2024-01-22 10:50:14 +01:00
Mark Thompson 0f85f247b6 Fix rounding bug when displaying frequencies (#1801) 2024-01-22 07:54:29 +01:00
30 changed files with 312 additions and 60 deletions
+5 -5
View File
@@ -71,11 +71,11 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CHANGELOG=$(python3 .github/workflows/changelog.py)
CHANGELOG="${CHANGELOG//'%'/'%25'}"
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
echo "content=$CHANGELOG" >> $GITHUB_OUTPUT
{
echo 'content<<EOF'
python3 .github/workflows/changelog.py
echo EOF
} >> "$GITHUB_OUTPUT"
id: changelog
- name: Create Release
id: create_release
+5 -5
View File
@@ -55,11 +55,11 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CHANGELOG=$(python3 .github/workflows/changelog.py ${{ steps.past_version.outputs.past_version }})
CHANGELOG="${CHANGELOG//'%'/'%25'}"
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
echo "content=$CHANGELOG" >> $GITHUB_OUTPUT
{
echo 'content<<EOF'
python3 .github/workflows/changelog.py ${{ steps.past_version.outputs.past_version }}
echo EOF
} >> "$GITHUB_OUTPUT"
id: changelog
- name: Create Release
id: create_release
+18 -3
View File
@@ -80,7 +80,13 @@ void SoundBoardView::file_error() {
}
void SoundBoardView::start_tx(const uint32_t id) {
if (file_list.empty()) {
file_error();
return;
}
auto reader = std::make_unique<WAVFileReader>();
uint32_t tone_key_index = options_tone_key.selected_index();
uint32_t sample_rate;
@@ -110,11 +116,11 @@ void SoundBoardView::start_tx(const uint32_t id) {
// TODO: Delete all this and use tx model.
baseband::set_audiotx_config(
1536000 / 20, // Update vu-meter at 20Hz
TONES_SAMPLERATE / 20, // Update vu-meter at 20Hz
transmitter_model.channel_bandwidth(),
0, // Gain is unused
8, // shift_bits_s16, default 8 bits, but also unused
TONES_F2D(tone_key_frequency(tone_key_index), 1536000),
TONES_F2D(tone_key_frequency(tone_key_index), TONES_SAMPLERATE),
0, // AM
0, // DSB
0, // USB
@@ -126,7 +132,8 @@ void SoundBoardView::start_tx(const uint32_t id) {
tx_view.set_transmitting(true);
audio::output::start();
if (tone_key_index == 0)
audio::output::start();
}
/*void SoundBoardView::show_infos() {
@@ -232,6 +239,7 @@ SoundBoardView::SoundBoardView(
//&text_duration,
//&progressbar,
&field_volume,
&text_volume_disabled,
&page_info,
&check_loop,
&check_random,
@@ -257,6 +265,13 @@ SoundBoardView::SoundBoardView(
tone_keys_populate(options_tone_key);
options_tone_key.set_selected_index(0);
text_volume_disabled.hidden(true);
options_tone_key.on_change = [this](size_t index, OptionsField::value_t) {
bool tone_key_enabled = (index != 0);
text_volume_disabled.hidden(!tone_key_enabled);
field_volume.hidden(tone_key_enabled);
};
check_loop.set_value(false);
check_random.set_value(false);
@@ -128,6 +128,9 @@ class SoundBoardView : public View {
AudioVolumeField field_volume{
{28 * 8, 180}};
Text text_volume_disabled{
{28 * 8, 180, 3 * 8, 16},
"--"};
Checkbox check_loop{
{0, 25 * 8 + 4},
+2
View File
@@ -101,6 +101,8 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
field_frequency.set_value(145175000);
} else if (i == 3) {
field_frequency.set_value(144575000);
} else if (i == 4) {
field_frequency.set_value(145825000);
}
};
+2 -1
View File
@@ -221,7 +221,8 @@ class APRSRxView : public View {
{{"NA ", 0},
{"EUR", 1},
{"AUS", 2},
{"NZ ", 3}}};
{"NZ ", 3},
{"ISS", 4}}};
RxFrequencyField field_frequency{
{3 * 8, 0 * 16},
+28 -1
View File
@@ -30,6 +30,30 @@ static const char16_t* firmware_folder = u"/FIRMWARE";
Thread* FlashUtilityView::thread{nullptr};
static constexpr size_t max_filename_length = 26;
bool valid_firmware_file(std::filesystem::path::string_type path) {
File firmware_file;
uint32_t read_buffer[128];
uint32_t checksum{1};
// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
auto result = firmware_file.open(path.c_str());
if (!result.is_valid()) {
checksum = 0;
for (uint32_t i = FLASH_STARTING_ADDRESS; i < FLASH_ROM_SIZE / sizeof(read_buffer); i++) {
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
// if file is smaller than 1MB, assume it's a downgrade to an old FW version and ignore the checksum
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
checksum = FLASH_EXPECTED_CHECKSUM;
break;
}
checksum += simple_checksum((uint32_t)read_buffer, sizeof(read_buffer));
}
}
return (checksum == FLASH_EXPECTED_CHECKSUM);
}
FlashUtilityView::FlashUtilityView(NavigationView& nav)
: nav_(nav) {
add_children({&labels,
@@ -111,8 +135,11 @@ void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
if (endsWith(path, u".tar")) {
// extract, then update
path = extract_tar(u'/' + path).native();
if (path.empty()) return;
}
if (path.empty() || !valid_firmware_file(path.c_str()))
return; // bad firmware image - just returning back to the file list
ui::Painter painter;
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
@@ -32,8 +32,14 @@
#include "untar.hpp"
#include <cstdint>
#define FLASH_ROM_SIZE 1048576
#define FLASH_STARTING_ADDRESS 0x00000000
#define FLASH_EXPECTED_CHECKSUM 0x00000000
namespace ui {
bool valid_firmware_file(std::filesystem::path::string_type path);
class FlashUtilityView : public View {
public:
FlashUtilityView(NavigationView& nav);
+2 -2
View File
@@ -185,7 +185,7 @@ std::string to_string_freq(const uint64_t f) {
// right-justified frequency in MHz, rounded to 4 decimal places, always 9 characters
std::string to_string_short_freq(const uint64_t f) {
auto final_str = to_string_dec_int(f / 1000000, 4) + "." + to_string_dec_int(((f + 50) / 100) % 10000, 4, '0');
auto final_str = to_string_dec_int((f + 50) / 1000000, 4) + "." + to_string_dec_int(((f + 50) / 100) % 10000, 4, '0');
return final_str;
}
@@ -210,7 +210,7 @@ std::string to_string_rounded_freq(const uint64_t f, int8_t precision) {
uint32_t divisor = pow10[6 - precision];
final_str = to_string_dec_uint(f / 1000000) + "." + to_string_dec_int(((f + (divisor / 2)) / divisor) % pow10[precision], precision, '0');
final_str = to_string_dec_uint((f + (divisor / 2)) / 1000000) + "." + to_string_dec_int(((f + (divisor / 2)) / divisor) % pow10[precision], precision, '0');
}
return final_str;
}
+1 -1
View File
@@ -85,7 +85,7 @@ const tone_key_t tone_keys = {
{"Senn. 32.768k", F2Ix100(32768.0)}};
std::string fx100_string(uint32_t f) {
return to_string_dec_uint(f / 100) + "." + to_string_dec_uint(((f + 5) / 10) % 10);
return to_string_dec_uint((f + 5) / 100) + "." + to_string_dec_uint(((f + 5) / 10) % 10);
}
float tone_key_frequency(tone_index index) {
+2 -1
View File
@@ -498,12 +498,13 @@ void GeoMap::draw_scale(Painter& painter) {
if (m < 1000) {
km_string = to_string_dec_uint(m) + "m";
} else {
m += 50; // (add rounding factor for div by 100 below)
uint32_t km = m / 1000;
m -= km * 1000;
if (m == 0) {
km_string = to_string_dec_uint(km) + " km";
} else {
km_string = to_string_dec_uint(km) + "." + to_string_dec_uint((m + 50) / 100, 1) + "km";
km_string = to_string_dec_uint(km) + "." + to_string_dec_uint(m / 100, 1) + "km";
}
}
@@ -107,6 +107,7 @@ namespace ui {
/* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
File app;
uint32_t checksum{0};
auto openError = app.open(filePath);
if (openError)
@@ -115,7 +116,6 @@ namespace ui {
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
return false;
@@ -135,6 +135,8 @@ namespace ui {
if (!readResult)
return false;
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
if (readResult.value() < std::filesystem::max_file_block_size)
break;
}
@@ -156,6 +158,8 @@ namespace ui {
if (!readResult)
return false;
checksum += simple_checksum((uint32_t)target_memory, readResult.value());
if (readResult.value() != bytes_to_read)
break;
}
@@ -168,11 +172,16 @@ namespace ui {
if (!readResult)
return false;
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
if (readResult.value() < std::filesystem::max_file_block_size)
break;
}
}
if (checksum != EXT_APP_EXPECTED_CHECKSUM)
return false;
application_information.externalAppEntry(nav);
return true;
}
@@ -29,6 +29,8 @@
#include "file.hpp"
#define EXT_APP_EXPECTED_CHECKSUM 0x00000000
namespace ui {
template <size_t Width, size_t Height>
+19 -8
View File
@@ -503,8 +503,8 @@ void SystemStatusView::rtc_battery_workaround() {
month = (timestamp.FAT_date >> 5) & 0xF;
day = timestamp.FAT_date & 0x1F;
// bump to next month at 28 days for simplicity
if (++day > 28) {
// bump to next month
if (++day > rtc_time::days_per_month(year, month)) {
day = 1;
if (++month > 12) {
month = 1;
@@ -547,16 +547,16 @@ InformationView::InformationView(
&ltime});
#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);
version.set_style(&Styles::yellow);
#else
version.set_style(&style_infobar);
#endif
if (firmware_checksum_error()) {
version.set("FLASH ERROR");
version.set_style(&Styles::red);
}
ltime.set_style(&style_infobar);
refresh();
set_dirty();
@@ -568,6 +568,17 @@ void InformationView::refresh() {
ltime.set_date_enabled(pmem::clock_with_date());
}
bool InformationView::firmware_checksum_error() {
static bool fw_checksum_checked{false};
static bool fw_checksum_error{false};
// only checking firmware checksum once per boot
if (!fw_checksum_checked) {
fw_checksum_error = (simple_checksum(FLASH_STARTING_ADDRESS, FLASH_ROM_SIZE) != FLASH_EXPECTED_CHECKSUM);
}
return fw_checksum_error;
}
/* Navigation ************************************************************/
bool NavigationView::is_top() const {
+1
View File
@@ -286,6 +286,7 @@ class InformationView : public View {
public:
InformationView(NavigationView& nav);
void refresh();
bool firmware_checksum_error();
private:
// static constexpr auto version_string = "v1.4.4"; // This is commented out as we are now setting the version via ENV (VERSION_STRING=v1.0.0)
+8 -1
View File
@@ -41,6 +41,7 @@
#include "chprintf.h"
#include "chqueues.h"
#include "ui_external_items_menu_loader.hpp"
#include "ui_flash_utility.hpp"
#include "untar.hpp"
#include "ui_widget.hpp"
@@ -170,7 +171,13 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
} 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");
chprintf(chp, "error only .bin or .ppfw.tar files can be flashed.\r\n");
nav->pop();
return;
}
if (!ui::valid_firmware_file(path.native().c_str())) {
chprintf(chp, "error corrupt firmware file.\r\n");
nav->pop();
return;
}
+6 -3
View File
@@ -215,9 +215,12 @@ void disable() {
gpdma_channel_i2s0_rx.disable();
}
void shrink_tx_buffer() {
single_tx_buffer = true;
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
void shrink_tx_buffer(bool shrink) {
single_tx_buffer = shrink;
if (single_tx_buffer)
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
else
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[1]);
}
buffer_t tx_empty_buffer() {
+1 -1
View File
@@ -47,7 +47,7 @@ void init();
void configure();
void enable();
void disable();
void shrink_tx_buffer();
void shrink_tx_buffer(bool shrink);
audio::buffer_t tx_empty_buffer();
audio::buffer_t rx_empty_buffer();
+36 -16
View File
@@ -32,34 +32,34 @@
#include <cstddef>
#include <array>
void AudioOutput::configure(
const bool do_proc) {
void AudioOutput::configure(const bool do_proc) {
do_processing = do_proc;
}
void AudioOutput::configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
const float squelch_threshold) {
void AudioOutput::configure(const iir_biquad_config_t& hpf_config, const iir_biquad_config_t& deemph_config, const float squelch_threshold) {
hpf.configure(hpf_config);
deemph.configure(deemph_config);
squelch.set_threshold(squelch_threshold);
}
void AudioOutput::write(
const buffer_s16_t& audio) {
void AudioOutput::write_unprocessed(const buffer_s16_t& audio) {
block_buffer_s16.feed(
audio,
[this](const buffer_s16_t& buffer) {
audio_present = true;
fill_audio_buffer(buffer, audio_present);
});
}
void AudioOutput::write(const buffer_s16_t& audio) {
std::array<float, 32> audio_f;
for (size_t i = 0; i < audio.count; i++) {
audio_f[i] = audio.p[i] * ki;
}
write(buffer_f32_t{
audio_f.data(),
audio.count,
audio.sampling_rate});
write(buffer_f32_t{audio_f.data(), audio.count, audio.sampling_rate});
}
void AudioOutput::write(
const buffer_f32_t& audio) {
void AudioOutput::write(const buffer_f32_t& audio) {
block_buffer.feed(
audio,
[this](const buffer_f32_t& buffer) {
@@ -67,8 +67,7 @@ void AudioOutput::write(
});
}
void AudioOutput::on_block(
const buffer_f32_t& audio) {
void AudioOutput::on_block(const buffer_f32_t& audio) {
if (do_processing) {
const auto audio_present_now = squelch.execute(audio);
@@ -93,6 +92,18 @@ bool AudioOutput::is_squelched() {
return !audio_present;
}
void AudioOutput::fill_audio_buffer(const buffer_s16_t& audio, const bool send_to_fifo) {
auto audio_buffer = audio::dma::tx_empty_buffer();
for (size_t i = 0; i < audio_buffer.count; i++) {
audio_buffer.p[i].left = audio_buffer.p[i].right = audio.p[i];
}
if (stream && send_to_fifo) {
stream->write(audio.p, audio_buffer.count * sizeof(int16_t));
}
feed_audio_stats(audio);
}
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
std::array<int16_t, 32> audio_int;
@@ -110,6 +121,15 @@ void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_t
feed_audio_stats(audio);
}
void AudioOutput::feed_audio_stats(const buffer_s16_t& audio) {
audio_stats.feed(
audio,
[](const AudioStatistics& statistics) {
const AudioStatisticsMessage audio_stats_message{statistics};
shared_memory.application_queue.push(audio_stats_message);
});
}
void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
audio_stats.feed(
audio,
+6
View File
@@ -44,6 +44,7 @@ class AudioOutput {
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
const float squelch_threshold = 0.0f);
void write_unprocessed(const buffer_s16_t& audio);
void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
@@ -57,6 +58,7 @@ class AudioOutput {
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
BlockDecimator<int16_t, 32> block_buffer_s16{1};
BlockDecimator<float, 32> block_buffer{1};
IIRBiquadFilter hpf{};
@@ -73,7 +75,11 @@ class AudioOutput {
bool do_processing = true;
void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_s16_t& audio, const bool send_to_fifo);
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
void feed_audio_stats(const buffer_s16_t& audio);
void feed_audio_stats(const buffer_f32_t& audio);
};
@@ -23,6 +23,19 @@
#include "utility.hpp"
void AudioStatsCollector::consume_audio_buffer(const buffer_s16_t& src) {
auto src_p = src.p;
const auto src_end = &src.p[src.count];
while (src_p < src_end) {
const auto sample = *(src_p++);
const auto sample_squared = sample * sample;
squared_sum += sample_squared;
if (sample_squared > max_squared) {
max_squared = sample_squared;
}
}
}
void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) {
auto src_p = src.p;
const auto src_end = &src.p[src.count];
@@ -56,6 +69,12 @@ bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t s
}
}
bool AudioStatsCollector::feed(const buffer_s16_t& src) {
consume_audio_buffer(src);
return update_stats(src.count, src.sampling_rate);
}
bool AudioStatsCollector::feed(const buffer_f32_t& src) {
consume_audio_buffer(src);
@@ -30,6 +30,13 @@
class AudioStatsCollector {
public:
template <typename Callback>
void feed(const buffer_s16_t& src, Callback callback) {
if (feed(src)) {
callback(statistics);
}
}
template <typename Callback>
void feed(const buffer_f32_t& src, Callback callback) {
if (feed(src)) {
@@ -52,10 +59,12 @@ class AudioStatsCollector {
AudioStatistics statistics{};
void consume_audio_buffer(const buffer_s16_t& src);
void consume_audio_buffer(const buffer_f32_t& src);
bool update_stats(const size_t sample_count, const size_t sampling_rate);
bool feed(const buffer_s16_t& src);
bool feed(const buffer_f32_t& src);
bool mute(const size_t sample_count, const size_t sampling_rate);
};
+13 -7
View File
@@ -41,13 +41,17 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer) {
}
}
// Output to speaker too
uint32_t imod32 = i & (AUDIO_OUTPUT_BUFFER_SIZE - 1);
audio_data[imod32] = ((int16_t)audio_sample - 0x80) * (1.0f / 128.0f);
if (imod32 == (AUDIO_OUTPUT_BUFFER_SIZE - 1))
audio_output.write(audio_buffer);
sample = audio_sample - 0x80;
sample = tone_gen.process(audio_sample - 0x80);
// Output to speaker too
if (!tone_key_enabled) {
uint32_t imod32 = i & (AUDIO_OUTPUT_BUFFER_SIZE - 1);
audio_data[imod32] = sample * 256;
if (imod32 == (AUDIO_OUTPUT_BUFFER_SIZE - 1))
audio_output.write_unprocessed(audio_buffer);
}
sample = tone_gen.process(sample);
// FM
delta = sample * fm_delta;
@@ -102,7 +106,9 @@ void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) {
progress_interval_samples = message.divider;
resample_acc = 0;
audio_output.configure(false);
audio::dma::shrink_tx_buffer();
tone_key_enabled = (message.tone_key_delta != 0);
audio::dma::shrink_tx_buffer(!tone_key_enabled);
}
void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) {
+3 -2
View File
@@ -52,14 +52,15 @@ class AudioTXProcessor : public BasebandProcessor {
int32_t sample{0}, delta{};
int8_t re{0}, im{0};
float audio_data[AUDIO_OUTPUT_BUFFER_SIZE];
buffer_f32_t audio_buffer{audio_data, AUDIO_OUTPUT_BUFFER_SIZE, 48000};
int16_t audio_data[AUDIO_OUTPUT_BUFFER_SIZE];
buffer_s16_t audio_buffer{audio_data, AUDIO_OUTPUT_BUFFER_SIZE, 48000};
AudioOutput audio_output{};
size_t progress_interval_samples = 0, progress_samples = 0;
bool configured{false};
uint32_t bytes_read{0};
bool tone_key_enabled{false};
void sample_rate_config(const SampleRateConfigMessage& message);
void audio_config(const AudioTXConfigMessage& message);
@@ -32,6 +32,7 @@
#include "string_format.hpp"
#include "ui_styles.hpp"
#include "ui_painter.hpp"
#include "ui_flash_utility.hpp"
#include "utility.hpp"
#include "rtc_time.hpp"
@@ -1051,6 +1052,9 @@ bool debug_dump() {
pmem_dump_file.write_line("Ext APPS version req'd: 0x" + to_string_hex(VERSION_MD5));
pmem_dump_file.write_line("GCC version: " + to_string_dec_int(__GNUC__) + "." + to_string_dec_int(__GNUC_MINOR__) + "." + to_string_dec_int(__GNUC_PATCHLEVEL__));
// firmware checksum
pmem_dump_file.write_line("Firmware calculated checksum: 0x" + to_string_hex(simple_checksum(FLASH_STARTING_ADDRESS, FLASH_ROM_SIZE), 8));
// write persistent memory
pmem_dump_file.write_line("\n[Persistent Memory]");
+7
View File
@@ -235,3 +235,10 @@ std::string join(char c, std::initializer_list<std::string_view> strings) {
return result;
}
uint32_t simple_checksum(uint32_t buffer_address, uint32_t length) {
uint32_t checksum = 0;
for (uint32_t i = 0; i < length; i += 4)
checksum += *(uint32_t*)(buffer_address + i);
return checksum;
}
+2
View File
@@ -217,4 +217,6 @@ struct range_t {
std::string join(char c, std::initializer_list<std::string_view> strings);
uint32_t simple_checksum(uint32_t buffer_address, uint32_t length);
#endif /*__UTILITY_H__*/
+16
View File
@@ -104,6 +104,14 @@ for external_image_prefix in sys.argv[4:]:
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')
checksum = 0
for i in range(0, len(external_application_image), 4):
checksum += external_application_image[i] + (external_application_image[i + 1] << 8) + (external_application_image[i + 2] << 16) + (external_application_image[i + 3] << 24)
final_checksum = 0
checksum = (final_checksum - checksum) & 0xFFFFFFFF
external_application_image += checksum.to_bytes(4, 'little')
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
continue
@@ -127,5 +135,13 @@ for external_image_prefix in sys.argv[4:]:
print("application {} can not exceed 32kb: {} bytes used".format(external_image_prefix, len(external_application_image)))
sys.exit(-1)
checksum = 0
for i in range(0, len(external_application_image), 4):
checksum += external_application_image[i] + (external_application_image[i + 1] << 8) + (external_application_image[i + 2] << 16) + (external_application_image[i + 3] << 24)
final_checksum = 0
checksum = (final_checksum - checksum) & 0xFFFFFFFF
external_application_image += checksum.to_bytes(4, 'little')
# write .ppma (portapack mayhem application)
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
+19 -2
View File
@@ -74,7 +74,24 @@ for image in images:
padded_data = image['data'] + (spi_image_default_byte * pad_size)
spi_image += padded_data
if len(spi_image) > spi_size:
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image), spi_size))
if len(spi_image) > spi_size - 4:
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
pad_size = spi_size - 4 - len(spi_image)
for i in range(pad_size):
spi_image += spi_image_default_byte
# quick "add up the words" checksum:
checksum = 0
for i in range(0, len(spi_image), 4):
checksum += (spi_image[i] + (spi_image[i + 1] << 8) + (spi_image[i + 2] << 16) + (spi_image[i + 3] << 24))
final_checksum = 0
checksum = (final_checksum - checksum) & 0xFFFFFFFF
spi_image += checksum.to_bytes(4, 'little')
write_image(spi_image, output_path)
percent_remaining = round(1000 * pad_size / spi_size) / 10;
print ("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
#
# Copyright (C) 2024 Mark Thompson
#
# 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 sys
import struct
usage_message = """
PortaPack ROM image checksum checker
Usage: <command> <input-file>
"""
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()
if len(sys.argv) != 2:
print(usage_message)
sys.exit(-1)
image = read_image(sys.argv[1])
image = bytearray(image)
# simple "add up the words" checksum:
checksum = 0
for i in range(0, len(image), 4):
checksum += (image[i] + (image[i + 1] << 8) + (image[i + 2] << 16) + (image[i + 3] << 24))
checksum &= 0xFFFFFFFF
print ("Simple checksum =", checksum)