diff --git a/CMakeLists.txt b/CMakeLists.txt index 8859da0..91f700b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.22...4.1) project(libtslitex) @@ -19,12 +19,49 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # error if compiler doesn't support c++17 set(CMAKE_CXX_EXTENSIONS ON) +################################ +# Project Dependencies +################################ +include(FetchContent) + +# json-c Library +FetchContent_Declare( + json_c + GIT_REPOSITORY https://github.com/json-c/json-c + GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f + FIND_PACKAGE_ARGS NAMES json-c +) + +set(BUILD_SHARED_LIBS OFF) +FetchContent_MakeAvailable(json_c) + +# zlib Library +FetchContent_Declare( + zlib + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG v1.3.1 + FIND_PACKAGE_ARGS NAMES zlib +) + +set(ZLIB_BUILD_TESTING OFF) +set(ZLIB_BUILD_SHARED OFF) +set(ZLIB_INSTALL OFF) +FetchContent_MakeAvailable(zlib) + + + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") -add_definitions(-DUNICODE -D_UNICODE) +option(ENABLE_FACTORY_PROVISIONING "Include Factory Provisioning Functions" OFF) + +if(ENABLE_FACTORY_PROVISIONING) + add_compile_definitions(FACTORY_PROVISIONING_API) +endif() + +add_compile_definitions(UNICODE _UNICODE) -add_definitions(-DEN_LOGGING) +add_compile_definitions(EN_LOGGING) add_compile_definitions("$<$:EN_LOGGING_DEBUG>") @@ -49,6 +86,7 @@ set(TS_SOURCES src/samples.c src/spiflash.c src/ts_channel.c + src/ts_data.c src/ts_fw_manager.c src/thunderscope.c ) @@ -69,6 +107,7 @@ set(TS_HEADERS src/samples.h src/spiflash.h src/ts_channel.h + src/ts_data.h src/ts_fw_manager.h src/util.h ) @@ -92,10 +131,11 @@ set_target_properties(tslitex PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) -target_link_libraries(tslitex litepcie) +target_link_libraries(tslitex PUBLIC litepcie) +target_link_libraries(tslitex PRIVATE json-c-static zlibstatic) if(APPLE) - target_link_libraries(tslitex "-framework IOKit") + target_link_libraries(tslitex PRIVATE "-framework IOKit") endif() target_include_directories(tslitex PUBLIC include) @@ -112,12 +152,11 @@ set_target_properties(tslitex_static PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) - -#target_compile_definitions(tslitex_static tslitex_static_STATIC) -set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) +set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) -target_link_libraries(tslitex_static litepcie) +target_link_libraries(tslitex_static PUBLIC litepcie) +target_link_libraries(tslitex_static PRIVATE json-c-static zlibstatic) target_include_directories(tslitex_static PUBLIC include) file(COPY ${TS_LIB_HEADERS} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/include) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index dd03a08..0a4959e 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.22...4.1) find_package( Python @@ -19,6 +19,9 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h COMMAND ${CMAKE_COMMAND} -E copy_directory "$" + "$" + "$" + "$" "${CMAKE_CURRENT_BINARY_DIR}/lib" DEPENDS tslitex_static ) @@ -29,9 +32,11 @@ add_custom_target(pydeps tslitex_static ) -set(PY_BIND_LIBS tslitex_static litepcie) +set(PY_BIND_LIBS tslitex_static) + if(WIN32) list(APPEND PY_BIND_LIBS setupapi) + list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) endif() list(JOIN PY_BIND_LIBS "\", \"" PY_EXT_LIBS) @@ -47,7 +52,7 @@ add_custom_target(PyBindings COMMAND Python::Interpreter -m pipx run build --wheel . WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Building python whl" - DEPENDS pydeps tslitex_static + DEPENDS pydeps ) if (NOT DEFINED PY_PLATFORM) @@ -61,7 +66,7 @@ add_custom_target(PyWheels COMMAND Python::Interpreter -m cibuildwheel --platform ${PY_PLATFORM} --archs ${PY_ARCH} --config-file pyproject.toml WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Building all Python wheels" - DEPENDS pydeps tslitex_static + DEPENDS pydeps ) else() message(STATUS "Python bindings not available: Python not found") diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 3f23899..e8f39cc 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -37,9 +37,13 @@ cdef extern from "thunderscope.h": uint32_t hw_id uint32_t gw_id uint32_t litex + uint32_t board_rev char device_path[256] char identity[256] char serial_number[256] + char build_config[256] + char build_date[256] + char mfg_signature[256] ctypedef tsDeviceInfo_s tsDeviceInfo_t @@ -93,6 +97,8 @@ cdef extern from "thunderscope.h": int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution) + int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate) + int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) nogil diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index bff09cb..887196c 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -253,11 +253,22 @@ cdef class Thunderscope: @SampleResolution.setter def SampleResolution(self, resolution: int): - assert resolution is 256, f"Support for 8-bit mode only" + assert resolution in [256, 4096], f"Unsupported Resolution" self._sample_resolution = resolution retval = tslitex.thunderscopeSampleModeSet(self._tsHandle, self._sample_rate, self._sample_resolution) + @property + def UpdateRate(self): + return self._interrupt_rate + + @UpdateRate.setter + def UpdateRate(self, rate : int): + assert rate > 0, f"Invalid Update Rate" + self._interrupt_rate = rate + retval = tslitex.thunderscopeSampleInterruptRate(self._tsHandle, + rate) + def Enable(self, enable: bool): cdef int32_t retVal = tslitex.thunderscopeDataEnable(self._tsHandle, enable) if retVal != tslitex.TS_STATUS_OK: diff --git a/doc/calibration_data.md b/doc/calibration_data.md new file mode 100644 index 0000000..20e79b6 --- /dev/null +++ b/doc/calibration_data.md @@ -0,0 +1,37 @@ +## Calibration Data + +### API + +- Factory Provision Prepare +- Factory Provision Append Data +- Factory Provision Verify +- Factory Read Item + + +### Factory TLV's + +Each section is dentoted by a 32-bit TAG, 32-bit LENGTH, ASCII Data, and CRC32 Checksum. + +#### HWID + +- Tag: `HWID` +- Format: JSON +- Contents: + - ID Version + - "Serial Number" + - "Board Revision" + - "Build Config" + - "Build Date" + - "Mfg Signature" + + +#### FCAL + +- Tag: `FCAL` +- Format: JSON +- Contents: + - Calibration Version + - Calibration Station + - Calibration Date + - Calibration Data + diff --git a/doc/data/hwid.schema.json b/doc/data/hwid.schema.json new file mode 100644 index 0000000..938bab6 --- /dev/null +++ b/doc/data/hwid.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Hardware Identity Description", + "type": "object", + "properties": { + "version": { + "type": "integer", + "minimum": 1 + }, + "Serial Number": { + "type": "string", + "maxLength": 256 + }, + "Board Revision": { + "type": "number" + }, + "Build Config": { + "type": "string", + "maxLength": 256 + }, + "Build Date": { + "type": "string", + "maxLength": 256 + }, + "Mfg Signature": { + "type": "string", + "maxLength": 256 + } + }, + "required": [ + "version", + "Serial Number" + ] +} \ No newline at end of file diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 6a6a5ad..8fc700d 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -22,6 +22,7 @@ #include "optparse.h" #include +#include #define MAX_USER_DATA_SIZE (0x500000) @@ -52,7 +53,7 @@ static void user_write(tsHandle_t ts, const char* file_path, uint32_t offset) // Close File file.close(); - delete bitstream; + delete[] bitstream; } else { @@ -80,7 +81,7 @@ static void user_read(tsHandle_t ts, const char* file_path) // Close File file.flush(); file.close(); - delete data_buffer; + delete[] data_buffer; } else { @@ -114,7 +115,7 @@ static void fw_upgrade(tsHandle_t ts, const char* file_path) // Close File file.close(); - delete bitstream; + delete[] bitstream; } else { @@ -142,6 +143,13 @@ static void print_help(void) printf("\thelp - Print this help message\r\n"); printf("\tCommon Options:\r\n"); printf("\t\t-d Device Index\r\n"); +#if defined(FACTORY_PROVISIONING_API) + printf("--FACTORY USAGE ONLY--\r\n"); + printf("\tfactory_prepare [dna] - Erase the Factory Data Partition\r\n"); + printf("\t\t[dna] - Device DNA value to confirm erase\r\n"); + printf("\tfactory_load [tag] [file] - Load a JSON file with Factory Data\r\n"); + printf("\t\t[tag] - Tag identifier for the data item\r\n"); +#endif } int main(int argc, char** argv) @@ -258,6 +266,85 @@ int main(int argc, char** argv) //Nothing else to do ;; } +#if defined(FACTORY_PROVISIONING_API) + else if(0 == strcmp(arg, "factory_prepare")) + { + if(argc != 3) + { + printf("Error: Missing args\r\n"); + print_help(); + } + else + { + uint64_t op_dna = 0; + if(!sscanf(argv[2], "%llx", &op_dna)) + { + printf("Error: Invalid DNA\r\n"); + print_help(); + } + else if(TS_STATUS_OK == thunderscopeFactoryProvisionPrepare(ts, op_dna)) + { + printf("Factory Data Partition Erased \r\n"); + } + else + { + printf("Failed to prepare Factory Data Partition\r\n"); + } + } + } + else if(0 == strcmp(arg, "factory_load")) + { + char tag[5]; + if(argc != 4) + { + printf("Error: Missing args\r\n"); + print_help(); + } + else if(!sscanf(argv[2], "%4s", tag)) + { + printf("Error: Invalid TAG arg\r\n"); + print_help(); + } + else + { + file_path = argv[3]; + std::error_code err; + auto file_size = std::filesystem::file_size(file_path, err); + if(err) + { + std::cout << "Error opening \"" << file_path << "\" : " << err.message() << "\r\n"; + thunderscopeClose(ts); + return -1; + } + + // Open Data File + std::ifstream file(file_path, std::ios::binary); + if(file) + { + char* data_content = new char[file_size]; + file.read(data_content, file_size); + + if(TS_STATUS_OK == thunderscopeFactoryProvisionAppendTLV(ts, (uint32_t)(tag[0] + (tag[1] << 8) + (tag[2] << 16) + (tag[3] << 24)), + file_size, (const char*)data_content)) + { + printf("Finished writing the %s item\r\n", tag); + } + else + { + printf("Failed to write factory data item\r\n"); + } + + // Close File + file.close(); + delete data_content; + } + else + { + printf("ERROR Factory TLV: Failed to open file %s\r\n", file_path); + } + } + } +#endif else if(argCount == argc) { printf("**Missing File path argument**\r\n"); diff --git a/include/thunderscope.h b/include/thunderscope.h index abeb5fe..896253c 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -86,6 +86,20 @@ int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* conf); */ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution); +/** + * @brief Set the approximate rate at which interrupts will fire + * + * This method sets the target frequency for interrupts in the driver. A higher value will trigger + * more often, zero is invalid. The default update rate is 100Hz, or every 10ms. The driver + * configuration will be updated as the sample mode is changed. This setting will take affect the + * next time the ADC is enabled. + * + * @param ts Handle to the Thunderscope device + * @param rate Sample Interrupt Rate (interrupts per second) + * @return int32_t TS_STATUS_OK if the Thunderscope was configured + */ +int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate); + /** * @brief Enable or Disable * diff --git a/include/ts_calibration.h b/include/ts_calibration.h index 2e41bff..ae89161 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -82,7 +82,7 @@ typedef struct tsScopeCalibration_s * @param channel Channel number * @param cal Channel Calibration data * @return int32_t TS_STATUS_OK if the calibration was accepted -*/ + */ int32_t thunderscopeChanCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); /** @@ -132,6 +132,51 @@ int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsCha * @return int32_t TS_STATUS_OK if the parameters were applied */ int32_t thunderscopeCalibrationAdcTest(tsHandle_t ts, tsCalAdcTest_t test_mode, uint32_t test_pattern); + +/****************************************************************************** + * Factory Provisioning API + * WARNING: May cause data corruption + ******************************************************************************/ +/** + * @brief Erase the entire Factory data partition + * + * @param ts Handle to the Thunderscope device + * @param dna Device DNA value for confirmation + * @return int32_t TS_STATUS_OK if the partition is erased successfully + */ +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna); + +/** + * @brief Append an arbitrary data item to the Factory data partition + * + * @param ts Handle to the Thunderscope device + * @param tag 32-bit Tag to identify the data item + * @param length Length of the content string + * @param content ASCII string containing a JSON object + * @return int32_t TS_STATUS_OK if the object was written successfully + */ +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content); + +/** + * @brief Test the Factory data items are valid + * + * @param ts Handle to the Thunderscope device + * @return int32_t TS_STATUS_OK if all data item checks pass + */ +int32_t thunderscopeFactoryProvisionVerify(tsHandle_t ts); + +/** + * @brief Retrieve an item from the Factory data partition. If a NULL buffer is provided, only + * return the length of the item. + * + * @param ts Handle to the Thunderscope device + * @param tag 32-bit Tag value to lookup + * @param content_buffer Pointer for a buffer to store the read value string + * @param item_max_len Maximum size of value string that the provided buffer can hold + * @return int32_t Length of the item value or a negative error code if the Item could not be read + */ +int32_t thunderscopeFactoryReadItem(tsHandle_t ts, const uint32_t tag, char* content_buffer, uint32_t item_max_len); + #ifdef __cplusplus } #endif diff --git a/include/ts_common.h b/include/ts_common.h index 3d76558..5a77536 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -61,12 +61,17 @@ typedef enum tsChannelTerm_e typedef struct tsDeviceInfo_s { uint32_t device_id; - uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ - uint32_t gw_id; /**< 32-bit version ID: gw_id[31:16] - Major, gw_id[15:8] - Minor, gw_id[7:1] - Patch, gw_id[0] - next */ - uint32_t litex; /**< 32-bit LiteX version: litex[31:16] - Year, litex[15:0] - Month */ - char device_path[TS_IDENT_STR_LEN]; - char identity[TS_IDENT_STR_LEN]; - char serial_number[TS_IDENT_STR_LEN]; + uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ + uint32_t gw_id; /**< 32-bit version ID: gw_id[31:16] - Major, gw_id[15:8] - Minor, gw_id[7:1] - Patch, gw_id[0] - next */ + uint32_t litex; /**< 32-bit LiteX version: litex[31:16] - Year, litex[15:0] - Month */ + uint32_t board_rev; /**< 32-bit Board revision counter. */ + char device_path[TS_IDENT_STR_LEN]; /**< Device Path where the instance is opened */ + char identity[TS_IDENT_STR_LEN]; /**< Device Identity String */ + char serial_number[TS_IDENT_STR_LEN]; /**< Serial Number */ + char build_config[TS_IDENT_STR_LEN]; /**< Build Configuration description */ + char build_date[TS_IDENT_STR_LEN]; /**< Date of Manufacture */ + char mfg_signature[TS_IDENT_STR_LEN]; /**< Device Unique Manufacturing Signature */ + } tsDeviceInfo_t; typedef struct tsChannelParam_s diff --git a/litepcie/include/litepcie_dma.h b/litepcie/include/litepcie_dma.h index ff66d91..c5baf43 100644 --- a/litepcie/include/litepcie_dma.h +++ b/litepcie/include/litepcie_dma.h @@ -42,8 +42,8 @@ struct litepcie_dma_ctrl { }; void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_enable); -void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); -void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); diff --git a/litepcie/public_h/litepcie_linux.h b/litepcie/public_h/litepcie_linux.h index d284165..96ee48f 100644 --- a/litepcie/public_h/litepcie_linux.h +++ b/litepcie/public_h/litepcie_linux.h @@ -39,6 +39,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -46,6 +47,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h index 8985072..028e59f 100644 --- a/litepcie/public_h/litepcie_mac.h +++ b/litepcie/public_h/litepcie_mac.h @@ -25,6 +25,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint32_t enable; uint32_t channel; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -33,6 +34,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint32_t enable; uint32_t channel; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/public_h/litepcie_win.h b/litepcie/public_h/litepcie_win.h index 47bec48..04d3130 100644 --- a/litepcie/public_h/litepcie_win.h +++ b/litepcie/public_h/litepcie_win.h @@ -53,6 +53,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -60,6 +61,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 1469fd6..bc31d49 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -39,9 +39,10 @@ void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_e checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA, m)); } -void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_writer m; m.enable = enable; + m.interrupt_count = intr_count; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; @@ -55,9 +56,10 @@ void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *lost_count = m.lost_count; } -void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_reader m; m.enable = enable; + m.interrupt_count = intr_count; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; @@ -193,9 +195,9 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui void litepcie_dma_cleanup(struct litepcie_dma_ctrl *dma) { if (dma->use_reader) - litepcie_dma_reader(dma, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 0, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); if (dma->use_writer) - litepcie_dma_writer(dma, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 0, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); litepcie_release_dma(dma, dma->use_reader, dma->use_writer); @@ -221,9 +223,9 @@ void litepcie_dma_process(struct litepcie_dma_ctrl *dma) /* set / get dma */ if (dma->use_writer) - litepcie_dma_writer(dma, 1, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 1, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); if (dma->use_reader) - litepcie_dma_reader(dma, 1, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 1, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); #if defined(_WIN32) uint32_t retLen = 0; diff --git a/src/afe.c b/src/afe.c index bb4bc39..31a019a 100644 --- a/src/afe.c +++ b/src/afe.c @@ -52,7 +52,7 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri { afe->trimPotBits = MCP4452_NUM_BITS; - if(litepcie_readl(coupling.fd, CSR_DEV_STATUS_HW_ID_ADDR) & TS_HW_ID_REV_MASK > 0) + if((litepcie_readl(coupling.fd, CSR_DEV_STATUS_HW_ID_ADDR) & TS_HW_ID_REV_MASK) > 0) { afe->couplingInverted = true; } diff --git a/src/samples.c b/src/samples.c index d2b1fd7..a6a6aaf 100644 --- a/src/samples.c +++ b/src/samples.c @@ -101,6 +101,7 @@ int32_t samples_enable_set(sampleStream_t* inst, uint8_t en) //Start/Stop DMA litepcie_dma_writer(&inst->dma, en, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -139,13 +140,14 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ { #if defined (APPLE_MMAP_DMA) //POLLING litepcie_dma_writer(&inst->dma, inst->active, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); int64_t buff_available = inst->dma_buffer_count - inst->driver_buffer_count; - if(buff_available > (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ)) + if(buff_available > (DMA_BUFFER_COUNT - inst->interrupt_rate)) { - inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ); + inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - inst->interrupt_rate); inst->dropped_buffer_count++; } if(buff_available > 0) @@ -201,6 +203,7 @@ int32_t samples_update_status(sampleStream_t* inst) } litepcie_dma_writer(&inst->dma, inst->active, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -218,7 +221,7 @@ int32_t samples_teardown(sampleStream_t* inst) else { inst->active = 0; - litepcie_dma_writer(&inst->dma, 0, + litepcie_dma_writer(&inst->dma, 0, 0, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); diff --git a/src/samples.h b/src/samples.h index 18a6db7..a53271c 100644 --- a/src/samples.h +++ b/src/samples.h @@ -31,6 +31,7 @@ typedef struct sampleStream_s int64_t dma_buffer_count; int64_t driver_buffer_count; int64_t dropped_buffer_count; + uint32_t interrupt_rate; uint8_t active; } sampleStream_t; diff --git a/src/thunderscope.c b/src/thunderscope.c index 307290e..bafd8b3 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -18,17 +18,20 @@ #include "samples.h" #include "gpio.h" #include "ts_fw_manager.h" +#include "ts_data.h" #include "util.h" #include "litepcie.h" -#ifdef _WIN32 +#if defined(_WIN32) #define FILE_FLAGS (FILE_ATTRIBUTE_NORMAL) #else #define FILE_FLAGS (O_RDWR) #endif +#define DEFAULT_DATA_INTR_RATE (100) + typedef struct ts_inst_s { file_t ctrl; @@ -39,6 +42,7 @@ typedef struct ts_inst_s const led_signals_t *signals; bool initialized; ts_fw_manager_t fw; + uint32_t interrupt_rate; //TBD - Other Instance Data } ts_inst_t; @@ -47,6 +51,7 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) { int32_t retVal = TS_STATUS_ERROR; char testPath[TS_IDENT_STR_LEN]; + ts_fw_manager_t fwmngr; // Find device path by index snprintf(testPath, TS_IDENT_STR_LEN, LITEPCIE_CTRL_NAME(%d), devIndex); @@ -64,9 +69,11 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) { info->identity[i] = (char)litepcie_readl(testDev, CSR_IDENTIFIER_MEM_BASE + 4 * i); } - //TODO Implement Serial Number - snprintf(info->serial_number, TS_IDENT_STR_LEN, "TS00##"); strncpy(info->device_path, testPath, TS_IDENT_STR_LEN); + // Get Factory Build Info + ts_fw_manager_init(testDev, &fwmngr); + ts_data_factory_id_get(&fwmngr, info); + litepcie_close(testDev); retVal = TS_STATUS_OK; } @@ -108,12 +115,13 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) { pInst->identity.identity[i] = (char)litepcie_readl(pInst->ctrl, CSR_IDENTIFIER_MEM_BASE + 4 * i); } - //TODO Implement Serial Number - snprintf(pInst->identity.serial_number, TS_IDENT_STR_LEN, "TS00##"); + + strncpy(pInst->identity.device_path, devName, TS_IDENT_STR_LEN); if(!skip_init) { + pInst->interrupt_rate = DEFAULT_DATA_INTR_RATE; if(TS_STATUS_OK != ts_channel_init(&pInst->pChannel, pInst->ctrl)) { LOG_ERROR("Failed to initialize channels"); @@ -143,6 +151,9 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) return NULL; } + // Get Factory Build Info + ts_data_factory_id_get(&pInst->fw, &pInst->identity); + pInst->status_leds.fd = pInst->ctrl; pInst->status_leds.reg = TS_STATUS_LED_ADDR; pInst->status_leds.bit_mask = TS_STATUS_LED_MASK; @@ -226,7 +237,36 @@ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolut if(pInst && pInst->initialized) { - return ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); + int32_t status = ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); + if(status == TS_STATUS_OK) + { + //Target 100Hz interrupt rate + pInst->samples.interrupt_rate = 1 + ((((resolution == 256) ? rate : 2*rate) / + (DMA_BUFFER_SIZE)) / pInst->interrupt_rate); + + LOG_DEBUG("DMA Interrupt Rate is 1/%d MB", pInst->samples.interrupt_rate); + } + return status; + } + + return TS_STATUS_ERROR; +} + +int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + if(interrupt_rate == 0) + { + interrupt_rate = DEFAULT_DATA_INTR_RATE; + } + pInst->interrupt_rate = interrupt_rate; + + LOG_DEBUG("Targeting Interrupt frequency of %d Hz", pInst->interrupt_rate); + + return TS_STATUS_OK; } return TS_STATUS_ERROR; @@ -402,4 +442,70 @@ int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) { return TS_STATUS_ERROR; } -} \ No newline at end of file +} + +#if defined(FACTORY_PROVISIONING_API) +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_erase(&pInst->fw, dna); + } + else + { + return TS_STATUS_ERROR; + } +} + +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_append(&pInst->fw, tag, length, content); + } + else + { + return TS_STATUS_ERROR; + } +} +#else +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna) +{ + LOG_ERROR("Factory API Not Enabled"); + return TS_STATUS_ERROR; +} + +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content) +{ + LOG_ERROR("Factory API Not Enabled"); + return TS_STATUS_ERROR; +} +#endif + +int32_t thunderscopeFactoryProvisionVerify(tsHandle_t ts) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_verify(&pInst->fw); + } + else + { + return TS_STATUS_ERROR; + } +} + +int32_t thunderscopeFactoryReadItem(tsHandle_t ts, const uint32_t tag, char* content_buffer, uint32_t item_max_len) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_retreive(&pInst->fw, tag, content_buffer, item_max_len); + } + else + { + return TS_STATUS_ERROR; + } +} diff --git a/src/ts_data.c b/src/ts_data.c new file mode 100644 index 0000000..d7effad --- /dev/null +++ b/src/ts_data.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Provide methods to fetch and store content in flash + * + * Copyright (c) 2024 Nate Meyer + */ + +#include +#include + +#include "ts_data.h" +#include "ts_fw_manager.h" +#include "util.h" + + +int32_t ts_data_factory_cal_get(ts_fw_manager_t* mngr, tsScopeCalibration_t *fcal) +{ + // struct json_object * fcal; + + return TS_STATUS_OK; +} + +int32_t ts_data_factory_id_get(ts_fw_manager_t* mngr, tsDeviceInfo_t* infos) +{ + struct json_object *fid; + struct json_object *item; + uint32_t hwid_version = 0; + uint32_t hwid_len = 0; + uint8_t* hwid_buffer = NULL; + + // Check HWID tag in factory partition + hwid_len = ts_fw_manager_factory_data_get_length(mngr, TAG_HWID); + + if(hwid_len <= 0) + { + LOG_ERROR("Could not read mfg data"); + return TS_STATUS_ERROR; + } + + // Read HWID to buffer + hwid_buffer = malloc(hwid_len + 1); + if(TS_STATUS_ERROR == ts_fw_manager_factory_data_retreive(mngr, TAG_HWID, hwid_buffer, hwid_len)) + { + LOG_ERROR("Failed to read HWID"); + free(hwid_buffer); + return TS_STATUS_ERROR; + } + + // Parse json + hwid_buffer[hwid_len] = '\0'; + fid = json_tokener_parse(hwid_buffer); + + // Store HWID version + if(json_object_object_get_ex(fid,"version", &item)) + { + if(json_object_get_type(item) == json_type_int) + { + hwid_version = json_object_get_int(item); + } + else + { + LOG_ERROR("HWID Version bad value"); + } + } + else + { + LOG_ERROR("HWID Version key not found"); + } + + // Fill struct with info + if(json_object_object_get_ex(fid,"Serial Number", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->serial_number, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Serial Number bad value"); + } + } + else + { + LOG_ERROR("Serial Number key not found"); + } + + if(json_object_object_get_ex(fid,"Board Revision", &item)) + { + if(json_object_get_type(item) == json_type_int) + { + infos->board_rev = json_object_get_int(item); + } + else + { + LOG_ERROR("Board Revision bad value"); + } + } + else + { + LOG_ERROR("Board Revision key not found"); + } + + if(json_object_object_get_ex(fid,"Build Config", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->build_config, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Build Config bad value"); + } + } + else + { + LOG_ERROR("Build Config key not found"); + } + + // Build Date + if(json_object_object_get_ex(fid,"Build Date", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->build_date, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Build Date bad value"); + } + } + else + { + LOG_ERROR("Build Date key not found"); + } + + // Signature + if(json_object_object_get_ex(fid,"Mfg Signature", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->mfg_signature, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Mfg Signature bad value"); + } + } + else + { + LOG_ERROR("Mfg Signature key not found"); + } + + free(hwid_buffer); + return TS_STATUS_OK; +} diff --git a/src/ts_data.h b/src/ts_data.h new file mode 100644 index 0000000..87a2e4c --- /dev/null +++ b/src/ts_data.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Library configuration definitions + * + * Copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com + * + */ +#ifndef _TS_DATA_H_ +#define _TS_DATA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ts_common.h" +#include "ts_calibration.h" +#include "ts_fw_manager.h" + + +#define TAGSTR(x) (uint32_t)(x[0] + (x[1] << 8) + (x[2] << 16) + (x[3] << 24)) +#define TAG_HWID TAGSTR("HWID") +#define TAG_FCAL TAGSTR("FCAL") + +/** + * @brief Read and parse the Factory Calibration from flash + * + * @param mngr Pointer to the FW Manager instance + * @param fcal Pointer to the calibration data struct + * + * @return TS_STATUS_OK if the calibration was parsed successfully + */ +int32_t ts_data_factory_cal_get(ts_fw_manager_t* mngr, tsScopeCalibration_t *fcal); + +/** + * @brief Read and parse the Factory Device Information + * + * @param mngr Pointer to the FW Manager instance + * @param infos Pointer to the device information structf + * + * @return TS_STATUS_OK if the Device information data was parsed + */ +int32_t ts_data_factory_id_get(ts_fw_manager_t* mngr, tsDeviceInfo_t* infos); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 4a46d24..0c5e542 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -8,6 +8,15 @@ #include +#if defined(_WIN32) +#include +#pragma comment(lib, "Ws2_32.lib") +#else +#include +#endif + +#include + #include "ts_fw_manager.h" #include "platform.h" #include "spiflash.h" @@ -25,6 +34,8 @@ #define ICAP_REG_IDCODE (0x0C) +#define INVALID_TAG (0xFFFFFFFF) + static const struct { uint32_t code; char name[16]; @@ -462,13 +473,6 @@ int32_t ts_fw_manager_user_data_write(ts_fw_manager_t* mngr, const char* buffer, return TS_STATUS_OK; } -int32_t ts_fw_manager_factory_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len) -{ - // Read File from SPI Flash - - return TS_STATUS_OK; -} - static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_total) { ts_fw_manager_t* mngr = (ts_fw_manager_t*)ctx; @@ -476,4 +480,376 @@ static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_t { atomic_fetch_add(&mngr->fw_progress, work_done); } +} + +int32_t ts_fw_manager_factory_data_erase(ts_fw_manager_t* mngr, uint64_t dna) +{ + uint64_t dna_actual= (uint64_t)litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR) << 32; + dna_actual |= litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR + 4); + + if(dna == dna_actual) + { + return spiflash_erase(&mngr->flash_dev, mngr->partition_table->factory_config_start, + (mngr->partition_table->factory_config_end - mngr->partition_table->factory_config_start)); + } + return TS_STATUS_ERROR; +} + +int32_t ts_fw_manager_factory_data_append(ts_fw_manager_t* mngr, uint32_t tag, uint32_t length, const uint8_t *content) +{ + int32_t retVal = TS_STATUS_ERROR; + uint32_t offset = 0; + uint32_t current_tag = INVALID_TAG; + uint32_t next_len = 0; + uint32_t tlv_crc = crc32(0, Z_NULL, 0); + + // Append the new TLV + while (offset < mngr->partition_table->factory_config_end) + { + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)¤t_tag, sizeof(current_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + if(tag == htonl(current_tag)) + { + LOG_ERROR("ERROR: Duplicate TAG %08X", tag); + retVal = TS_STATUS_ERROR; + break; + } + + // Check if empty + if(current_tag == INVALID_TAG) + { + //Test for length + if((mngr->partition_table->factory_bitstream_start + offset + sizeof(uint32_t)*3 + length) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Not enough space in Factory Partition to store object of %dB",length); + retVal = TS_STATUS_ERROR; + break; + } + + //Write Tag, and Length + uint64_t tl = ((uint64_t)htonl(length) << 32) + htonl(tag); + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&tl, sizeof(uint64_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + offset += sizeof(uint64_t); + + //Write Value + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), content, length); + if(retVal < 0) + { + LOG_ERROR("Failed to write value for tag %08x at offset 0x%x", tag, offset); + break; + } + offset += length; + + //Write CRC + tlv_crc = crc32(tlv_crc, content, length); + LOG_DEBUG("Completing TLV with CRC %08X", tlv_crc); + tlv_crc = htonl(tlv_crc); + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), (uint8_t*)&tlv_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write CRC32 for tag %08X at offset 0x%x", tag, offset); + break; + } + retVal = TS_STATUS_OK; + break; + } + + //Read Len and increment for next tag + offset += sizeof(uint32_t); + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + // Increment size of Length, Value, and CRC + offset += sizeof(uint32_t)*2 + ntohl(next_len); + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_verify(ts_fw_manager_t* mngr) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t tag = INVALID_TAG; + uint32_t next_len = 0; + uint8_t val_buffer[4096]; + uint32_t stored_crc; + uint32_t new_crc = crc32(0, Z_NULL, 0); + uint32_t element_count = 0; + + // Read and Verify each TLV CRC32 + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)&tag, sizeof(tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(tag); + + // Check if empty, no more tags to verify + if(tag == INVALID_TAG) + { + LOG_DEBUG("Factory data partition verified"); + retVal = TS_STATUS_OK; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + ntohl(next_len) + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Exceeds Length (%d)", tag, next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Read the CRC + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset + ntohl(next_len)), + (uint8_t*)&stored_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + //Read Value and Compute CRC + while(next_len > 0) + { + uint32_t segment_len = next_len > sizeof(val_buffer) ? sizeof(val_buffer) : next_len; + next_len -= segment_len; + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), + val_buffer, segment_len); + if(retVal < 0) + { + LOG_ERROR("Failed to write value for tag %08x at offset 0x%x", tag, offset); + break; + } + offset += segment_len; + + //Calculate the CRC + new_crc = crc32(new_crc, val_buffer, segment_len); + } + + if(ntohl(stored_crc) != new_crc) + { + LOG_ERROR(); + retVal = TS_STATUS_ERROR; + break; + } + + // Increment size of CRC + offset += sizeof(uint32_t); + element_count++; + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_get_length(ts_fw_manager_t* mngr, uint32_t tag) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t read_tag = INVALID_TAG; + uint32_t next_len = 0; + + // Search for a specific Tag and copy it to the provided Content buffer + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&read_tag, sizeof(read_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(read_tag); + + // Check if empty, no more tags to try + if(read_tag == INVALID_TAG) + { + LOG_DEBUG("Tag 0x%08X not found in Factory data partition", tag); + retVal = 0; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + next_len = ntohl(next_len); + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + next_len + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Bad Length (%d)", tag, next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Tag matches, return length + if(ntohl(read_tag) == tag) + { + retVal = next_len; + break; + } + else + { + offset += (sizeof(uint32_t) + next_len); + } + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_retreive(ts_fw_manager_t* mngr, uint32_t tag, uint8_t* content, uint32_t max_len) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t check_offset = 0; + uint32_t read_tag = INVALID_TAG; + uint32_t next_len = 0; + uint32_t val_len = 0; + uint8_t val_buffer[4096]; + uint32_t stored_crc; + uint32_t new_crc = crc32(0, Z_NULL, 0); + + // Search for a specific Tag and copy it to the provided Content buffer + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&read_tag, sizeof(read_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(read_tag); + + // Check if empty, no more tags to try + if(tag == INVALID_TAG) + { + LOG_DEBUG("Tag 0x%08X not found in Factory data partition", tag); + retVal = TS_STATUS_OK; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag %08x length at offset 0x%x", ntohl(read_tag), offset); + break; + } + next_len = ntohl(next_len); + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + next_len + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Bad Length (%d)", ntohl(read_tag), next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Find next if tag doesn't match + if(ntohl(read_tag) != tag) + { + offset += (sizeof(uint32_t) + next_len); + continue; + } + + //Read the CRC + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset + next_len), (uint8_t*)&stored_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + //Read Value and Compute CRC + val_len = next_len; + check_offset = offset; + while(next_len > 0) + { + uint32_t segment_len = next_len > sizeof(val_buffer) ? sizeof(val_buffer) : next_len; + next_len -= segment_len; + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + check_offset), val_buffer, segment_len); + if(retVal < 0) + { + LOG_ERROR("Failed to read value for tag %08x at offset 0x%x : %d", tag, offset, retVal); + break; + } + check_offset += segment_len; + + //Calculate the CRC + new_crc = crc32(new_crc, val_buffer, segment_len); + } + + if(ntohl(stored_crc) != new_crc) + { + LOG_ERROR("Failed to read Tag %08X, bad CRC (expected %08X, calculated %08X)", tag, + ntohl(stored_crc), new_crc); + retVal = TS_STATUS_ERROR; + break; + } + + //CRC Passes, read to user buffer + new_crc = crc32(0, Z_NULL, 0); + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), content, val_len); + if(retVal < 0) + { + LOG_ERROR("Failed to read value for tag %08x at offset 0x%x : %d", tag, offset, retVal); + break; + } + + //Calculate the CRC one last time + new_crc = crc32(new_crc, content, val_len); + + if(ntohl(stored_crc) == new_crc) + { + retVal = val_len; + } + else + { + LOG_ERROR("Failed CRC check for tag %08X (expected 0x%08X, calculated 0x%08X)", + tag, ntohl(stored_crc), new_crc); + retVal = TS_STATUS_ERROR; + } + break; + } + return retVal; } \ No newline at end of file diff --git a/src/ts_fw_manager.h b/src/ts_fw_manager.h index 6965ee4..10bfdd2 100644 --- a/src/ts_fw_manager.h +++ b/src/ts_fw_manager.h @@ -89,6 +89,59 @@ int32_t ts_fw_manager_user_data_read(ts_fw_manager_t* mngr, char* buffer, uint32 */ int32_t ts_fw_manager_factory_reset(ts_fw_manager_t* mngr, bool reset_config, bool reset_bitstream); +/** + * @brief Erase the Factory Data partition + * + * @param mngr Pointer to a manager instance + * @param dna Unit PORT_DNA value + * + * @return int32_t TS_STATUS_SUCCESS if the factory partition is erased. + */ +int32_t ts_fw_manager_factory_data_erase(ts_fw_manager_t* mngr, uint64_t dna); + +/** + * @brief Append a TLV to the Factory data + * + * @param mngr Pointer to a manager instance + * @param tag 32-bit Tag + * @param length Length of the value data + * @param content Pointer to the TLV value data array + * + * @return + */ +int32_t ts_fw_manager_factory_data_append(ts_fw_manager_t* mngr, uint32_t tag, uint32_t length, const uint8_t *content); + + +/** + * @brief Retrieve the length of a specific Tag from the Factory data partition + * + * @param mngr Pointer to a manager instance + * @param tag Tag to retrieve the length of + * + * @return Length of the item if successful, zero if the item is not found, else TS_STATUS_ERROR + */ +int32_t ts_fw_manager_factory_data_get_length(ts_fw_manager_t* mngr, uint32_t tag); + +/** + * @brief Read every item and confirm good checksum for each value + * + * @param mngr Pointer to a manager instance + * + * @return TS_STATUS_OK if every item has a good checksum + */ +int32_t ts_fw_manager_factory_data_verify(ts_fw_manager_t* mngr); + +/** + * @brief Read a specific Tag from the Factory data partition + * + * @param mngr Pointer to a manager instance + * @param tag Tag to retrieve + * @param content Pointer to a buffer to store the TLV value + * @param max_len Not-to-exceed length of the content buffer + * + * @return Length of the content retrieved if successful, else TS_STATUS_ERROR + */ +int32_t ts_fw_manager_factory_data_retreive(ts_fw_manager_t* mngr, uint32_t tag, uint8_t* content, uint32_t max_len); #ifdef __cplusplus }