mirror of
https://github.com/pine64/blisp.git
synced 2025-08-21 02:50:51 +00:00
Compare commits
6 Commits
e45941c45e
...
26cee48869
Author | SHA1 | Date | |
---|---|---|---|
|
26cee48869 | ||
|
027b7d8c51 | ||
|
e598bdb002 | ||
|
a9d27efe4f | ||
|
9e0dabff67 | ||
|
ed68b973a4 |
34
.github/workflows/build.yml
vendored
34
.github/workflows/build.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: "recursive"
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
- name: Build blisp tool
|
- name: Build blisp tool
|
||||||
run: |
|
run: |
|
||||||
@ -47,7 +47,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: "recursive"
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
- name: Build blisp tool
|
- name: Build blisp tool
|
||||||
run: |
|
run: |
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: "recursive"
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
- name: Build blisp tool
|
- name: Build blisp tool
|
||||||
run: |
|
run: |
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: "recursive"
|
||||||
- uses: uraimo/run-on-arch-action@v2
|
- uses: uraimo/run-on-arch-action@v2
|
||||||
name: Build artifact
|
name: Build artifact
|
||||||
id: build
|
id: build
|
||||||
@ -155,3 +155,29 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
artifacts/blisp-*
|
artifacts/blisp-*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
test-linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
- uses: lukka/get-cmake@latest
|
||||||
|
- name: Build blisp tool & unit tests
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DBLISP_BUILD_CLI=ON -DCOMPILE_TESTS=ON
|
||||||
|
cmake --build .
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
for f in $(find . -type f -executable -iname "*_test"); do
|
||||||
|
echo "Running test file $f"
|
||||||
|
"$f"
|
||||||
|
status=$?
|
||||||
|
if [ $status -ne 0 ]; then
|
||||||
|
echo "Test $f failed with exit code $status"
|
||||||
|
exit $status
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -78,3 +78,4 @@ fabric.properties
|
|||||||
build/
|
build/
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.cache/
|
||||||
|
@ -48,13 +48,13 @@ if(BLISP_USE_SYSTEM_LIBRARIES)
|
|||||||
target_link_libraries(libblisp_static PUBLIC Libserialport::Libserialport)
|
target_link_libraries(libblisp_static PUBLIC Libserialport::Libserialport)
|
||||||
target_include_directories(libblisp_obj PUBLIC ${Libserialport_INCLUDE_DIRS})
|
target_include_directories(libblisp_obj PUBLIC ${Libserialport_INCLUDE_DIRS})
|
||||||
else()
|
else()
|
||||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||||
target_sources(libblisp_obj PRIVATE
|
target_sources(libblisp_obj PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/vendor/libserialport/serialport.c
|
${CMAKE_SOURCE_DIR}/vendor/libserialport/serialport.c
|
||||||
${CMAKE_SOURCE_DIR}/vendor/libserialport/timing.c)
|
${CMAKE_SOURCE_DIR}/vendor/libserialport/timing.c)
|
||||||
|
|
||||||
target_include_directories(libblisp_obj PRIVATE ${CMAKE_SOURCE_DIR}/vendor/libserialport)
|
target_include_directories(libblisp_obj PRIVATE ${CMAKE_SOURCE_DIR}/vendor/libserialport)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(libblisp PRIVATE Setupapi.lib)
|
target_link_libraries(libblisp PRIVATE Setupapi.lib)
|
||||||
@ -105,5 +105,21 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
if(COMPILE_TESTS)
|
if(COMPILE_TESTS)
|
||||||
add_subdirectory(tools/blisp/src/cmd/dfu/tests)
|
# Bring in googletest & C++
|
||||||
|
enable_language(CXX)
|
||||||
|
enable_testing()
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
googletest
|
||||||
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
|
GIT_TAG v1.17.0
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
add_library(GTest::GTest INTERFACE IMPORTED)
|
||||||
|
target_link_libraries(GTest::GTest INTERFACE gtest_main)
|
||||||
|
|
||||||
|
add_subdirectory(tools/blisp/src/file_parsers/dfu/tests)
|
||||||
|
add_subdirectory(tools/blisp/src/file_parsers/hex/tests)
|
||||||
endif(COMPILE_TESTS)
|
endif(COMPILE_TESTS)
|
||||||
|
@ -94,6 +94,15 @@ Because this is done at the lowest level of serial communication, the
|
|||||||
displays aren't packet-aware or know about the chip's command set or such.
|
displays aren't packet-aware or know about the chip's command set or such.
|
||||||
This is really only useful for debugging systems-level issues withing
|
This is really only useful for debugging systems-level issues withing
|
||||||
the device or blisp itself.
|
the device or blisp itself.
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DBLISP_BUILD_CLI=ON -DCOMPILE_TESTS=ON ..
|
||||||
|
cmake --build .
|
||||||
|
# Find all compiled unit test files; you can now run these directly
|
||||||
|
find . -type f -executable -iname "*_test" -print
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
list(APPEND ADD_INCLUDE
|
list(APPEND ADD_INCLUDE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/bin"
|
"${CMAKE_CURRENT_SOURCE_DIR}/bin"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/dfu"
|
"${CMAKE_CURRENT_SOURCE_DIR}/dfu"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hex"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ add_library(file_parsers STATIC
|
|||||||
"${CMAKE_CURRENT_SOURCE_DIR}/bin/bin_file.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/bin/bin_file.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hex/hex_file.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/get_file_contents.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/get_file_contents.c"
|
||||||
)
|
)
|
||||||
@ -19,6 +21,7 @@ add_library(file_parsers STATIC
|
|||||||
target_include_directories(file_parsers PUBLIC
|
target_include_directories(file_parsers PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/bin
|
${CMAKE_CURRENT_SOURCE_DIR}/bin
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/dfu
|
${CMAKE_CURRENT_SOURCE_DIR}/dfu
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/hex
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -78,6 +78,7 @@ int dfu_file_parse(const char* file_path_on_disk,
|
|||||||
size_t* payload_address) {
|
size_t* payload_address) {
|
||||||
uint8_t* dfu_file_contents = NULL;
|
uint8_t* dfu_file_contents = NULL;
|
||||||
ssize_t file_size = get_file_contents(file_path_on_disk, &dfu_file_contents);
|
ssize_t file_size = get_file_contents(file_path_on_disk, &dfu_file_contents);
|
||||||
|
// Bubble up the result if it was an error instead of size (a negative value)
|
||||||
if (file_size < 0) {
|
if (file_size < 0) {
|
||||||
return file_size;
|
return file_size;
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,10 @@
|
|||||||
enable_language(CXX)
|
add_executable(dfu_file_test test_dfu_file.cpp ../dfu_file.c ../dfu_crc.c ../../get_file_contents.c)
|
||||||
|
|
||||||
enable_testing()
|
|
||||||
include(FetchContent)
|
|
||||||
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
googletest
|
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
GIT_TAG release-1.11.0
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(googletest)
|
|
||||||
add_library(GTest::GTest INTERFACE IMPORTED)
|
|
||||||
target_link_libraries(GTest::GTest INTERFACE gtest_main)
|
|
||||||
|
|
||||||
|
|
||||||
add_executable(dfu_file_test test_dfu_file.cpp ../dfu_file.c ../dfu_crc.c)
|
|
||||||
|
|
||||||
target_link_libraries(dfu_file_test
|
target_link_libraries(dfu_file_test
|
||||||
PRIVATE
|
PRIVATE
|
||||||
GTest::GTest
|
GTest::GTest
|
||||||
)
|
)
|
||||||
include_directories(dfu_file_test PRIVATE ../)
|
include(GoogleTest)
|
||||||
add_test(dfu_file_test_gtests dfu_file_test)
|
include_directories(dfu_file_test PRIVATE ../ ../../)
|
||||||
|
target_compile_definitions(dfu_file_test PUBLIC "SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\"")
|
||||||
configure_file(Config.h.in ${CMAKE_BINARY_DIR}/Config.h)
|
gtest_discover_tests(dfu_file_test)
|
||||||
include_directories(${CMAKE_BINARY_DIR})
|
|
||||||
|
|
||||||
set(TEST_APP_NAME dfu_file_tests)
|
|
||||||
|
|
||||||
#add_custom_command(TARGET ${TEST_APP_NAME} COMMAND ./${TEST_APP_NAME} POST_BUILD)
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by ralim on 26/09/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BLISP_CONFIG_H_IN_H
|
|
||||||
#define BLISP_CONFIG_H_IN_H
|
|
||||||
|
|
||||||
#define SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
|
|
||||||
#endif // BLISP_CONFIG_H_IN_H
|
|
@ -2,16 +2,16 @@
|
|||||||
// Created by ralim on 26/09/22.
|
// Created by ralim on 26/09/22.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "dfu_file.h"
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include "dfu_file.h"
|
||||||
TEST(DFU_FILE_PARSER, ParseTestFile) {
|
TEST(DFU_FILE_PARSER, ParseTestFile) {
|
||||||
uint8_t* payload = nullptr;
|
uint8_t* payload = nullptr;
|
||||||
size_t payload_size = 0;
|
size_t payload_size = 0;
|
||||||
size_t payload_address = 0;
|
size_t payload_address = 0;
|
||||||
int res = dfu_file_parse(SOURCE_DIR "/test.dfu", &payload, &payload_size,
|
int res = dfu_file_parse(SOURCE_DIR
|
||||||
&payload_address);
|
"/tools/blisp/src/file_parsers/dfu/tests/test.dfu",
|
||||||
ASSERT_EQ(res, 1);
|
&payload, &payload_size, &payload_address);
|
||||||
ASSERT_EQ(payload_size, 1337);
|
ASSERT_EQ(res, 1);
|
||||||
ASSERT_EQ(payload_address, 0x11223344);
|
ASSERT_EQ(payload_size, 1337);
|
||||||
|
ASSERT_EQ(payload_address, 0x11223344);
|
||||||
}
|
}
|
261
tools/blisp/src/file_parsers/hex/hex_file.c
Normal file
261
tools/blisp/src/file_parsers/hex/hex_file.c
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
#include "hex_file.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "parse_file.h"
|
||||||
|
|
||||||
|
// Convert ASCII hex character to integer
|
||||||
|
static int hex_to_int(char c) {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert 2 ASCII hex characters to a byte
|
||||||
|
static int hex_byte_to_int(const char* str) {
|
||||||
|
int high = hex_to_int(str[0]);
|
||||||
|
int low = hex_to_int(str[1]);
|
||||||
|
if (high < 0 || low < 0)
|
||||||
|
return -1;
|
||||||
|
return (high << 4) | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a single Intel HEX line into the record type and data
|
||||||
|
// Returns: Record Type on success, negative error code on failure
|
||||||
|
static int parse_hex_line(const char* line,
|
||||||
|
uint8_t* data_buffer,
|
||||||
|
uint32_t* address,
|
||||||
|
uint32_t* base_address,
|
||||||
|
uint32_t* max_address,
|
||||||
|
uint32_t min_address) {
|
||||||
|
size_t len = strlen(line);
|
||||||
|
|
||||||
|
// Line must start with ':' and have at least 11 characters (:BBAAAATTCC)
|
||||||
|
if (line[0] != ':' || len < 11) {
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract record fields
|
||||||
|
int byte_count = hex_byte_to_int(line + 1);
|
||||||
|
if (byte_count < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
|
||||||
|
// Make sure line is long enough
|
||||||
|
if (len < (size_t)(11 + byte_count * 2)) {
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int addr_high = hex_byte_to_int(line + 3);
|
||||||
|
int addr_low = hex_byte_to_int(line + 5);
|
||||||
|
if (addr_high < 0 || addr_low < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
|
||||||
|
uint16_t record_address = (addr_high << 8) | addr_low;
|
||||||
|
|
||||||
|
int record_type = hex_byte_to_int(line + 7);
|
||||||
|
if (record_type < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
|
||||||
|
// Verify checksum
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (int i = 1; i < 9 + byte_count * 2; i += 2) {
|
||||||
|
int value = hex_byte_to_int(line + i);
|
||||||
|
if (value < 0) {
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
checksum += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_checksum = hex_byte_to_int(line + 9 + byte_count * 2);
|
||||||
|
if (file_checksum < 0) {
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
checksum = ~checksum + 1; // Two's complement
|
||||||
|
// Verify checksum
|
||||||
|
if (checksum != file_checksum) {
|
||||||
|
return HEX_PARSE_ERROR_CHECKSUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the record based on record type
|
||||||
|
switch (record_type) {
|
||||||
|
case HEX_RECORD_DATA: {
|
||||||
|
uint32_t absolute_address = *base_address + record_address;
|
||||||
|
*address = absolute_address;
|
||||||
|
|
||||||
|
// Update max address if this data extends beyond current max
|
||||||
|
if (absolute_address + byte_count > *max_address) {
|
||||||
|
*max_address = absolute_address + byte_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse data bytes
|
||||||
|
for (int i = 0; i < byte_count; i++) {
|
||||||
|
int value = hex_byte_to_int(line + 9 + i * 2);
|
||||||
|
if (value < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
|
||||||
|
// Make sure we don't write outside our buffer
|
||||||
|
if (data_buffer != NULL) {
|
||||||
|
size_t buf_offset = absolute_address - min_address + i;
|
||||||
|
data_buffer[buf_offset] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HEX_RECORD_EOF:
|
||||||
|
// End of file, nothing to do
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HEX_RECORD_EXTENDED_SEGMENT:
|
||||||
|
// Set segment base address (offset = value * 16)
|
||||||
|
if (byte_count != 2)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
int value_high = hex_byte_to_int(line + 9);
|
||||||
|
int value_low = hex_byte_to_int(line + 11);
|
||||||
|
if (value_high < 0 || value_low < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
*base_address = ((value_high << 8) | value_low) << 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HEX_RECORD_EXTENDED_LINEAR:
|
||||||
|
// Set high-order 16 bits of address
|
||||||
|
if (byte_count != 2)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
value_high = hex_byte_to_int(line + 9);
|
||||||
|
value_low = hex_byte_to_int(line + 11);
|
||||||
|
if (value_high < 0 || value_low < 0)
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
*base_address = ((value_high << 8) | value_low) << 16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HEX_RECORD_START_LINEAR:
|
||||||
|
// Start linear address - store as a potential entry point
|
||||||
|
// but not crucial for firmware extraction
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HEX_RECORD_START_SEGMENT:
|
||||||
|
// Start segment address - similar to above
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return HEX_PARSE_ERROR_UNSUPPORTED_RECORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return record_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hex_file_parse(const char* file_path_on_disk,
|
||||||
|
uint8_t** payload,
|
||||||
|
size_t* payload_length,
|
||||||
|
size_t* payload_address) {
|
||||||
|
FILE* file = fopen(file_path_on_disk, "r");
|
||||||
|
if (!file) {
|
||||||
|
fprintf(stderr, "Could not open file %s for reading\n", file_path_on_disk);
|
||||||
|
return PARSED_ERROR_CANT_OPEN_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First pass: Find start and end addresses, and thus size of the payload
|
||||||
|
char line[512];
|
||||||
|
uint32_t base_address = 0;
|
||||||
|
uint32_t address = 0;
|
||||||
|
uint32_t min_address = 0xFFFFFFFF;
|
||||||
|
uint32_t max_address = 0;
|
||||||
|
bool found_data = false;
|
||||||
|
|
||||||
|
// First pass to determine memory range
|
||||||
|
while (fgets(line, sizeof(line), file)) {
|
||||||
|
size_t len = strlen(line);
|
||||||
|
// Trim trailing newline and carriage return
|
||||||
|
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
|
||||||
|
line[--len] = 0;
|
||||||
|
}
|
||||||
|
if (len == 0 || line[0] != ':')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int result =
|
||||||
|
parse_hex_line(line, NULL, &address, &base_address, &max_address, 0);
|
||||||
|
if (result < 0) {
|
||||||
|
fclose(file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a data record (type 0)
|
||||||
|
if (result == HEX_RECORD_DATA) {
|
||||||
|
found_data = true;
|
||||||
|
if (address < min_address) {
|
||||||
|
min_address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we hit EOF record, we're done
|
||||||
|
if (result == HEX_RECORD_EOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no data was found, return error
|
||||||
|
if (!found_data) {
|
||||||
|
fclose(file);
|
||||||
|
return HEX_PARSE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate payload size
|
||||||
|
size_t size = max_address - min_address;
|
||||||
|
if (size > (1024 * 1024 * 128)) { // Limit to 128 MB
|
||||||
|
fclose(file);
|
||||||
|
return HEX_PARSE_ERROR_TOO_LARGE;
|
||||||
|
}
|
||||||
|
// Allocate memory for the payload
|
||||||
|
*payload = (uint8_t*)calloc(size, sizeof(uint8_t));
|
||||||
|
if (!*payload) {
|
||||||
|
fclose(file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the memory to ensure all bytes are initialized
|
||||||
|
memset(*payload, 0, size);
|
||||||
|
|
||||||
|
// Second pass: actually parse the data and fill out the buffer with the data
|
||||||
|
rewind(file);
|
||||||
|
base_address = 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), file)) {
|
||||||
|
size_t len = strlen(line);
|
||||||
|
// Trim trailing newline and carriage return
|
||||||
|
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
|
||||||
|
line[--len] = 0;
|
||||||
|
}
|
||||||
|
if (len == 0 || line[0] != ':')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// When parsing for real, data is written to the payload buffer
|
||||||
|
// with addresses relative to min_address
|
||||||
|
uint32_t dummy_max = 0;
|
||||||
|
|
||||||
|
int result = parse_hex_line(line, *payload, &address, &base_address,
|
||||||
|
&dummy_max, min_address);
|
||||||
|
if (result < 0) {
|
||||||
|
free(*payload);
|
||||||
|
*payload = NULL;
|
||||||
|
fclose(file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we hit EOF record, we're done
|
||||||
|
if (result == HEX_RECORD_EOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Set output parameters
|
||||||
|
*payload_length = size;
|
||||||
|
*payload_address = min_address;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
49
tools/blisp/src/file_parsers/hex/hex_file.h
Normal file
49
tools/blisp/src/file_parsers/hex/hex_file.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// Created for Intel HEX file parsing
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BLISP_HEX_FILE_H
|
||||||
|
#define BLISP_HEX_FILE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Error codes specific to hex parsing
|
||||||
|
#define HEX_PARSE_ERROR_INVALID_FORMAT -0x2001
|
||||||
|
#define HEX_PARSE_ERROR_CHECKSUM -0x2002
|
||||||
|
#define HEX_PARSE_ERROR_UNSUPPORTED_RECORD -0x2003
|
||||||
|
#define HEX_PARSE_ERROR_TOO_LARGE -0x2004
|
||||||
|
|
||||||
|
// Intel HEX record types
|
||||||
|
typedef enum {
|
||||||
|
HEX_RECORD_DATA = 0x00, // Data record
|
||||||
|
HEX_RECORD_EOF = 0x01, // End of file record
|
||||||
|
HEX_RECORD_EXTENDED_SEGMENT = 0x02, // Extended segment address record
|
||||||
|
HEX_RECORD_START_SEGMENT = 0x03, // Start segment address record
|
||||||
|
HEX_RECORD_EXTENDED_LINEAR = 0x04, // Extended linear address record
|
||||||
|
HEX_RECORD_START_LINEAR = 0x05 // Start linear address record
|
||||||
|
} hex_record_type_t;
|
||||||
|
|
||||||
|
// Parse an Intel HEX file and return a contiguous memory block
|
||||||
|
// Parameters:
|
||||||
|
// file_path_on_disk: Path to the Intel HEX file
|
||||||
|
// payload: Pointer to the buffer that will hold the parsed data
|
||||||
|
// payload_length: Size of the payload
|
||||||
|
// payload_address: Start address of the payload
|
||||||
|
// Returns:
|
||||||
|
// 0 on success, negative value on error
|
||||||
|
int hex_file_parse(const char* file_path_on_disk,
|
||||||
|
uint8_t** payload,
|
||||||
|
size_t* payload_length,
|
||||||
|
size_t* payload_address);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // BLISP_HEX_FILE_H
|
10
tools/blisp/src/file_parsers/hex/tests/CMakeLists.txt
Normal file
10
tools/blisp/src/file_parsers/hex/tests/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
add_executable(hex_file_test test_hex_file.cpp ../hex_file.c )
|
||||||
|
|
||||||
|
target_link_libraries(hex_file_test
|
||||||
|
PRIVATE
|
||||||
|
GTest::GTest
|
||||||
|
)
|
||||||
|
include(GoogleTest)
|
||||||
|
include_directories(hex_file_test PRIVATE ../ ../../)
|
||||||
|
target_compile_definitions(hex_file_test PUBLIC "SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\"")
|
||||||
|
gtest_discover_tests(hex_file_test)
|
9
tools/blisp/src/file_parsers/hex/tests/test.hex
Normal file
9
tools/blisp/src/file_parsers/hex/tests/test.hex
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
:02000004230FC8
|
||||||
|
:10FC0000400196000500000021005A000200070094
|
||||||
|
:10FC10000100000000001E000000000000000000C5
|
||||||
|
:10FC2000000000000000040001007602A40184032B
|
||||||
|
:10FC3000000000000A0001000700000000000000B2
|
||||||
|
:10FC4000140000001A000100000000000000040081
|
||||||
|
:10FC50005A00010082005A008C001E00A5001E0000
|
||||||
|
:10FC60008C001E005A001E00020000000000000070
|
||||||
|
:00000001FF
|
47
tools/blisp/src/file_parsers/hex/tests/test_hex_file.cpp
Normal file
47
tools/blisp/src/file_parsers/hex/tests/test_hex_file.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Intel hex file parser test
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "hex_file.h"
|
||||||
|
#include "parse_file.h"
|
||||||
|
|
||||||
|
TEST(HEX_FILE_PARSER, ParseTestFile) {
|
||||||
|
uint8_t* payload = nullptr;
|
||||||
|
size_t payload_size = 0;
|
||||||
|
size_t payload_address = 0;
|
||||||
|
int res = hex_file_parse(SOURCE_DIR
|
||||||
|
"/tools/blisp/src/file_parsers/hex/tests/test.hex",
|
||||||
|
&payload, &payload_size, &payload_address);
|
||||||
|
// Shall return 0 on success
|
||||||
|
ASSERT_EQ(res, 0);
|
||||||
|
// The expected base address is 0x230F0000 + 0xFC00 = 0x230FFC00
|
||||||
|
ASSERT_EQ(payload_address, 0x230FFC00);
|
||||||
|
// There are 7 data records of 16 bytes each, so payload size should be 0x70
|
||||||
|
// (112 bytes)
|
||||||
|
ASSERT_EQ(payload_size, 0x70);
|
||||||
|
|
||||||
|
// Optionally, check the first few bytes for expected values
|
||||||
|
ASSERT_EQ(payload[0], 0x40);
|
||||||
|
ASSERT_EQ(payload[1], 0x01);
|
||||||
|
ASSERT_EQ(payload[2], 0x96);
|
||||||
|
ASSERT_EQ(payload[3], 0x00);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
free(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HEX_FILE_PARSER, ParseNonExistentFile) {
|
||||||
|
uint8_t* payload = nullptr;
|
||||||
|
size_t payload_size = 0;
|
||||||
|
size_t payload_address = 0;
|
||||||
|
int res = hex_file_parse(SOURCE_DIR "/non_existent_file.hex", &payload,
|
||||||
|
&payload_size, &payload_address);
|
||||||
|
|
||||||
|
ASSERT_EQ(res, PARSED_ERROR_CANT_OPEN_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HEX_FILE_PARSER, ParseInvalidFormat) {
|
||||||
|
// This test would require creating an invalid hex file
|
||||||
|
// For simplicity, we'll skip actual implementation
|
||||||
|
// but in a real test suite we would create a file with invalid format
|
||||||
|
// and verify that it returns HEX_PARSE_ERROR_INVALID_FORMAT
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "bin_file.h"
|
#include "bin_file.h"
|
||||||
#include "dfu_file.h"
|
#include "dfu_file.h"
|
||||||
|
#include "hex_file.h"
|
||||||
|
|
||||||
const char* get_filename_ext(const char* filename) {
|
const char* get_filename_ext(const char* filename) {
|
||||||
const char* dot = strrchr(filename, '.');
|
const char* dot = strrchr(filename, '.');
|
||||||
@ -27,8 +28,13 @@ int parse_firmware_file(const char* file_path_on_disk,
|
|||||||
res = bin_file_parse(file_path_on_disk, &parsed_results->payload,
|
res = bin_file_parse(file_path_on_disk, &parsed_results->payload,
|
||||||
&parsed_results->payload_length,
|
&parsed_results->payload_length,
|
||||||
&parsed_results->payload_address);
|
&parsed_results->payload_address);
|
||||||
|
} else if (strncmp(ext, "hex", 3) == 0 || strncmp(ext, "HEX", 3) == 0) {
|
||||||
|
printf("Input file identified as a .hex file\n");
|
||||||
|
// Intel HEX file
|
||||||
|
res = hex_file_parse(file_path_on_disk, &parsed_results->payload,
|
||||||
|
&parsed_results->payload_length,
|
||||||
|
&parsed_results->payload_address);
|
||||||
}
|
}
|
||||||
// If we wanted to support hex files, here would be where
|
|
||||||
|
|
||||||
// Normalise address, some builds will base the firmware at flash start but
|
// Normalise address, some builds will base the firmware at flash start but
|
||||||
// for the flasher we use 0 base (i.e. offsets into flash)
|
// for the flasher we use 0 base (i.e. offsets into flash)
|
||||||
|
2
vendor/argtable3
vendored
2
vendor/argtable3
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 6f0e40bc44c99af353ced367c6fafca8705f5fca
|
Subproject commit b50c6c81f25eef8af51141678b333c55b661414d
|
Loading…
x
Reference in New Issue
Block a user