From e17c26970e5114a81292897da3f38fc6c589a8dc Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Fri, 23 Jul 2021 18:52:35 +0300 Subject: [PATCH 01/19] Revert "Removed flash_bootloader example temporarily" This reverts commit ee2a04e58b995e1bfa0cb03b91f83a45d446ca7f. --- examples/src/flash_bootloader.cpp | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/src/flash_bootloader.cpp diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp new file mode 100644 index 0000000000..0579e89ebf --- /dev/null +++ b/examples/src/flash_bootloader.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + using namespace std::chrono; + + dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::USB; + if(argc > 1) { + if(std::string(argv[1]) == "usb") { + blType = dai::DeviceBootloader::Type::USB; + } else if(std::string(argv[1]) == "eth") { + blType = dai::DeviceBootloader::Type::NETWORK; + } else { + std::cout << "Specify either 'usb' or 'eth' bootloader type\n"; + return 0; + } + } else { + std::cout << "Usage: " << argv[0] << " \n"; + return 0; + } + + bool res = false; + dai::DeviceInfo info; + std::tie(res, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + + dai::DeviceBootloader bl(info); + auto progress = [](float p) { std::cout << "Flashing Progress..." << p * 100 << "%" << std::endl; }; + + std::string message; + auto t1 = steady_clock::now(); + std::tie(res, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, blType, progress); + if(res) { + std::cout << "Flashing successful. Took " << duration_cast(steady_clock::now() - t1).count() << "ms" << std::endl; + } else { + std::cout << "Flashing failed: " << message << std::endl; + } + return 0; +} \ No newline at end of file From 7ce7614ece441d1f03b64d85976fc946b7bc51fa Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Fri, 23 Jul 2021 18:52:42 +0300 Subject: [PATCH 02/19] Revert "Removed flash_bootloader" This reverts commit f1f03bcefde92b518fe5a1534b83c3fa919e30e6. --- examples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 75327436e6..6573918e63 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -268,7 +268,7 @@ target_compile_definitions(object_tracker_video PRIVATE BLOB_PATH="${person_dete dai_add_example(queue_add_callback src/queue_add_callback.cpp ON) dai_add_example(bootloader_version src/bootloader_version.cpp ON) -#dai_add_example(flash_bootloader src/flash_bootloader.cpp OFF) +dai_add_example(flash_bootloader src/flash_bootloader.cpp OFF) dai_add_example(edge_detector src/edge_detector.cpp ON) # Calibration Read and write samples From eb3defc3b3cc40880d6e24f5ceb5fa37919c2ca0 Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Wed, 28 Jul 2021 20:14:11 +0300 Subject: [PATCH 03/19] Update bootloader: support for more NOR flash chips, fixes issues with flash erasing --- cmake/Depthai/DepthaiBootloaderConfig.cmake | 6 ++++-- shared/depthai-bootloader-shared | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 780c2313f3..7441ed04bf 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -1,5 +1,7 @@ # Maturity level "snapshot" / "release" -set(DEPTHAI_BOOTLOADER_MATURITY "release") +#set(DEPTHAI_BOOTLOADER_MATURITY "release") +set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" -set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") +#set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") +set(DEPTHAI_BOOTLOADER_VERSION "68b296bf866b11174127290b46f9a5f4966102c1") diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 7efb3e1887..391e3431fc 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 7efb3e188715844d7b2b0e50861e4bfc36087370 +Subproject commit 391e3431fcc6f271f60584603697edf664bbb59c From c1e4180f364711655251ac5b7e0d2b5d412416c8 Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Wed, 28 Jul 2021 20:17:14 +0300 Subject: [PATCH 04/19] Optional env var DEPTHAI_BOOTLOADER_BINARY to override bootloader FW path, mostly for development --- src/utility/Resources.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/utility/Resources.cpp b/src/utility/Resources.cpp index 3f8e00f19b..da6697b5b0 100644 --- a/src/utility/Resources.cpp +++ b/src/utility/Resources.cpp @@ -191,6 +191,20 @@ constexpr static std::array RESOURCE_LIST_BOOTLOADER = { }; std::vector Resources::getBootloaderFirmware(dai::bootloader::Type type) { + // Check if env variable DEPTHAI_BOOTLOADER_BINARY is set + auto blBinaryPath = spdlog::details::os::getenv("DEPTHAI_BOOTLOADER_BINARY"); + if(!blBinaryPath.empty()) { + // Load binary file at path + std::ifstream stream(blBinaryPath, std::ios::binary); + if(!stream.is_open()) { + // Throw an error + // TODO(themarpe) - Unify exceptions into meaningful groups + throw std::runtime_error(fmt::format("File at path {} pointed to by DEPTHAI_BOOTLOADER_BINARY doesn't exist.", blBinaryPath)); + } + // Read the file and return its content + return std::vector(std::istreambuf_iterator(stream), {}); + } + // Acquire mutex (this mutex signifies that lazy load is complete) // It is necessary when accessing resourceMap variable std::unique_lock lock(mtxBootloader); From 92a5c0dca19a24619f9513e29bb625be29951844 Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Wed, 28 Jul 2021 20:19:11 +0300 Subject: [PATCH 05/19] Warn when firmware or bootloader binaries are overriden - to confirm it's picked up, or to notice when forgotten exported --- src/utility/Resources.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utility/Resources.cpp b/src/utility/Resources.cpp index da6697b5b0..8eaba3e3b0 100644 --- a/src/utility/Resources.cpp +++ b/src/utility/Resources.cpp @@ -78,6 +78,7 @@ std::vector Resources::getDeviceBinary(OpenVINO::Version version, // TODO(themarpe) - Unify exceptions into meaningful groups throw std::runtime_error(fmt::format("File at path {} pointed to by DEPTHAI_DEVICE_BINARY doesn't exist.", fwBinaryPath)); } + spdlog::warn("Overriding firmware: {}", fwBinaryPath); // Read the file and return its contents return std::vector(std::istreambuf_iterator(stream), {}); } @@ -201,6 +202,7 @@ std::vector Resources::getBootloaderFirmware(dai::bootloader::Type // TODO(themarpe) - Unify exceptions into meaningful groups throw std::runtime_error(fmt::format("File at path {} pointed to by DEPTHAI_BOOTLOADER_BINARY doesn't exist.", blBinaryPath)); } + spdlog::warn("Overriding bootloader: {}", blBinaryPath); // Read the file and return its content return std::vector(std::istreambuf_iterator(stream), {}); } From d2946aa84d2d3dfb2a9839dc2aea111c0920ccc1 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 29 Jul 2021 03:38:08 +0200 Subject: [PATCH 06/19] Moved operator<< overloads to global namespace --- include/depthai/common/CameraBoardSocket.hpp | 15 ++- include/depthai/common/UsbSpeed.hpp | 19 ++-- .../depthai/pipeline/datatype/Tracklets.hpp | 17 ++-- src/utility/BootloaderHelper.hpp | 98 +++++++++---------- 4 files changed, 72 insertions(+), 77 deletions(-) diff --git a/include/depthai/common/CameraBoardSocket.hpp b/include/depthai/common/CameraBoardSocket.hpp index c9871c9185..9a2586c6b8 100644 --- a/include/depthai/common/CameraBoardSocket.hpp +++ b/include/depthai/common/CameraBoardSocket.hpp @@ -4,24 +4,21 @@ #include "depthai-shared/common/CameraBoardSocket.hpp" -namespace dai { - -inline std::ostream& operator<<(std::ostream& out, const CameraBoardSocket& socket) { +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::CameraBoardSocket& socket) { switch(socket) { - case CameraBoardSocket::AUTO: + case dai::CameraBoardSocket::AUTO: out << "AUTO"; break; - case CameraBoardSocket::RGB: + case dai::CameraBoardSocket::RGB: out << "RGB"; break; - case CameraBoardSocket::LEFT: + case dai::CameraBoardSocket::LEFT: out << "LEFT"; break; - case CameraBoardSocket::RIGHT: + case dai::CameraBoardSocket::RIGHT: out << "RIGHT"; break; } return out; } - -} // namespace dai \ No newline at end of file diff --git a/include/depthai/common/UsbSpeed.hpp b/include/depthai/common/UsbSpeed.hpp index e53346bb8e..aef2dc45ed 100644 --- a/include/depthai/common/UsbSpeed.hpp +++ b/include/depthai/common/UsbSpeed.hpp @@ -4,30 +4,27 @@ #include "depthai-shared/common/UsbSpeed.hpp" -namespace dai { - -inline std::ostream& operator<<(std::ostream& out, const UsbSpeed& speed) { +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::UsbSpeed& speed) { switch(speed) { - case UsbSpeed::UNKNOWN: + case dai::UsbSpeed::UNKNOWN: out << "UNKNOWN"; break; - case UsbSpeed::LOW: + case dai::UsbSpeed::LOW: out << "LOW"; break; - case UsbSpeed::FULL: + case dai::UsbSpeed::FULL: out << "FULL"; break; - case UsbSpeed::HIGH: + case dai::UsbSpeed::HIGH: out << "HIGH"; break; - case UsbSpeed::SUPER: + case dai::UsbSpeed::SUPER: out << "SUPER"; break; - case UsbSpeed::SUPER_PLUS: + case dai::UsbSpeed::SUPER_PLUS: out << "SUPER_PLUS"; break; } return out; } - -} // namespace dai diff --git a/include/depthai/pipeline/datatype/Tracklets.hpp b/include/depthai/pipeline/datatype/Tracklets.hpp index 81d77bdec1..8dd483ecc4 100644 --- a/include/depthai/pipeline/datatype/Tracklets.hpp +++ b/include/depthai/pipeline/datatype/Tracklets.hpp @@ -31,22 +31,23 @@ class Tracklets : public Buffer { std::vector& tracklets; }; -inline std::ostream& operator<<(std::ostream& out, const Tracklet::TrackingStatus& status) { +} // namespace dai + +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::Tracklet::TrackingStatus& status) { switch(status) { - case Tracklet::TrackingStatus::NEW: + case dai::Tracklet::TrackingStatus::NEW: out << "NEW"; break; - case Tracklet::TrackingStatus::TRACKED: + case dai::Tracklet::TrackingStatus::TRACKED: out << "TRACKED"; break; - case Tracklet::TrackingStatus::LOST: + case dai::Tracklet::TrackingStatus::LOST: out << "LOST"; break; - case Tracklet::TrackingStatus::REMOVED: + case dai::Tracklet::TrackingStatus::REMOVED: out << "REMOVED"; break; } return out; -} - -} // namespace dai +} \ No newline at end of file diff --git a/src/utility/BootloaderHelper.hpp b/src/utility/BootloaderHelper.hpp index 8fd11b3f49..79e6678ebf 100644 --- a/src/utility/BootloaderHelper.hpp +++ b/src/utility/BootloaderHelper.hpp @@ -13,55 +13,55 @@ namespace dai { - template - bool sendBootloaderRequest(streamId_t streamId, T request){ - if(XLinkWriteData(streamId, (uint8_t*) &request, sizeof(T)) != X_LINK_SUCCESS) return false; - return true; - } - - inline bool receiveBootloaderResponseData(streamId_t streamId, std::vector& data){ - data = std::vector(); - - streamPacketDesc_t* pPacket; - if(XLinkReadData(streamId, &pPacket) != X_LINK_SUCCESS) return false; - - // Resize vector - data.resize(pPacket->length); - - // copy data - memcpy(data.data(), pPacket->data, pPacket->length); - - // release data - if(XLinkReleaseData(streamId) != X_LINK_SUCCESS) return false; - - return true; - } - - template - bool parseBootloaderResponse(const std::vector& data, T& response){ - // Checks that 'data' is type T - dai::bootloader::response::Command command; - if(data.size() < sizeof(command)) return false; - memcpy(&command, data.data(), sizeof(command)); - if(response.cmd != command) return false; - if(data.size() < sizeof(response)) return false; - - // If yes, memcpy to response - memcpy(&response, data.data(), sizeof(response)); - return true; - } - - template - bool receiveBootloaderResponse(streamId_t streamId, T& response){ - // Receive data first - std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return false; - - // Then try to parse - if(!parseBootloaderResponse(data, response)) return false; - - return true; - } +template +bool sendBootloaderRequest(streamId_t streamId, T request){ + if(XLinkWriteData(streamId, (uint8_t*) &request, sizeof(T)) != X_LINK_SUCCESS) return false; + return true; +} + +inline bool receiveBootloaderResponseData(streamId_t streamId, std::vector& data){ + data = std::vector(); + + streamPacketDesc_t* pPacket; + if(XLinkReadData(streamId, &pPacket) != X_LINK_SUCCESS) return false; + + // Resize vector + data.resize(pPacket->length); + + // copy data + memcpy(data.data(), pPacket->data, pPacket->length); + + // release data + if(XLinkReleaseData(streamId) != X_LINK_SUCCESS) return false; + + return true; +} + +template +bool parseBootloaderResponse(const std::vector& data, T& response){ + // Checks that 'data' is type T + dai::bootloader::response::Command command; + if(data.size() < sizeof(command)) return false; + memcpy(&command, data.data(), sizeof(command)); + if(response.cmd != command) return false; + if(data.size() < sizeof(response)) return false; + + // If yes, memcpy to response + memcpy(&response, data.data(), sizeof(response)); + return true; +} + +template +bool receiveBootloaderResponse(streamId_t streamId, T& response){ + // Receive data first + std::vector data; + if(!receiveBootloaderResponseData(streamId, data)) return false; + + // Then try to parse + if(!parseBootloaderResponse(data, response)) return false; + + return true; +} } // namespace dai From 78fdd289dd39c377f6a52ad2a09c01e3ba3dee87 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 29 Jul 2021 03:40:31 +0200 Subject: [PATCH 07/19] Added an explicit flag to allow flashing bootloader --- include/depthai/device/DeviceBootloader.hpp | 80 ++++++++++++++++++--- src/device/DeviceBootloader.cpp | 67 +++++++++++------ 2 files changed, 117 insertions(+), 30 deletions(-) diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index fb5fcb40d9..8cc03cf9d0 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -39,16 +39,16 @@ class DeviceBootloader { Version(unsigned major, unsigned minor, unsigned patch); bool operator==(const Version& other) const; bool operator<(const Version& other) const; - inline bool operator!=(const Version& rhs) { + inline bool operator!=(const Version& rhs) const { return !(*this == rhs); } - inline bool operator>(const Version& rhs) { + inline bool operator>(const Version& rhs) const { return rhs < *this; } - inline bool operator<=(const Version& rhs) { + inline bool operator<=(const Version& rhs) const { return !(*this > rhs); } - inline bool operator>=(const Version& rhs) { + inline bool operator>=(const Version& rhs) const { return !(*this < rhs); } /// Convert Version to string @@ -107,28 +107,31 @@ class DeviceBootloader { /** * Connects to or boots device in bootloader mode depending on devInfo state. * @param devInfo DeviceInfo of which to boot or connect to + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - explicit DeviceBootloader(const DeviceInfo& devInfo); + explicit DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader = false); /** * Connects to device in bootloader of specified type. Throws if it wasn't possible. * This constructor will automatically boot into specified bootloader type if not already running * @param devInfo DeviceInfo of which to boot or connect to * @param type Type of bootloader to boot/connect to. + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - DeviceBootloader(const DeviceInfo& devInfo, Type type); + DeviceBootloader(const DeviceInfo& devInfo, Type type, bool allowFlashingBootloader = false); /** * Connects to or boots device in bootloader mode depending on devInfo state with a custom bootloader firmware. * @param devInfo DeviceInfo of which to boot or connect to * @param pathToBootloader Custom bootloader firmware to boot + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader); + DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader, bool allowFlashingBootloader = false); /** * @overload */ - DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader); + DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader, bool allowFlashingBootloader = false); ~DeviceBootloader(); /** @@ -176,10 +179,21 @@ class DeviceBootloader { Version getVersion(); /** - * @returns True whether the bootloader running is flashed or booted by library + * @returns True when bootloader was booted using latest bootloader integrated in the library. + * False when bootloader is already running on the device and just connected to. */ bool isEmbeddedVersion() const; + /** + * @returns Type of currently connected bootloader + */ + Type getType(); + + /** + * @returns True if allowed to flash bootloader + */ + bool isAllowedFlashingBootloader(); + /** * Explicitly closes connection to device. * @note This function does not need to be explicitly called @@ -196,7 +210,7 @@ class DeviceBootloader { // private static // private variables - void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type); + void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type, bool allowBlFlash); void checkClosed() const; std::shared_ptr connection; @@ -214,6 +228,52 @@ class DeviceBootloader { // bootloader stream std::unique_ptr stream; + + // Allow flashing bootloader flag + bool allowFlashingBootloader = false; }; } // namespace dai + +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Type& type) { + switch(type) { + case dai::DeviceBootloader::Type::USB: + out << "USB"; + break; + case dai::DeviceBootloader::Type::NETWORK: + out << "NETWORK"; + break; + } + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Memory& memory) { + switch(memory) { + case dai::DeviceBootloader::Memory::FLASH: + out << "FLASH"; + break; + case dai::DeviceBootloader::Memory::EMMC: + out << "EMMC"; + break; + } + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Section& type) { + switch(type) { + case dai::DeviceBootloader::Section::HEADER: + out << "HEADER"; + break; + case dai::DeviceBootloader::Section::BOOTLOADER: + out << "BOOTLOADER"; + break; + case dai::DeviceBootloader::Section::BOOTLOADER_CONFIG: + out << "BOOTLOADER_CONFIG"; + break; + case dai::DeviceBootloader::Section::APPLICATION: + out << "APPLICATION"; + break; + } + return out; +} \ No newline at end of file diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 43ebc7667d..223673cc30 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -141,24 +141,25 @@ std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& return fwPackage; } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo) : deviceInfo(devInfo) { - init(true, "", tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", tl::nullopt, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, Type type) : deviceInfo(devInfo) { - init(true, "", type); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, Type type, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", type, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader) : deviceInfo(devInfo) { - init(false, std::string(pathToBootloader), tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(false, std::string(pathToBootloader), tl::nullopt, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader) : deviceInfo(devInfo) { - init(false, pathToBootloader, tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(false, pathToBootloader, tl::nullopt, allowFlashingBootloader); } -void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type) { +void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type, bool allowBlFlash) { stream = nullptr; + allowFlashingBootloader = allowBlFlash; bootloaderType = type.value_or(DEFAULT_TYPE); @@ -194,8 +195,9 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, if(!receiveBootloaderResponse(stream->getStreamId(), ver)) throw std::runtime_error("Error trying to connect to device"); DeviceBootloader::Version version(ver.major, ver.minor, ver.patch); - // If version is adequite if(version >= Version(0, 0, 12)) { + // If version is adequate, do an in memory boot. + // Send request for bootloader type if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderType{})) { throw std::runtime_error("Error trying to connect to device"); @@ -207,8 +209,10 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Modify actual bootloader type bootloaderType = runningBootloaderType.type; - // Boot memory correct type of BL - if(type && runningBootloaderType.type != *type) { + Type desiredBootloaderType = type.value_or(bootloaderType); + + // If not correct type OR if allowFlashingBootloader is set, then boot internal (latest) bootloader of correct type + if((desiredBootloaderType != bootloaderType) || allowFlashingBootloader) { // prepare watchdog thread, which will keep device alive std::atomic wdRunning{true}; std::thread wd = std::thread([&]() { @@ -228,7 +232,7 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Send request to boot firmware directly from bootloader dai::bootloader::request::BootMemory bootMemory; - auto binary = getEmbeddedBootloaderBinary(*type); + auto binary = getEmbeddedBootloaderBinary(desiredBootloaderType); bootMemory.totalSize = static_cast(binary.size()); bootMemory.numPackets = ((static_cast(binary.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; if(!sendBootloaderRequest(stream->getStreamId(), bootMemory)) { @@ -249,13 +253,22 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Now connect connection = std::make_shared(deviceInfo, X_LINK_BOOTLOADER); - isEmbedded = false; - } else { + // The type of bootloader is now 'desiredBootloaderType' + bootloaderType = desiredBootloaderType; + + // Embedded bootloader was used to boot, set to true isEmbedded = true; + } else { + // Just connected to existing bootloader on device. Set embedded to false + isEmbedded = false; } } else { - if(type && *type != Type::USB) { + // If version isn't adequate to do an in memory boot - do regular Bootloader -> USB ROM -> Boot transition. + Type desiredBootloaderType = type.value_or(Type::USB); + + // If not correct type OR if allowFlashingBootloader is set, then boot internal (latest) bootloader of correct type + if((desiredBootloaderType != Type::USB) || allowFlashingBootloader) { // Send request to jump to USB bootloader // Boot into USB ROM BOOTLOADER NOW if(!sendBootloaderRequest(stream->getStreamId(), dai::bootloader::request::UsbRomBoot{})) { @@ -268,19 +281,20 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Unbooted device found, boot to BOOTLOADER and connect with XLinkConnection constructor if(embeddedMvcmd) { - connection = std::make_shared(deviceInfo, getEmbeddedBootloaderBinary(*type), X_LINK_BOOTLOADER); + connection = std::make_shared(deviceInfo, getEmbeddedBootloaderBinary(desiredBootloaderType), X_LINK_BOOTLOADER); } else { connection = std::make_shared(deviceInfo, pathToMvcmd, X_LINK_BOOTLOADER); } - bootloaderType = *type; + bootloaderType = desiredBootloaderType; - // Device wasn't already in bootloader, that means that embedded bootloader is booted + // Embedded bootloader was used to boot, set to true isEmbedded = true; } else { bootloaderType = dai::bootloader::Type::USB; - // Device was already in bootloader, that means that embedded isn't running + + // Just connected to existing bootloader on device. Set embedded to false isEmbedded = false; } } @@ -385,6 +399,14 @@ DeviceBootloader::Version DeviceBootloader::getVersion() { return DeviceBootloader::Version(ver.major, ver.minor, ver.patch); } +DeviceBootloader::Type DeviceBootloader::getType() { + return bootloaderType; +} + +bool DeviceBootloader::isAllowedFlashingBootloader() { + return allowFlashingBootloader; +} + std::tuple DeviceBootloader::flash(std::function progressCb, Pipeline& pipeline) { return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline)); } @@ -439,6 +461,11 @@ std::tuple DeviceBootloader::flashBootloader(std::function DeviceBootloader::flashBootloader(Memory memory, Type type, std::function progressCb, std::string path) { + // Check if 'allowFlashingBootloader' is set to true. + if(!allowFlashingBootloader) { + throw std::invalid_argument("DeviceBootloader wasn't initialized to allow flashing bootloader. Set 'allowFlashingBootloader' in constructor"); + } + // Only flash memory is supported for now if(memory != Memory::FLASH) { throw std::invalid_argument("Only FLASH memory is supported for now"); From 4cac8173dc49dd844d71c860e49562d74dc8978f Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 29 Jul 2021 04:31:56 +0200 Subject: [PATCH 08/19] Updated flash_bootloader to be a bit more verbose --- examples/src/flash_bootloader.cpp | 55 +++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp index 0579e89ebf..30559955f5 100644 --- a/examples/src/flash_bootloader.cpp +++ b/examples/src/flash_bootloader.cpp @@ -6,32 +6,67 @@ int main(int argc, char** argv) { using namespace std::chrono; + char confirmation; + dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::USB; + std::string blTypeStr; if(argc > 1) { - if(std::string(argv[1]) == "usb") { + blTypeStr = std::string(argv[1]); + if(blTypeStr == "usb") { blType = dai::DeviceBootloader::Type::USB; - } else if(std::string(argv[1]) == "eth") { + } else if(blTypeStr == "network") { blType = dai::DeviceBootloader::Type::NETWORK; } else { - std::cout << "Specify either 'usb' or 'eth' bootloader type\n"; + std::cout << "Specify either 'usb' or 'network' bootloader type\n"; return 0; } } else { - std::cout << "Usage: " << argv[0] << " \n"; + std::cout << "Usage: " << argv[0] << " \n"; return 0; } - bool res = false; + std::cout << "Warning! Flashing bootloader can potentially soft brick your device and should be done with caution." << std::endl; + std::cout << "Do not unplug your device in while the bootloader is flashing." << std::endl; + std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; + confirmation = std::cin.get(); + if(confirmation != 'y') { + std::cout << "Prompt declined, exiting..." << std::endl; + return -1; + } + + bool found = false; dai::DeviceInfo info; - std::tie(res, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + std::tie(found, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + if(!found) { + std::cout << "No device found to flash. Exiting." << std::endl; + return -1; + } + + // Open DeviceBootloader and allow flashing bootloader + dai::DeviceBootloader bl(info, true); + auto currentBlType = bl.getType(); + std::cout << "Connected to already running bootloader: " << !bl.isEmbeddedVersion() << std::endl; - dai::DeviceBootloader bl(info); + // Check if bootloader type is the same + if(currentBlType != blType) { + std::cout << "Are you sure you want to flash '" << blType << "' bootloader over current '" << currentBlType << "' bootloader?" << std::endl; + std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; + std::cin.ignore(); + confirmation = std::cin.get(); + if(confirmation != 'y') { + std::cout << "Prompt declined, exiting..." << std::endl; + return -1; + } + } + + // Create a progress callback lambda auto progress = [](float p) { std::cout << "Flashing Progress..." << p * 100 << "%" << std::endl; }; - std::string message; auto t1 = steady_clock::now(); - std::tie(res, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, blType, progress); - if(res) { + bool success = false; + std::string message; + std::tie(success, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, blType, progress); + if(success) { std::cout << "Flashing successful. Took " << duration_cast(steady_clock::now() - t1).count() << "ms" << std::endl; } else { std::cout << "Flashing failed: " << message << std::endl; From 9eedc3531d14832e98d8c1ea336e5f38dad819b9 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 29 Jul 2021 04:59:03 +0200 Subject: [PATCH 09/19] Improved the flash_bootloader example a bit --- examples/src/flash_bootloader.cpp | 10 +++------- include/depthai/device/DeviceBootloader.hpp | 4 ++-- src/device/DeviceBootloader.cpp | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp index 30559955f5..a7aafda279 100644 --- a/examples/src/flash_bootloader.cpp +++ b/examples/src/flash_bootloader.cpp @@ -6,12 +6,9 @@ int main(int argc, char** argv) { using namespace std::chrono; - char confirmation; - dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::USB; - std::string blTypeStr; if(argc > 1) { - blTypeStr = std::string(argv[1]); + std::string blTypeStr(argv[1]); if(blTypeStr == "usb") { blType = dai::DeviceBootloader::Type::USB; } else if(blTypeStr == "network") { @@ -28,8 +25,7 @@ int main(int argc, char** argv) { std::cout << "Warning! Flashing bootloader can potentially soft brick your device and should be done with caution." << std::endl; std::cout << "Do not unplug your device in while the bootloader is flashing." << std::endl; std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; - confirmation = std::cin.get(); - if(confirmation != 'y') { + if(std::cin.get() != 'y') { std::cout << "Prompt declined, exiting..." << std::endl; return -1; } @@ -45,7 +41,6 @@ int main(int argc, char** argv) { // Open DeviceBootloader and allow flashing bootloader dai::DeviceBootloader bl(info, true); auto currentBlType = bl.getType(); - std::cout << "Connected to already running bootloader: " << !bl.isEmbeddedVersion() << std::endl; // Check if bootloader type is the same if(currentBlType != blType) { @@ -62,6 +57,7 @@ int main(int argc, char** argv) { // Create a progress callback lambda auto progress = [](float p) { std::cout << "Flashing Progress..." << p * 100 << "%" << std::endl; }; + std::cout << "Flashing " << blType << " bootloader..." << std::endl; auto t1 = steady_clock::now(); bool success = false; std::string message; diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index 8cc03cf9d0..ec9436cce9 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -187,12 +187,12 @@ class DeviceBootloader { /** * @returns Type of currently connected bootloader */ - Type getType(); + Type getType() const; /** * @returns True if allowed to flash bootloader */ - bool isAllowedFlashingBootloader(); + bool isAllowedFlashingBootloader() const; /** * Explicitly closes connection to device. diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 223673cc30..3e6a936710 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -399,11 +399,11 @@ DeviceBootloader::Version DeviceBootloader::getVersion() { return DeviceBootloader::Version(ver.major, ver.minor, ver.patch); } -DeviceBootloader::Type DeviceBootloader::getType() { +DeviceBootloader::Type DeviceBootloader::getType() const { return bootloaderType; } -bool DeviceBootloader::isAllowedFlashingBootloader() { +bool DeviceBootloader::isAllowedFlashingBootloader() const { return allowFlashingBootloader; } From 08e6ad7f0765340e07a0bbb846a72b4b02cb2ca7 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 29 Jul 2021 15:16:11 +0200 Subject: [PATCH 10/19] Updated flash_bootloader example --- examples/src/flash_bootloader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp index a7aafda279..5886b01999 100644 --- a/examples/src/flash_bootloader.cpp +++ b/examples/src/flash_bootloader.cpp @@ -47,8 +47,7 @@ int main(int argc, char** argv) { std::cout << "Are you sure you want to flash '" << blType << "' bootloader over current '" << currentBlType << "' bootloader?" << std::endl; std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; std::cin.ignore(); - confirmation = std::cin.get(); - if(confirmation != 'y') { + if(std::cin.get() != 'y') { std::cout << "Prompt declined, exiting..." << std::endl; return -1; } From 29a3590556b45259676db81ee6bfc9a79ce98fc8 Mon Sep 17 00:00:00 2001 From: alex-luxonis Date: Tue, 3 Aug 2021 18:36:27 +0300 Subject: [PATCH 11/19] Allow to specify which bootloader is overridden by the env var: `DEPTHAI_BOOTLOADER_BINARY_USB` `DEPTHAI_BOOTLOADER_BINARY_ETH` (both can be set) --- src/utility/Resources.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/utility/Resources.cpp b/src/utility/Resources.cpp index 8eaba3e3b0..8804be82af 100644 --- a/src/utility/Resources.cpp +++ b/src/utility/Resources.cpp @@ -192,17 +192,23 @@ constexpr static std::array RESOURCE_LIST_BOOTLOADER = { }; std::vector Resources::getBootloaderFirmware(dai::bootloader::Type type) { - // Check if env variable DEPTHAI_BOOTLOADER_BINARY is set - auto blBinaryPath = spdlog::details::os::getenv("DEPTHAI_BOOTLOADER_BINARY"); + // Check if env variable DEPTHAI_BOOTLOADER_BINARY_USB/_ETH is set + std::string blEnvVar; + if(type == dai::bootloader::Type::USB) { + blEnvVar = "DEPTHAI_BOOTLOADER_BINARY_USB"; + } else if(type == dai::bootloader::Type::NETWORK) { + blEnvVar = "DEPTHAI_BOOTLOADER_BINARY_ETH"; + } + auto blBinaryPath = spdlog::details::os::getenv(blEnvVar.c_str()); if(!blBinaryPath.empty()) { // Load binary file at path std::ifstream stream(blBinaryPath, std::ios::binary); if(!stream.is_open()) { // Throw an error // TODO(themarpe) - Unify exceptions into meaningful groups - throw std::runtime_error(fmt::format("File at path {} pointed to by DEPTHAI_BOOTLOADER_BINARY doesn't exist.", blBinaryPath)); + throw std::runtime_error(fmt::format("File at path {} pointed to by {} doesn't exist.", blBinaryPath, blEnvVar)); } - spdlog::warn("Overriding bootloader: {}", blBinaryPath); + spdlog::warn("Overriding bootloader {}: {}", blEnvVar, blBinaryPath); // Read the file and return its content return std::vector(std::istreambuf_iterator(stream), {}); } From 819e9e2211041113ed8f30f203d9ca12ba03b2cd Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Wed, 4 Aug 2021 19:52:10 +0200 Subject: [PATCH 12/19] Fixed boot_memory bootloader upgrade routine --- cmake/Depthai/DepthaiBootloaderConfig.cmake | 2 +- examples/src/flash_bootloader.cpp | 2 +- src/device/DeviceBootloader.cpp | 32 ++++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 7441ed04bf..8195330bbd 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" #set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") -set(DEPTHAI_BOOTLOADER_VERSION "68b296bf866b11174127290b46f9a5f4966102c1") +set(DEPTHAI_BOOTLOADER_VERSION "d3119797ed0cc301e934be12fcbda283f307e4e1") diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp index 5886b01999..e1847c9182 100644 --- a/examples/src/flash_bootloader.cpp +++ b/examples/src/flash_bootloader.cpp @@ -23,7 +23,7 @@ int main(int argc, char** argv) { } std::cout << "Warning! Flashing bootloader can potentially soft brick your device and should be done with caution." << std::endl; - std::cout << "Do not unplug your device in while the bootloader is flashing." << std::endl; + std::cout << "Do not unplug your device while the bootloader is flashing." << std::endl; std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; if(std::cin.get() != 'y') { std::cout << "Prompt declined, exiting..." << std::endl; diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 3e6a936710..933a260a5d 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -183,7 +183,7 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, connection = std::make_shared(deviceInfo, X_LINK_BOOTLOADER); // If type is specified, try to boot into that BL type - stream = std::unique_ptr(new XLinkStream(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE)); + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); // Send request for bootloader version if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderVersion{})) { @@ -241,17 +241,18 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // After that send numPackets of data stream->writeSplit(binary.data(), binary.size(), bootloader::XLINK_STREAM_MAX_SIZE); - + // Close existing stream first + stream = nullptr; // Stop watchdog wdRunning = false; wd.join(); + // Close connection + connection->close(); - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream->getStreamId(), &pPacket); - - // Now connect + // Now reconnect connection = std::make_shared(deviceInfo, X_LINK_BOOTLOADER); + // prepare new bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); // The type of bootloader is now 'desiredBootloaderType' bootloaderType = desiredBootloaderType; @@ -274,11 +275,12 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, if(!sendBootloaderRequest(stream->getStreamId(), dai::bootloader::request::UsbRomBoot{})) { throw std::runtime_error("Error trying to connect to device"); } + // Close existing stream first + stream = nullptr; + // Close connection + connection->close(); - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream->getStreamId(), &pPacket); - + // Now reconnect // Unbooted device found, boot to BOOTLOADER and connect with XLinkConnection constructor if(embeddedMvcmd) { connection = std::make_shared(deviceInfo, getEmbeddedBootloaderBinary(desiredBootloaderType), X_LINK_BOOTLOADER); @@ -286,6 +288,9 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, connection = std::make_shared(deviceInfo, pathToMvcmd, X_LINK_BOOTLOADER); } + // prepare bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + bootloaderType = desiredBootloaderType; // Embedded bootloader was used to boot, set to true @@ -334,11 +339,6 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Sleep a bit, so device isn't available anymore std::this_thread::sleep_for(std::chrono::milliseconds(500)); }); - - // prepare bootloader stream - if(stream == nullptr) { - stream = std::unique_ptr(new XLinkStream(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE)); - } } void DeviceBootloader::close() { From f7711f9091998871e4c07d2efe7f13fab9a0e94f Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Mon, 16 Aug 2021 13:26:17 +0200 Subject: [PATCH 13/19] WIP: Bootloader configuration --- examples/CMakeLists.txt | 5 +- examples/src/bootloader_configuration.cpp | 60 ++++++++++++++ include/depthai/device/DeviceBootloader.hpp | 52 ++++++++++++ shared/depthai-bootloader-shared | 2 +- src/device/DeviceBootloader.cpp | 87 +++++++++++++++++++++ 5 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 examples/src/bootloader_configuration.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 94f4143515..7c726f8b54 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -289,4 +289,7 @@ dai_add_example(image_manip_tiling src/image_manip_tiling.cpp ON) target_compile_definitions(calibration_flash PRIVATE CALIB_PATH="${calib_v6}") target_compile_definitions(calibration_flash_version5 PRIVATE CALIB_PATH="${calib_v5}" BOARD_PATH="${device_config}") -target_compile_definitions(calibration_load PRIVATE CALIB_PATH="${calib_v6}") \ No newline at end of file +target_compile_definitions(calibration_load PRIVATE CALIB_PATH="${calib_v6}") + +# Bootloader configuration example +dai_add_example(bootloader_configuration src/bootloader_configuration.cpp OFF) diff --git a/examples/src/bootloader_configuration.cpp b/examples/src/bootloader_configuration.cpp new file mode 100644 index 0000000000..a956224a79 --- /dev/null +++ b/examples/src/bootloader_configuration.cpp @@ -0,0 +1,60 @@ +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + bool read = true, clear = false; + std::string path = ""; + if(argc >= 2) { + std::string op = argv[1]; + if(op == "read") { + read = true; + } else if(op == "flash") { + read = false; + if(argc >= 3) { + path = argv[2]; + } else if(op == "clear") { + clear = true; + read = false; + } else { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path/to/config/json]" << std::endl; + return -1; + } + } else { + std::cout << "Usage: " << argv[0] << " [read/flash] [flash: path/to/config/json]" << std::endl; + return -1; + } + } else { + std::cout << "Usage: " << argv[0] << " [read/flash] [flash: path/to/config/json]" << std::endl; + return -1; + } + + bool res = false; + dai::DeviceInfo info; + std::tie(res, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + + if(res) { + std::cout << "Found device with name: " << info.desc.name << std::endl; + dai::DeviceBootloader bl(info); + + if(read) { + std::cout << "Current flashed configuration\n" << bl.readConfigurationData().dump(4) << std::endl; + } else { + bool success; + std::string error; + if(clear) { + std::tie(success, error) = bl.flashConfigurationClear(); + } else { + std::tie(success, error) = bl.flashConfigurationFile(path); + } + if(success) { + std::cout << "Successfully flashed bootloader configuration\n"; + } else { + std::cout << "Error flashing bootloader configuration: " << error; + } + } + + } else { + std::cout << "No devices found" << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index ec9436cce9..c0a65b7cb8 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -13,6 +13,7 @@ #include "depthai/xlink/XLinkStream.hpp" // shared +#include "depthai-bootloader-shared/Config.hpp" #include "depthai-bootloader-shared/Memory.hpp" #include "depthai-bootloader-shared/Section.hpp" #include "depthai-bootloader-shared/Type.hpp" @@ -30,6 +31,9 @@ class DeviceBootloader { using Type = dai::bootloader::Type; using Memory = dai::bootloader::Memory; using Section = dai::bootloader::Section; + using UsbConfig = dai::bootloader::UsbConfig; + using NetworkConfig = dai::bootloader::NetworkConfig; + using Config = dai::bootloader::Config; /// Bootloader version structure struct Version { @@ -173,6 +177,48 @@ class DeviceBootloader { */ // std::tuple flashCustom(Memory memory, uint32_t offset, std::function progressCb, std::vector data); + /** + * Reads configuration data from bootloader + * @returns Unstructured configuration data + */ + nlohmann::json readConfigurationData(Memory memory = Memory::AUTO); + + /** + * Flashes configuration data to bootloader + * @param configData Unstructured configuration data + */ + std::tuple flashConfigurationData(nlohmann::json configData, Memory memory = Memory::AUTO); + + /** + * Flashes JSON configuration data to bootloader + * @param configJson JSON configuration data + */ + std::tuple flashConfigurationData(std::string configJson, Memory memory = Memory::AUTO); + + /** + * Flashes configuration data to bootloader + * @param configPath Unstructured configuration data + */ + std::tuple flashConfigurationFile(std::string configPath, Memory memory = Memory::AUTO); + + /** + * Flashes configuration data to bootloader + * @param configData Unstructured configuration data + */ + std::tuple flashConfigurationClear(Memory memory = Memory::AUTO); + + /** + * Reads configuration from bootloader + * @returns Configuration + */ + Config readConfiguration(Memory memory = Memory::AUTO); + + /** + * Flashes configuration to bootloader + * @param configData Configuration + */ + std::tuple flashConfiguration(const Config& config); + /** * @returns Version of current running bootloader */ @@ -250,6 +296,9 @@ inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader:: inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Memory& memory) { switch(memory) { + case dai::DeviceBootloader::Memory::AUTO: + out << "AUTO"; + break; case dai::DeviceBootloader::Memory::FLASH: out << "FLASH"; break; @@ -262,6 +311,9 @@ inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader:: inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Section& type) { switch(type) { + case dai::DeviceBootloader::Section::AUTO: + out << "AUTO"; + break; case dai::DeviceBootloader::Section::HEADER: out << "HEADER"; break; diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 391e3431fc..ff0388376d 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 391e3431fcc6f271f60584603697edf664bbb59c +Subproject commit ff0388376d272cabdb79bf68f21fd7130d8212a6 diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 34b18c981e..5cbef6486e 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -6,6 +6,7 @@ // shared #include "depthai-bootloader-shared/Bootloader.hpp" #include "depthai-bootloader-shared/SBR.h" +#include "depthai-bootloader-shared/Structure.hpp" #include "depthai-bootloader-shared/XLinkConstants.hpp" #include "depthai-shared/datatype/RawImgFrame.hpp" #include "depthai-shared/pipeline/Assets.hpp" @@ -172,6 +173,9 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, connection = std::make_shared(deviceInfo, pathToMvcmd, X_LINK_BOOTLOADER); } + // prepare bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + // Device wasn't already in bootloader, that means that embedded bootloader is booted isEmbedded = true; } else if(deviceInfo.state == X_LINK_BOOTLOADER) { @@ -596,6 +600,89 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 } */ +nlohmann::json DeviceBootloader::readConfigurationData(Memory memory) { + // Send request to GET_BOOTLOADER_CONFIG + dai::bootloader::request::GetBootloaderConfig getConfigReq; + getConfigReq.memory = memory; + if(!sendBootloaderRequest(stream->getStreamId(), getConfigReq)) return {false, "Couldn't send request to get configuration data"}; + + // Get response + dai::bootloader::response::GetBootloaderConfig resp; + receiveBootloaderResponse(stream->getStreamId(), resp); + + if(resp.success) { + // Read back bootloader config (1 packet max) + auto bsonConfig = stream->read(); + // Parse from BSON + return nlohmann::json::from_bson(bsonConfig); + } else { + return {}; + } +} + +std::tuple DeviceBootloader::flashConfigurationClear(Memory memory) { + // send request to SET_BOOTLOADER_CONFIG + dai::bootloader::request::SetBootloaderConfig setConfigReq; + setConfigReq.memory = memory; + setConfigReq.numPackets = 0; + setConfigReq.totalSize = 0; + setConfigReq.clearConfig = 1; + if(!sendBootloaderRequest(stream->getStreamId(), setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + + // Read back response + dai::bootloader::response::FlashComplete result; + receiveBootloaderResponse(stream->getStreamId(), result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +std::tuple DeviceBootloader::flashConfigurationData(nlohmann::json configData, Memory memory) { + // Parse to BSON + auto bson = nlohmann::json::to_bson(configData); + + // Send request to SET_BOOTLOADER_CONFIG + dai::bootloader::request::SetBootloaderConfig setConfigReq; + setConfigReq.memory = memory; + setConfigReq.numPackets = 1; + setConfigReq.totalSize = bson.size(); + setConfigReq.clearConfig = 0; + if(!sendBootloaderRequest(stream->getStreamId(), setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + + // Send 1 packet, of bson config data + stream->write(bson); + + // Read back response + dai::bootloader::response::FlashComplete result; + receiveBootloaderResponse(stream->getStreamId(), result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +std::tuple DeviceBootloader::flashConfigurationData(std::string configJson, Memory memory) { + return flashConfigurationData(nlohmann::json::parse(configJson), memory); +} + +std::tuple DeviceBootloader::flashConfigurationFile(std::string configPath, Memory memory) { + // read a JSON file + std::ifstream configInputStream(configPath); + nlohmann::json configJson; + configInputStream >> configJson; + return flashConfigurationData(configJson, memory); +} + +DeviceBootloader::Config DeviceBootloader::readConfiguration(Memory memory) { + auto json = readConfigurationData(memory); + // Implicit parse from json to Config + return json; +} + +std::tuple DeviceBootloader::flashConfiguration(const Config& config) { + // Implicit parse from Config to json + return flashConfigurationData(config); +} + bool DeviceBootloader::isEmbeddedVersion() const { return isEmbedded; } From 02f51a8dd4f363d6a2d3d67d58c73667602c5d46 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 16 Aug 2021 20:42:26 +0200 Subject: [PATCH 14/19] Updated bootloader_configuration example --- examples/src/bootloader_configuration.cpp | 29 +++++++++++++++-------- shared/depthai-bootloader-shared | 2 +- src/device/DeviceBootloader.cpp | 1 + 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/src/bootloader_configuration.cpp b/examples/src/bootloader_configuration.cpp index a956224a79..546b812a3a 100644 --- a/examples/src/bootloader_configuration.cpp +++ b/examples/src/bootloader_configuration.cpp @@ -1,6 +1,8 @@ #include "depthai/depthai.hpp" int main(int argc, char** argv) { + // Options + bool usage = false; bool read = true, clear = false; std::string path = ""; if(argc >= 2) { @@ -11,22 +13,25 @@ int main(int argc, char** argv) { read = false; if(argc >= 3) { path = argv[2]; - } else if(op == "clear") { - clear = true; - read = false; - } else { - std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path/to/config/json]" << std::endl; - return -1; } + } else if(op == "clear") { + clear = true; + read = false; + } else if(op == "clear") { + clear = true; + read = false; } else { - std::cout << "Usage: " << argv[0] << " [read/flash] [flash: path/to/config/json]" << std::endl; - return -1; + usage = true; } } else { - std::cout << "Usage: " << argv[0] << " [read/flash] [flash: path/to/config/json]" << std::endl; + usage = true; + } + if(usage) { + std::cout << "Usage: " << argv[0] << " [read/flash/clear] [flash: path/to/config/json]" << std::endl; return -1; } + // DeviceBootloader configuration bool res = false; dai::DeviceInfo info; std::tie(res, info) = dai::DeviceBootloader::getFirstAvailableDevice(); @@ -43,7 +48,11 @@ int main(int argc, char** argv) { if(clear) { std::tie(success, error) = bl.flashConfigurationClear(); } else { - std::tie(success, error) = bl.flashConfigurationFile(path); + if(path.empty()) { + std::tie(success, error) = bl.flashConfiguration(dai::DeviceBootloader::Config{}); + } else { + std::tie(success, error) = bl.flashConfigurationFile(path); + } } if(success) { std::cout << "Successfully flashed bootloader configuration\n"; diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index ff0388376d..2771f02436 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit ff0388376d272cabdb79bf68f21fd7130d8212a6 +Subproject commit 2771f02436c80c211b17295754966be48123ebbf diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 5cbef6486e..8be491ef71 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -667,6 +667,7 @@ std::tuple DeviceBootloader::flashConfigurationData(std::stri std::tuple DeviceBootloader::flashConfigurationFile(std::string configPath, Memory memory) { // read a JSON file std::ifstream configInputStream(configPath); + if(!configInputStream.is_open()) throw std::runtime_error("Cannot flash configuration, JSON at path: " + configPath + " doesn't exist"); nlohmann::json configJson; configInputStream >> configJson; return flashConfigurationData(configJson, memory); From 5dc961ab5eb8ec4feb6e351760c6f647c69f235b Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Thu, 19 Aug 2021 10:27:52 +0200 Subject: [PATCH 15/19] Reduced BL check to 0.0.14 and updated FW and BL --- cmake/Depthai/DepthaiBootloaderConfig.cmake | 2 +- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- shared/depthai-bootloader-shared | 2 +- src/device/DeviceBase.cpp | 5 +++-- src/device/DeviceBootloader.cpp | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 8195330bbd..2bef389720 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" #set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") -set(DEPTHAI_BOOTLOADER_VERSION "d3119797ed0cc301e934be12fcbda283f307e4e1") +set(DEPTHAI_BOOTLOADER_VERSION "d404136d0b8d871f738258d8bfa29b6b6b00b98f") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 78e0ce9fc8..ced128ae6d 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "a2baa42ba41f7b79dcbee987b34f24da075de196") +set(DEPTHAI_DEVICE_SIDE_COMMIT "a0a53662404cb8cf126bbefa6365f3a4635a2a16") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 2771f02436..678ff68015 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 2771f02436c80c211b17295754966be48123ebbf +Subproject commit 678ff68015661137137c454383cc2ff7bf82f016 diff --git a/src/device/DeviceBase.cpp b/src/device/DeviceBase.cpp index fd797a6245..452740aa99 100644 --- a/src/device/DeviceBase.cpp +++ b/src/device/DeviceBase.cpp @@ -318,11 +318,12 @@ void DeviceBase::closeImpl() { auto t1 = steady_clock::now(); spdlog::debug("Device about to be closed..."); - // Close connection first (so queues unblock) + // Close connection first + // Resets device as well and queues unblock connection->close(); connection = nullptr; - // Stop watchdog + // Stop various threads watchdogRunning = false; timesyncRunning = false; loggingRunning = false; diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 8be491ef71..ea75b69d95 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -426,7 +426,7 @@ std::tuple DeviceBootloader::flashDepthaiApplicationPackage(s // Bug in NETWORK bootloader in version 0.0.12 < 0.1.0 - flashing can cause a soft brick auto version = getVersion(); - if(bootloaderType == Type::NETWORK && version < Version(0, 1, 0)) { + if(bootloaderType == Type::NETWORK && version < Version(0, 0, 14)) { throw std::invalid_argument("Network bootloader requires version 0.1.0 or higher to flash applications. Current version: " + version.toString()); } From 93df9978e301265229ca8a613a47397cf5f3dc47 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Sat, 21 Aug 2021 22:34:21 +0200 Subject: [PATCH 16/19] Added capability to compress FW and additional BL config helper --- CMakeLists.txt | 2 + cmake/Depthai/DepthaiBootloaderConfig.cmake | 2 +- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- cmake/depthaiDependencies.cmake | 7 +- examples/src/bootloader_configuration.cpp | 11 +- include/depthai/device/DeviceBootloader.hpp | 122 ++++++++++-- shared/depthai-bootloader-shared | 2 +- src/device/DeviceBootloader.cpp | 208 +++++++++++++++++--- src/utility/Platform.cpp | 47 +++++ src/utility/Platform.hpp | 13 ++ src/utility/Resources.cpp | 4 + tests/CMakeLists.txt | 2 + tests/src/bootloader_config_test.cpp | 39 ++++ 13 files changed, 405 insertions(+), 56 deletions(-) create mode 100644 src/utility/Platform.cpp create mode 100644 src/utility/Platform.hpp create mode 100644 tests/src/bootloader_config_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e0984ef20..980ad50554 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,7 @@ add_library(${TARGET_CORE_NAME} src/pipeline/datatype/FeatureTrackerConfig.cpp src/utility/Initialization.cpp src/utility/Resources.cpp + src/utility/Platform.cpp src/xlink/XLinkConnection.cpp src/xlink/XLinkStream.cpp src/openvino/OpenVINO.cpp @@ -366,6 +367,7 @@ target_link_libraries(${TARGET_CORE_NAME} FP16::fp16 archive_static spdlog::spdlog + ZLIB::zlib ) # Add compile definitions diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 2bef389720..e91bab56bb 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" #set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") -set(DEPTHAI_BOOTLOADER_VERSION "d404136d0b8d871f738258d8bfa29b6b6b00b98f") +set(DEPTHAI_BOOTLOADER_VERSION "a243221305bd0351cc2c9b98037e931068f4afef") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index ced128ae6d..6c439da18f 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "a0a53662404cb8cf126bbefa6365f3a4635a2a16") +set(DEPTHAI_DEVICE_SIDE_COMMIT "9b857aedf34a19c7738a2f7f2f16a39f4e3831b3") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") diff --git a/cmake/depthaiDependencies.cmake b/cmake/depthaiDependencies.cmake index 3a9ba9d184..d74abb4107 100644 --- a/cmake/depthaiDependencies.cmake +++ b/cmake/depthaiDependencies.cmake @@ -12,11 +12,12 @@ else() hunter_add_package(FP16) hunter_add_package(libarchive-luxonis) hunter_add_package(spdlog) + hunter_add_package(ZLIB) endif() # If library was build as static, find all dependencies if(NOT CONFIG_MODE OR (CONFIG_MODE AND NOT depthai_SHARED_LIBS)) - + # BZip2 (for bspatch) find_package(BZip2 ${_QUIET} CONFIG REQUIRED) @@ -26,10 +27,12 @@ if(NOT CONFIG_MODE OR (CONFIG_MODE AND NOT depthai_SHARED_LIBS)) # libarchive for firmware packages find_package(archive_static ${_QUIET} CONFIG REQUIRED) find_package(lzma ${_QUIET} CONFIG REQUIRED) + # ZLIB for compressing Apps + find_package(ZLIB CONFIG REQUIRED) # spdlog for library and device logging find_package(spdlog ${_QUIET} CONFIG REQUIRED) - + endif() # Add threads (c++) diff --git a/examples/src/bootloader_configuration.cpp b/examples/src/bootloader_configuration.cpp index 546b812a3a..b2eb9564a3 100644 --- a/examples/src/bootloader_configuration.cpp +++ b/examples/src/bootloader_configuration.cpp @@ -2,8 +2,7 @@ int main(int argc, char** argv) { // Options - bool usage = false; - bool read = true, clear = false; + bool usage = false, read = true, clear = false; std::string path = ""; if(argc >= 2) { std::string op = argv[1]; @@ -41,17 +40,17 @@ int main(int argc, char** argv) { dai::DeviceBootloader bl(info); if(read) { - std::cout << "Current flashed configuration\n" << bl.readConfigurationData().dump(4) << std::endl; + std::cout << "Current flashed configuration\n" << bl.readConfigData().dump(4) << std::endl; } else { bool success; std::string error; if(clear) { - std::tie(success, error) = bl.flashConfigurationClear(); + std::tie(success, error) = bl.flashConfigClear(); } else { if(path.empty()) { - std::tie(success, error) = bl.flashConfiguration(dai::DeviceBootloader::Config{}); + std::tie(success, error) = bl.flashConfig(dai::DeviceBootloader::Config{}); } else { - std::tie(success, error) = bl.flashConfigurationFile(path); + std::tie(success, error) = bl.flashConfigFile(path); } } if(success) { diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index c0a65b7cb8..ae290dd567 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -8,6 +8,7 @@ // project #include "CallbackHandler.hpp" #include "DataQueue.hpp" +#include "depthai/common/UsbSpeed.hpp" #include "depthai/pipeline/Pipeline.hpp" #include "depthai/xlink/XLinkConnection.hpp" #include "depthai/xlink/XLinkStream.hpp" @@ -33,7 +34,48 @@ class DeviceBootloader { using Section = dai::bootloader::Section; using UsbConfig = dai::bootloader::UsbConfig; using NetworkConfig = dai::bootloader::NetworkConfig; - using Config = dai::bootloader::Config; + + // Derive and extend bootloader::Config for easier usage + struct Config : public bootloader::Config { + /// Setting a static IPv4 won't start DHCP client + void setStaticIPv4(std::string ip, std::string mask, std::string gateway); + /// Setting a dynamic IPv4 will set that IP as well as start DHCP client + void setDynamicIPv4(std::string ip, std::string mask, std::string gateway); + /// Get if static IPv4 configuration is set + bool isStaticIPV4(); + /// Get IPv4 + std::string getIPv4(); + /// Get IPv4 mask + std::string getIPv4Mask(); + /// Get IPv4 gateway + std::string getIPv4Gateway(); + /// Set IPv4 DNS options + void setDnsIPv4(std::string dns, std::string dnsAlt = ""); + /// Get primary IPv4 DNS server + std::string getDnsIPv4(); + /// Get alternate IPv4 DNS server + std::string getDnsAltIPv4(); + + /// Set USB timeout + void setUsbTimeout(std::chrono::milliseconds ms); + /// Get USB timeout + std::chrono::milliseconds getUsbTimeout(); + + /// Set NETWOR timeout + void setNetworkTimeout(std::chrono::milliseconds ms); + /// Get NETWORK timeout + std::chrono::milliseconds getNetworkTimeout(); + + /// Set MAC address if not flashed on controller + void setMacAddress(std::string mac); + /// Get MAC address if not flashed on controller + std::string getMacAddress(); + + /// Set maxUsbSpeed + void setUsbMaxSpeed(UsbSpeed speed); + /// Get maxUsbSpeed + UsbSpeed getUsbMaxSpeed(); + }; /// Bootloader version structure struct Version { @@ -84,17 +126,35 @@ class DeviceBootloader { * Creates application package which can be flashed to depthai device. * @param pipeline Pipeline from which to create the application package * @param pathToCmd Optional path to custom device firmware + * @param compress Optional boolean which specifies if contents should be compressed + * @returns Depthai application package + */ + static std::vector createDepthaiApplicationPackage(const Pipeline& pipeline, std::string pathToCmd = "", bool compress = false); + + /** + * Creates application package which can be flashed to depthai device. + * @param pipeline Pipeline from which to create the application package + * @param compress Specifies if contents should be compressed * @returns Depthai application package */ - static std::vector createDepthaiApplicationPackage(Pipeline& pipeline, std::string pathToCmd = ""); + static std::vector createDepthaiApplicationPackage(const Pipeline& pipeline, bool compress); /** * Saves application package to a file which can be flashed to depthai device. * @param path Path where to save the application package * @param pipeline Pipeline from which to create the application package * @param pathToCmd Optional path to custom device firmware + * @param compress Optional boolean which specifies if contents should be compressed */ - static void saveDepthaiApplicationPackage(std::string path, Pipeline& pipeline, std::string pathToCmd = ""); + static void saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, std::string pathToCmd = "", bool compress = false); + + /** + * Saves application package to a file which can be flashed to depthai device. + * @param path Path where to save the application package + * @param pipeline Pipeline from which to create the application package + * @param compress Specifies if contents should be compressed + */ + static void saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, bool compress); /** * @returns Embedded bootloader version @@ -139,11 +199,17 @@ class DeviceBootloader { ~DeviceBootloader(); /** - * Flashes a give pipeline to the board. + * Flashes a given pipeline to the device. * @param progressCallback Callback that sends back a value between 0..1 which signifies current flashing progress * @param pipeline Pipeline to flash to the board */ - std::tuple flash(std::function progressCallback, Pipeline& pipeline); + std::tuple flash(std::function progressCallback, const Pipeline& pipeline, bool compress = false); + + /** + * Flashes a given pipeline to the device. + * @param pipeline Pipeline to flash to the board + */ + std::tuple flash(const Pipeline& pipeline, bool compress = false); /** * Flashes a specific depthai application package that was generated using createDepthaiApplicationPackage or saveDepthaiApplicationPackage @@ -152,6 +218,12 @@ class DeviceBootloader { */ std::tuple flashDepthaiApplicationPackage(std::function progressCallback, std::vector package); + /** + * Flashes a specific depthai application package that was generated using createDepthaiApplicationPackage or saveDepthaiApplicationPackage + * @param package Depthai application package to flash to the board + */ + std::tuple flashDepthaiApplicationPackage(std::vector package); + /** * Flashes bootloader to the current board * @param progressCallback Callback that sends back a value between 0..1 which signifies current flashing progress @@ -180,44 +252,49 @@ class DeviceBootloader { /** * Reads configuration data from bootloader * @returns Unstructured configuration data + * @param memory Optional - from which memory to read configuration data + * @param type Optional - from which type of bootloader to read configuration data */ - nlohmann::json readConfigurationData(Memory memory = Memory::AUTO); + nlohmann::json readConfigData(Memory memory = Memory::AUTO, Type type = Type::AUTO); /** * Flashes configuration data to bootloader * @param configData Unstructured configuration data + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration */ - std::tuple flashConfigurationData(nlohmann::json configData, Memory memory = Memory::AUTO); - - /** - * Flashes JSON configuration data to bootloader - * @param configJson JSON configuration data - */ - std::tuple flashConfigurationData(std::string configJson, Memory memory = Memory::AUTO); + std::tuple flashConfigData(nlohmann::json configData, Memory memory = Memory::AUTO, Type type = Type::AUTO); /** * Flashes configuration data to bootloader * @param configPath Unstructured configuration data + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration */ - std::tuple flashConfigurationFile(std::string configPath, Memory memory = Memory::AUTO); + std::tuple flashConfigFile(std::string configPath, Memory memory = Memory::AUTO, Type type = Type::AUTO); /** - * Flashes configuration data to bootloader - * @param configData Unstructured configuration data + * Clears configuration data + * @param memory Optional - on which memory to clear configuration data + * @param type Optional - for which type of bootloader to clear configuration data */ - std::tuple flashConfigurationClear(Memory memory = Memory::AUTO); + std::tuple flashConfigClear(Memory memory = Memory::AUTO, Type type = Type::AUTO); /** * Reads configuration from bootloader - * @returns Configuration + * @param memory Optional - from which memory to read configuration + * @param type Optional - from which type of bootloader to read configuration + * @returns Configuration structure */ - Config readConfiguration(Memory memory = Memory::AUTO); + Config readConfig(Memory memory = Memory::AUTO, Type type = Type::AUTO); /** * Flashes configuration to bootloader - * @param configData Configuration + * @param configData Configuration structure + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration */ - std::tuple flashConfiguration(const Config& config); + std::tuple flashConfig(const Config& config, Memory memory = Memory::AUTO, Type type = Type::AUTO); /** * @returns Version of current running bootloader @@ -284,6 +361,9 @@ class DeviceBootloader { // Global namespace inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Type& type) { switch(type) { + case dai::DeviceBootloader::Type::AUTO: + out << "AUTO"; + break; case dai::DeviceBootloader::Type::USB: out << "USB"; break; diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 678ff68015..7b2c4ab141 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 678ff68015661137137c454383cc2ff7bf82f016 +Subproject commit 7b2c4ab141df199b3d08e06bda20a9326576c345 diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index ea75b69d95..24d9a8abce 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -16,10 +16,13 @@ #include "device/Device.hpp" #include "pipeline/Pipeline.hpp" #include "utility/BootloaderHelper.hpp" +#include "utility/Platform.hpp" #include "utility/Resources.hpp" // libraries +#include "spdlog/fmt/chrono.h" #include "spdlog/spdlog.h" +#include "zlib.h" // Resource compiled assets (cmds) #ifdef DEPTHAI_RESOURCE_COMPILED_BINARIES @@ -55,7 +58,7 @@ std::vector DeviceBootloader::getAllAvailableDevices() { return availableDevices; } -std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& pipeline, std::string pathToCmd) { +std::vector DeviceBootloader::createDepthaiApplicationPackage(const Pipeline& pipeline, std::string pathToCmd, bool compress) { // Serialize the pipeline PipelineSchema schema; Assets assets; @@ -99,12 +102,47 @@ std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& return ((((S) + (SECTION_ALIGNMENT_SIZE)-1)) & ~((SECTION_ALIGNMENT_SIZE)-1)); }; + // Should compress firmware? + if(compress) { + using namespace std::chrono; + + auto t1 = steady_clock::now(); + auto compressBufferSize = compressBound(deviceFirmware.size()); + std::vector compressBuffer(compressBufferSize); + // Chosen impirically + constexpr int COMPRESSION_LEVEL = 9; + if(compress2(compressBuffer.data(), &compressBufferSize, deviceFirmware.data(), deviceFirmware.size(), COMPRESSION_LEVEL) != Z_OK) { + throw std::runtime_error("Error while compressing device firmware\n"); + } + + // Resize output buffer + compressBuffer.resize(compressBufferSize); + + // Set the compressed firmware + auto prevSize = deviceFirmware.size(); + deviceFirmware = std::move(compressBuffer); + + auto diff = duration_cast(steady_clock::now() - t1); + spdlog::debug("Compressed firmware for Dephai Application Package. Took {}, size reduced from {:.2f}MiB to {:.2f}MiB", + diff, + prevSize / (1024.0f * 1024.0f), + deviceFirmware.size() / (1024.0f * 1024.0f)); + } + // First section, MVCMD, name '__firmware' sbr_section_set_name(fwSection, "__firmware"); sbr_section_set_bootable(fwSection, true); sbr_section_set_size(fwSection, static_cast(deviceFirmware.size())); sbr_section_set_checksum(fwSection, sbr_compute_checksum(deviceFirmware.data(), static_cast(deviceFirmware.size()))); sbr_section_set_offset(fwSection, SBR_RAW_SIZE); + // Ignore checksum to allow faster booting (images are verified after flashing, low risk) + sbr_section_set_ignore_checksum(fwSection, true); + // Set compression flags + if(compress) { + sbr_section_set_compression(fwSection, SBR_COMPRESSION_ZLIB); + } else { + sbr_section_set_compression(fwSection, SBR_NO_COMPRESSION); + } // Second section, pipeline schema, name 'pipeline' sbr_section_set_name(pipelineSection, "pipeline"); @@ -134,14 +172,30 @@ std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& sbr_serialize(&sbr, fwPackage.data(), static_cast(fwPackage.size())); // Write to fwPackage - for(unsigned i = 0; i < deviceFirmware.size(); i++) fwPackage[fwSection->offset + i] = deviceFirmware[i]; - for(unsigned i = 0; i < pipelineBinary.size(); i++) fwPackage[pipelineSection->offset + i] = pipelineBinary[i]; - for(unsigned i = 0; i < assetsBinary.size(); i++) fwPackage[assetsSection->offset + i] = assetsBinary[i]; - for(unsigned i = 0; i < assetStorage.size(); i++) fwPackage[assetStorageSection->offset + i] = assetStorage[i]; + for(std::size_t i = 0; i < deviceFirmware.size(); i++) fwPackage[fwSection->offset + i] = deviceFirmware[i]; + for(std::size_t i = 0; i < pipelineBinary.size(); i++) fwPackage[pipelineSection->offset + i] = pipelineBinary[i]; + for(std::size_t i = 0; i < assetsBinary.size(); i++) fwPackage[assetsSection->offset + i] = assetsBinary[i]; + for(std::size_t i = 0; i < assetStorage.size(); i++) fwPackage[assetStorageSection->offset + i] = assetStorage[i]; return fwPackage; } +std::vector DeviceBootloader::createDepthaiApplicationPackage(const Pipeline& pipeline, bool compress) { + return createDepthaiApplicationPackage(pipeline, "", compress); +} + +void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, std::string pathToCmd, bool compress) { + auto dap = createDepthaiApplicationPackage(pipeline, pathToCmd, compress); + std::ofstream outfile(path, std::ios::binary); + outfile.write(reinterpret_cast(dap.data()), dap.size()); +} + +void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, bool compress) { + auto dap = createDepthaiApplicationPackage(pipeline, compress); + std::ofstream outfile(path, std::ios::binary); + outfile.write(reinterpret_cast(dap.data()), dap.size()); +} + DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader) : deviceInfo(devInfo) { init(true, "", tl::nullopt, allowFlashingBootloader); } @@ -411,14 +465,12 @@ bool DeviceBootloader::isAllowedFlashingBootloader() const { return allowFlashingBootloader; } -std::tuple DeviceBootloader::flash(std::function progressCb, Pipeline& pipeline) { - return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline)); +std::tuple DeviceBootloader::flash(std::function progressCb, const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline, compress)); } -void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, Pipeline& pipeline, std::string pathToCmd) { - auto dap = createDepthaiApplicationPackage(pipeline, pathToCmd); - std::ofstream outfile(path, std::ios::binary); - outfile.write(reinterpret_cast(dap.data()), dap.size()); +std::tuple DeviceBootloader::flash(const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(createDepthaiApplicationPackage(pipeline, compress)); } std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { @@ -466,6 +518,10 @@ std::tuple DeviceBootloader::flashDepthaiApplicationPackage(s return {result.success, result.errorMsg}; } +std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::vector package) { + return flashDepthaiApplicationPackage(nullptr, package); +} + std::tuple DeviceBootloader::flashBootloader(std::function progressCb, std::string path) { return flashBootloader(Memory::FLASH, bootloaderType, progressCb, path); } @@ -600,10 +656,19 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 } */ -nlohmann::json DeviceBootloader::readConfigurationData(Memory memory) { +nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { // Send request to GET_BOOTLOADER_CONFIG dai::bootloader::request::GetBootloaderConfig getConfigReq; getConfigReq.memory = memory; + + if(type != Type::AUTO) { + const auto confStructure = bootloader::getStructure(type); + getConfigReq.offset = confStructure.offset.at(bootloader::Section::BOOTLOADER_CONFIG); + getConfigReq.maxSize = confStructure.size.at(bootloader::Section::BOOTLOADER_CONFIG); + } else { + // leaves as default values, which correspond to AUTO + } + if(!sendBootloaderRequest(stream->getStreamId(), getConfigReq)) return {false, "Couldn't send request to get configuration data"}; // Get response @@ -620,10 +685,14 @@ nlohmann::json DeviceBootloader::readConfigurationData(Memory memory) { } } -std::tuple DeviceBootloader::flashConfigurationClear(Memory memory) { +std::tuple DeviceBootloader::flashConfigClear(Memory memory, Type type) { // send request to SET_BOOTLOADER_CONFIG dai::bootloader::request::SetBootloaderConfig setConfigReq; setConfigReq.memory = memory; + if(type != Type::AUTO) { + setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); + } + setConfigReq.numPackets = 0; setConfigReq.totalSize = 0; setConfigReq.clearConfig = 1; @@ -637,13 +706,16 @@ std::tuple DeviceBootloader::flashConfigurationClear(Memory m return {result.success, result.errorMsg}; } -std::tuple DeviceBootloader::flashConfigurationData(nlohmann::json configData, Memory memory) { +std::tuple DeviceBootloader::flashConfigData(nlohmann::json configData, Memory memory, Type type) { // Parse to BSON auto bson = nlohmann::json::to_bson(configData); // Send request to SET_BOOTLOADER_CONFIG dai::bootloader::request::SetBootloaderConfig setConfigReq; setConfigReq.memory = memory; + if(type != Type::AUTO) { + setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); + } setConfigReq.numPackets = 1; setConfigReq.totalSize = bson.size(); setConfigReq.clearConfig = 0; @@ -660,28 +732,24 @@ std::tuple DeviceBootloader::flashConfigurationData(nlohmann: return {result.success, result.errorMsg}; } -std::tuple DeviceBootloader::flashConfigurationData(std::string configJson, Memory memory) { - return flashConfigurationData(nlohmann::json::parse(configJson), memory); -} - -std::tuple DeviceBootloader::flashConfigurationFile(std::string configPath, Memory memory) { +std::tuple DeviceBootloader::flashConfigFile(std::string configPath, Memory memory, Type type) { // read a JSON file std::ifstream configInputStream(configPath); if(!configInputStream.is_open()) throw std::runtime_error("Cannot flash configuration, JSON at path: " + configPath + " doesn't exist"); nlohmann::json configJson; configInputStream >> configJson; - return flashConfigurationData(configJson, memory); + return flashConfigData(configJson, memory, type); } -DeviceBootloader::Config DeviceBootloader::readConfiguration(Memory memory) { - auto json = readConfigurationData(memory); +DeviceBootloader::Config DeviceBootloader::readConfig(Memory memory, Type type) { + auto json = readConfigData(memory, type); // Implicit parse from json to Config return json; } -std::tuple DeviceBootloader::flashConfiguration(const Config& config) { +std::tuple DeviceBootloader::flashConfig(const Config& config, Memory memory, Type type) { // Implicit parse from Config to json - return flashConfigurationData(config); + return flashConfigData(config, memory, type); } bool DeviceBootloader::isEmbeddedVersion() const { @@ -727,4 +795,96 @@ std::string DeviceBootloader::Version::toString() const { return std::to_string(versionMajor) + "." + std::to_string(versionMinor) + "." + std::to_string(versionPatch); } +// Config functions +void DeviceBootloader::Config::setStaticIPv4(std::string ip, std::string mask, std::string gateway) { + network.ipv4 = platform::getIPv4AddressAsBinary(ip); + network.ipv4Mask = platform::getIPv4AddressAsBinary(mask); + network.ipv4Gateway = platform::getIPv4AddressAsBinary(gateway); + network.staticIpv4 = true; +} +void DeviceBootloader::Config::setDynamicIPv4(std::string ip, std::string mask, std::string gateway) { + network.ipv4 = platform::getIPv4AddressAsBinary(ip); + network.ipv4Mask = platform::getIPv4AddressAsBinary(mask); + network.ipv4Gateway = platform::getIPv4AddressAsBinary(gateway); + network.staticIpv4 = false; +} + +bool DeviceBootloader::Config::isStaticIPV4() { + return network.staticIpv4; +} + +std::string DeviceBootloader::Config::getIPv4() { + return platform::getIPv4AddressAsString(network.ipv4); +} +std::string DeviceBootloader::Config::getIPv4Mask() { + return platform::getIPv4AddressAsString(network.ipv4Mask); +} +std::string DeviceBootloader::Config::getIPv4Gateway() { + return platform::getIPv4AddressAsString(network.ipv4Gateway); +} + +void DeviceBootloader::Config::setDnsIPv4(std::string dns, std::string dnsAlt) { + network.ipv4Dns = platform::getIPv4AddressAsBinary(dns); + network.ipv4DnsAlt = platform::getIPv4AddressAsBinary(dnsAlt); +} + +std::string DeviceBootloader::Config::getDnsIPv4() { + return platform::getIPv4AddressAsString(network.ipv4Dns); +} + +std::string DeviceBootloader::Config::getDnsAltIPv4() { + return platform::getIPv4AddressAsString(network.ipv4DnsAlt); +} + +void DeviceBootloader::Config::setUsbTimeout(std::chrono::milliseconds ms) { + usb.timeoutMs = ms.count(); +} + +std::chrono::milliseconds DeviceBootloader::Config::getUsbTimeout() { + return std::chrono::milliseconds(usb.timeoutMs); +} + +void DeviceBootloader::Config::setNetworkTimeout(std::chrono::milliseconds ms) { + network.timeoutMs = ms.count(); +} + +std::chrono::milliseconds DeviceBootloader::Config::getNetworkTimeout() { + return std::chrono::milliseconds(network.timeoutMs); +} + +void DeviceBootloader::Config::setUsbMaxSpeed(UsbSpeed speed) { + usb.maxUsbSpeed = static_cast(speed); +} + +UsbSpeed DeviceBootloader::Config::getUsbMaxSpeed() { + return static_cast(usb.maxUsbSpeed); +} + +void DeviceBootloader::Config::setMacAddress(std::string mac) { + std::array a; + int last = -1; + int rc = std::sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &last); + if(rc != 6 || static_cast(mac.size()) != last) { + throw std::invalid_argument("Invalid MAC address format " + mac); + } + + // Set the parsed mac address + network.mac = a; +} +std::string DeviceBootloader::Config::getMacAddress() { + // 32 characters is adequite for MAC address representation + std::array macStr = {}; + std::snprintf(macStr.data(), + macStr.size(), + "%02X:%02X:%02X:%02X:%02X:%02X", + network.mac[0], + network.mac[1], + network.mac[2], + network.mac[3], + network.mac[4], + network.mac[5]); + + return std::string(macStr.data()); +} + } // namespace dai diff --git a/src/utility/Platform.cpp b/src/utility/Platform.cpp new file mode 100644 index 0000000000..be60c10da6 --- /dev/null +++ b/src/utility/Platform.cpp @@ -0,0 +1,47 @@ +#include "Platform.hpp" + +// Platform specific +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + #include + #ifdef _MSC_VER + #pragma comment(lib, "Ws2_32.lib") + #endif +#else + #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + #include + #endif + #include +#endif + +namespace dai { +namespace platform { + +uint32_t getIPv4AddressAsBinary(std::string address) { + uint32_t binary = 0; + +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + #ifdef _WIN32_WINNT 0x0501 + binary = inet_addr(address.c_str()); // for XP + #endif + inet_pton(AF_INET, address.c_str(), &binary); // for Vista or higher +#else + inet_pton(AF_INET, address.c_str(), &binary); +#endif + + return binary; +} + +std::string getIPv4AddressAsString(std::uint32_t binary) { + char address[INET_ADDRSTRLEN] = {0}; + +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + InetNtopA(AF_INET, &binary, address, sizeof(address)); +#else + inet_ntop(AF_INET, &binary, address, sizeof(address)); +#endif + + return {address}; +} + +} // namespace platform +} // namespace dai diff --git a/src/utility/Platform.hpp b/src/utility/Platform.hpp new file mode 100644 index 0000000000..ef299164b3 --- /dev/null +++ b/src/utility/Platform.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace dai { +namespace platform { + +uint32_t getIPv4AddressAsBinary(std::string address); +std::string getIPv4AddressAsString(std::uint32_t binary); + +} +} diff --git a/src/utility/Resources.cpp b/src/utility/Resources.cpp index 8804be82af..9608cf90b7 100644 --- a/src/utility/Resources.cpp +++ b/src/utility/Resources.cpp @@ -218,6 +218,10 @@ std::vector Resources::getBootloaderFirmware(dai::bootloader::Type std::unique_lock lock(mtxBootloader); switch(type) { + case dai::bootloader::Type::AUTO: + throw std::invalid_argument("DeviceBootloader::Type::AUTO not allowed, when getting bootloader firmware."); + break; + case dai::bootloader::Type::USB: return resourceMapBootloader[DEVICE_BOOTLOADER_USB_PATH]; break; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6b258c53bb..6b3963b4f0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -99,5 +99,7 @@ target_compile_definitions(openvino_blob PRIVATE OPENVINO_2021_4_BLOB_PATH="${openvino_2021_4_blob}" ) +# Bootloader configuration tests +dai_add_test(bootloader_config_test src/bootloader_config_test.cpp) diff --git a/tests/src/bootloader_config_test.cpp b/tests/src/bootloader_config_test.cpp new file mode 100644 index 0000000000..278665eb85 --- /dev/null +++ b/tests/src/bootloader_config_test.cpp @@ -0,0 +1,39 @@ +#include + +#include "depthai/depthai.hpp" + +int main() { + dai::DeviceBootloader::Config config; + + std::string ipv4 = "192.168.1.150"; + std::string ipv4Mask = "255.255.255.0"; + std::string ipv4Gateway = "192.168.1.1"; + + config.setStaticIPv4(ipv4, ipv4Mask, ipv4Gateway); + + assert(ipv4 == config.getIPv4()); + assert(ipv4Mask == config.getIPv4Mask()); + assert(ipv4Gateway == config.getIPv4Gateway()); + + std::string dns = "1.1.1.1"; + std::string dnsAlt = "8.8.8.8"; + + config.setDnsIPv4(dns); + + assert(config.getDnsIPv4() == dns); + assert(config.network.ipv4DnsAlt == 0); + + config.setDnsIPv4(dns, dnsAlt); + + assert(config.getDnsIPv4() == dns); + assert(config.getDnsAltIPv4() == dnsAlt); + + // MAC address + std::string mac = "FF:AA:BB:CC:00:11"; + config.setMacAddress(mac); + // std::cout << "Orig mac address: " << mac << " len: " << mac.length() << std::endl; + // std::cout << "Get mac address: " << config.getMacAddress() << " len: " << config.getMacAddress().length() << std::endl; + assert(config.getMacAddress() == mac); + + return 0; +} \ No newline at end of file From 6a414d4c081903a748a3941aed9b6f1c400cb31f Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Sat, 21 Aug 2021 22:35:40 +0200 Subject: [PATCH 17/19] Updated flash_bootloader example --- examples/src/flash_bootloader.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp index e1847c9182..001145797e 100644 --- a/examples/src/flash_bootloader.cpp +++ b/examples/src/flash_bootloader.cpp @@ -6,7 +6,7 @@ int main(int argc, char** argv) { using namespace std::chrono; - dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::USB; + dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::AUTO; if(argc > 1) { std::string blTypeStr(argv[1]); if(blTypeStr == "usb") { @@ -17,9 +17,6 @@ int main(int argc, char** argv) { std::cout << "Specify either 'usb' or 'network' bootloader type\n"; return 0; } - } else { - std::cout << "Usage: " << argv[0] << " \n"; - return 0; } std::cout << "Warning! Flashing bootloader can potentially soft brick your device and should be done with caution." << std::endl; @@ -39,11 +36,12 @@ int main(int argc, char** argv) { } // Open DeviceBootloader and allow flashing bootloader + std::cout << "Booting latest bootloader first, will take a tad longer..." << std::endl; dai::DeviceBootloader bl(info, true); auto currentBlType = bl.getType(); // Check if bootloader type is the same - if(currentBlType != blType) { + if(blType != dai::DeviceBootloader::Type::AUTO && currentBlType != blType) { std::cout << "Are you sure you want to flash '" << blType << "' bootloader over current '" << currentBlType << "' bootloader?" << std::endl; std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; std::cin.ignore(); @@ -56,11 +54,11 @@ int main(int argc, char** argv) { // Create a progress callback lambda auto progress = [](float p) { std::cout << "Flashing Progress..." << p * 100 << "%" << std::endl; }; - std::cout << "Flashing " << blType << " bootloader..." << std::endl; + std::cout << "Flashing " << currentBlType << " bootloader..." << std::endl; auto t1 = steady_clock::now(); bool success = false; std::string message; - std::tie(success, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, blType, progress); + std::tie(success, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, currentBlType, progress); if(success) { std::cout << "Flashing successful. Took " << duration_cast(steady_clock::now() - t1).count() << "ms" << std::endl; } else { From fb57b897826fb7feeb99522b5c63a2350764dde5 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 23 Aug 2021 05:28:13 +0200 Subject: [PATCH 18/19] Added versioning to BL requests and refactored --- cmake/Depthai/DepthaiBootloaderConfig.cmake | 2 +- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- examples/CMakeLists.txt | 2 +- ...onfiguration.cpp => bootloader_config.cpp} | 0 examples/src/rgb_preview.cpp | 2 +- include/depthai/device/DeviceBootloader.hpp | 29 ++- include/depthai/pipeline/node/Script.hpp | 2 + shared/depthai-bootloader-shared | 2 +- shared/depthai-bootloader-shared.cmake | 5 +- src/device/Device.cpp | 1 - src/device/DeviceBase.cpp | 73 +----- src/device/DeviceBootloader.cpp | 222 ++++++++++++------ src/utility/BootloaderHelper.hpp | 68 ------ 13 files changed, 200 insertions(+), 210 deletions(-) rename examples/src/{bootloader_configuration.cpp => bootloader_config.cpp} (100%) delete mode 100644 src/utility/BootloaderHelper.hpp diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index e91bab56bb..cc3090bfc5 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" #set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") -set(DEPTHAI_BOOTLOADER_VERSION "a243221305bd0351cc2c9b98037e931068f4afef") +set(DEPTHAI_BOOTLOADER_VERSION "ad1e3d8c3a335c42d17e3b968e5d26e69885d706") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 6c439da18f..0148988342 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "9b857aedf34a19c7738a2f7f2f16a39f4e3831b3") +set(DEPTHAI_DEVICE_SIDE_COMMIT "b595df452a0366d44b5b921cba31cc87f3596ad4") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7c726f8b54..e0300553b0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -292,4 +292,4 @@ target_compile_definitions(calibration_flash_version5 PRIVATE CALIB_PATH="${cali target_compile_definitions(calibration_load PRIVATE CALIB_PATH="${calib_v6}") # Bootloader configuration example -dai_add_example(bootloader_configuration src/bootloader_configuration.cpp OFF) +dai_add_example(bootloader_config src/bootloader_config.cpp OFF) diff --git a/examples/src/bootloader_configuration.cpp b/examples/src/bootloader_config.cpp similarity index 100% rename from examples/src/bootloader_configuration.cpp rename to examples/src/bootloader_config.cpp diff --git a/examples/src/rgb_preview.cpp b/examples/src/rgb_preview.cpp index 911f7d1d95..70e41867d0 100644 --- a/examples/src/rgb_preview.cpp +++ b/examples/src/rgb_preview.cpp @@ -48,7 +48,7 @@ int main() { int key = cv::waitKey(1); if(key == 'q' || key == 'Q') { - return 0; + break; } } return 0; diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index ae290dd567..5e07fccbf3 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -296,10 +296,23 @@ class DeviceBootloader { */ std::tuple flashConfig(const Config& config, Memory memory = Memory::AUTO, Type type = Type::AUTO); + /** + * Boots a custom FW in memory + * @param fw + * @throws A runtime exception if there are any communication issues + */ + void bootMemory(const std::vector& fw); + + /** + * Boots into integrated ROM bootloader in USB mode + * @throws A runtime exception if there are any communication issues + */ + void bootUsbRomBootloader(); + /** * @returns Version of current running bootloader */ - Version getVersion(); + Version getVersion() const; /** * @returns True when bootloader was booted using latest bootloader integrated in the library. @@ -332,10 +345,19 @@ class DeviceBootloader { private: // private static - // private variables + // private methods void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type, bool allowBlFlash); void checkClosed() const; + template + bool sendRequest(const T& request); + bool receiveResponseData(std::vector& data); + template + bool parseResponse(const std::vector& data, T& response); + template + bool receiveResponse(T& response); + Version requestVersion(); + // private variables std::shared_ptr connection; DeviceInfo deviceInfo = {}; @@ -354,6 +376,9 @@ class DeviceBootloader { // Allow flashing bootloader flag bool allowFlashingBootloader = false; + + // Current connected bootloader version + Version version{0, 0, 2}; }; } // namespace dai diff --git a/include/depthai/pipeline/node/Script.hpp b/include/depthai/pipeline/node/Script.hpp index b20c376860..2640bfc170 100644 --- a/include/depthai/pipeline/node/Script.hpp +++ b/include/depthai/pipeline/node/Script.hpp @@ -48,12 +48,14 @@ class Script : public Node { /** * Sets script data to be interpreted * @param script Script string to be interpreted + * @param name Optionally set a name of this script */ void setScript(const std::string& script, const std::string& name = ""); /** * Sets script data to be interpreted * @param data Binary data that represents the script to be interpreted + * @param name Optionally set a name of this script */ void setScript(const std::vector& data, const std::string& name = ""); diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 7b2c4ab141..1d97cf4630 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 7b2c4ab141df199b3d08e06bda20a9326576c345 +Subproject commit 1d97cf4630268c1292dfe9104abc19c7fe486da5 diff --git a/shared/depthai-bootloader-shared.cmake b/shared/depthai-bootloader-shared.cmake index 2f1c56ed4c..18435fb8b3 100644 --- a/shared/depthai-bootloader-shared.cmake +++ b/shared/depthai-bootloader-shared.cmake @@ -2,6 +2,7 @@ set(DEPTHAI_BOOTLOADER_SHARED_FOLDER ${CMAKE_CURRENT_LIST_DIR}/depthai-bootloade set(DEPTHAI_BOOTLOADER_SHARED_SOURCES ${DEPTHAI_BOOTLOADER_SHARED_FOLDER}/src/SBR.c + ${DEPTHAI_BOOTLOADER_SHARED_FOLDER}/src/Bootloader.cpp ) set(DEPTHAI_BOOTLOADER_SHARED_PUBLIC_INCLUDE @@ -26,8 +27,8 @@ if(GIT_FOUND AND NOT DEPTHAI_DOWNLOADED_SOURCES) string(SUBSTRING ${statusCommit} 0 1 status) if(${status} STREQUAL "-") message(FATAL_ERROR "Submodule 'depthai-bootloader-shared' not initialized/updated. Run 'git submodule update --init --recursive' first") - endif() - + endif() + # Get depthai-bootloader-shared current commit execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD diff --git a/src/device/Device.cpp b/src/device/Device.cpp index 8810a0fdce..d07e229f70 100644 --- a/src/device/Device.cpp +++ b/src/device/Device.cpp @@ -15,7 +15,6 @@ #include "depthai/pipeline/node/XLinkIn.hpp" #include "depthai/pipeline/node/XLinkOut.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" #include "utility/Initialization.hpp" #include "utility/Resources.hpp" diff --git a/src/device/DeviceBase.cpp b/src/device/DeviceBase.cpp index 452740aa99..8ea3719805 100644 --- a/src/device/DeviceBase.cpp +++ b/src/device/DeviceBase.cpp @@ -18,7 +18,6 @@ #include "depthai/pipeline/node/XLinkIn.hpp" #include "depthai/pipeline/node/XLinkOut.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" #include "utility/Initialization.hpp" #include "utility/PimplImpl.hpp" #include "utility/Resources.hpp" @@ -408,85 +407,31 @@ void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pat } } else if(deviceInfo.state == X_LINK_BOOTLOADER) { - // Scope so bootloaderConnection is desctructed and XLink cleans its state + // Scope so DeviceBootloader is disconnected { - // TODO(themarpe) - move this logic to DeviceBootloader - - // Bootloader state, proceed by issuing a command to bootloader - XLinkConnection bootloaderConnection(deviceInfo, X_LINK_BOOTLOADER); - - // Open stream - XLinkStream stream(bootloaderConnection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - - // prepare watchdog thread, which will keep device alive - std::atomic watchdogRunning{true}; - watchdogThread = std::thread([&]() { - // prepare watchdog thread - XLinkStream stream(bootloaderConnection, bootloader::XLINK_CHANNEL_WATCHDOG, 64); - std::vector watchdogKeepalive = {0, 0, 0, 0}; - while(watchdogRunning) { - try { - stream.write(watchdogKeepalive); - } catch(const std::exception&) { - break; - } - // Ping with a period half of that of the watchdog timeout - std::this_thread::sleep_for(bootloader::XLINK_WATCHDOG_TIMEOUT / 2); - } - }); - - // Send request for bootloader version - if(!sendBootloaderRequest(stream.getStreamId(), bootloader::request::GetBootloaderVersion{})) { - throw std::runtime_error("Error trying to connect to device"); - } - // Receive response - bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(stream.getStreamId(), ver)) throw std::runtime_error("Error trying to connect to device"); - DeviceBootloader::Version version(ver.major, ver.minor, ver.patch); + DeviceBootloader bl(deviceInfo); + auto version = bl.getVersion(); // If version is >= 0.0.12 then boot directly, otherwise jump to USB ROM bootloader // Check if version is recent enough for this operation if(version >= DeviceBootloader::Version(0, 0, 12)) { - // Send request to boot firmware directly from bootloader - dai::bootloader::request::BootMemory bootMemory; - bootMemory.totalSize = static_cast(embeddedFw.size()); - bootMemory.numPackets = ((static_cast(embeddedFw.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(stream.getStreamId(), bootMemory)) { - throw std::runtime_error("Error trying to connect to device"); - } - using namespace std::chrono; + // Boot the given FW auto t1 = steady_clock::now(); - // After that send numPackets of data - stream.writeSplit(embeddedFw.data(), embeddedFw.size(), bootloader::XLINK_STREAM_MAX_SIZE); - spdlog::debug( - "Booting FW with Bootloader. Version {}, Time taken: {}", version.toString(), duration_cast(steady_clock::now() - t1)); + bl.bootMemory(embeddedFw); + auto t2 = steady_clock::now(); + spdlog::debug("Booting FW with Bootloader. Version {}, Time taken: {}", version.toString(), duration_cast(t2 - t1)); // After that the state will be BOOTED deviceInfo.state = X_LINK_BOOTED; - - // TODO(themarpe) - Possibility of switching to another port for cleaner transitions - // strcat(deviceInfo.desc.name, ":11492"); - } else { + // Boot into USB ROM BOOTLOADER + bl.bootUsbRomBootloader(); spdlog::debug("Booting FW by jumping to USB ROM Bootloader first. Bootloader Version {}", version.toString()); - // Send request to jump to USB bootloader - // Boot into USB ROM BOOTLOADER NOW - if(!sendBootloaderRequest(stream.getStreamId(), dai::bootloader::request::UsbRomBoot{})) { - throw std::runtime_error("Error trying to connect to device"); - } - // After that the state will be UNBOOTED deviceInfo.state = X_LINK_UNBOOTED; } - - watchdogRunning = false; - watchdogThread.join(); - - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream.getStreamId(), &pPacket); } // Boot and connect with XLinkConnection constructor diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 24d9a8abce..d82ec156fe 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -15,7 +15,6 @@ // project #include "device/Device.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" #include "utility/Platform.hpp" #include "utility/Resources.hpp" @@ -32,6 +31,10 @@ CMRC_DECLARE(depthai); namespace dai { +// Using +namespace Request = bootloader::request; +namespace Response = bootloader::response; + // constants constexpr const DeviceBootloader::Type DeviceBootloader::DEFAULT_TYPE; @@ -230,6 +233,9 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // prepare bootloader stream stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + // Retrieve bootloader version + version = requestVersion(); + // Device wasn't already in bootloader, that means that embedded bootloader is booted isEmbedded = true; } else if(deviceInfo.state == X_LINK_BOOTLOADER) { @@ -243,26 +249,18 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // If type is specified, try to boot into that BL type stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - // Send request for bootloader version - if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderVersion{})) { - throw std::runtime_error("Error trying to connect to device"); - } - - // Receive response - bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(stream->getStreamId(), ver)) throw std::runtime_error("Error trying to connect to device"); - DeviceBootloader::Version version(ver.major, ver.minor, ver.patch); - + // Retrieve bootloader version + version = requestVersion(); if(version >= Version(0, 0, 12)) { // If version is adequate, do an in memory boot. // Send request for bootloader type - if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderType{})) { + if(!sendRequest(Request::GetBootloaderType{})) { throw std::runtime_error("Error trying to connect to device"); } // Receive response - bootloader::response::BootloaderType runningBootloaderType; - if(!receiveBootloaderResponse(stream->getStreamId(), runningBootloaderType)) throw std::runtime_error("Error trying to connect to device"); + Response::BootloaderType runningBootloaderType; + if(!receiveResponse(runningBootloaderType)) throw std::runtime_error("Error trying to connect to device"); // Modify actual bootloader type bootloaderType = runningBootloaderType.type; @@ -289,11 +287,11 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, }); // Send request to boot firmware directly from bootloader - dai::bootloader::request::BootMemory bootMemory; + Request::BootMemory bootMemory; auto binary = getEmbeddedBootloaderBinary(desiredBootloaderType); bootMemory.totalSize = static_cast(binary.size()); bootMemory.numPackets = ((static_cast(binary.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(stream->getStreamId(), bootMemory)) { + if(!sendRequest(bootMemory)) { throw std::runtime_error("Error trying to connect to device"); } @@ -312,6 +310,9 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // prepare new bootloader stream stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + // Retrieve bootloader version + version = requestVersion(); + // The type of bootloader is now 'desiredBootloaderType' bootloaderType = desiredBootloaderType; @@ -330,7 +331,7 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, if((desiredBootloaderType != Type::USB) || allowFlashingBootloader) { // Send request to jump to USB bootloader // Boot into USB ROM BOOTLOADER NOW - if(!sendBootloaderRequest(stream->getStreamId(), dai::bootloader::request::UsbRomBoot{})) { + if(!sendRequest(Request::UsbRomBoot{})) { throw std::runtime_error("Error trying to connect to device"); } // Close existing stream first @@ -349,6 +350,10 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // prepare bootloader stream stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + // Retrieve bootloader version + version = requestVersion(); + + // The type of bootloader is now 'desiredBootloaderType' bootloaderType = desiredBootloaderType; // Embedded bootloader was used to boot, set to true @@ -439,17 +444,19 @@ DeviceBootloader::Version DeviceBootloader::getEmbeddedBootloaderVersion() { return DeviceBootloader::Version(DEPTHAI_BOOTLOADER_VERSION); } -DeviceBootloader::Version DeviceBootloader::getVersion() { - streamId_t streamId = stream->getStreamId(); +DeviceBootloader::Version DeviceBootloader::getVersion() const { + return version; +} +DeviceBootloader::Version DeviceBootloader::requestVersion() { // Send request to jump to USB bootloader - if(!sendBootloaderRequest(streamId, bootloader::request::GetBootloaderVersion{})) { + if(!sendRequest(Request::GetBootloaderVersion{})) { throw std::runtime_error("Couldn't get bootloader version"); } // Receive response - dai::bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(streamId, ver)) { + Response::BootloaderVersion ver; + if(!receiveResponse(ver)) { throw std::runtime_error("Couldn't get bootloader version"); } @@ -474,8 +481,6 @@ std::tuple DeviceBootloader::flash(const Pipeline& pipeline, } std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { - streamId_t streamId = stream->getStreamId(); - // Bug in NETWORK bootloader in version 0.0.12 < 0.1.0 - flashing can cause a soft brick auto version = getVersion(); if(bootloaderType == Type::NETWORK && version < Version(0, 0, 14)) { @@ -483,29 +488,29 @@ std::tuple DeviceBootloader::flashDepthaiApplicationPackage(s } // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlash updateFlash; - updateFlash.storage = dai::bootloader::request::UpdateFlash::SBR; + Request::UpdateFlash updateFlash; + updateFlash.storage = Request::UpdateFlash::SBR; updateFlash.totalSize = static_cast(package.size()); updateFlash.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlash)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlash)) return {false, "Couldn't send bootloader flash request"}; // After that send numPackets of data stream->writeSplit(package.data(), package.size(), bootloader::XLINK_STREAM_MAX_SIZE); // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; - if(parseBootloaderResponse(data, update)) { + Response::FlashStatusUpdate update; + if(parseResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { progressCb(update.progress); } - } else if(parseBootloaderResponse(data, result)) { + } else if(parseResponse(data, result)) { break; } else { // Unknown response, shouldn't happen @@ -536,7 +541,7 @@ std::tuple DeviceBootloader::flashBootloader(Memory memory, T if(memory != Memory::FLASH) { throw std::invalid_argument("Only FLASH memory is supported for now"); } - if(bootloaderType != type && getVersion() < Version(0, 0, 12)) { + if(bootloaderType != type && getVersion() < Version(Request::UpdateFlashEx2::VERSION)) { std::runtime_error("Current bootloader version doesn't support flashing different type of bootloader"); } @@ -549,29 +554,26 @@ std::tuple DeviceBootloader::flashBootloader(Memory memory, T package = getEmbeddedBootloaderBinary(type); } - // get streamId - streamId_t streamId = stream->getStreamId(); - // If booted and desired bootloader types don't match // Use UpdateFlashEx2 instead to properly flash if(bootloaderType == type) { // Old command // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlash updateFlash; - updateFlash.storage = dai::bootloader::request::UpdateFlash::BOOTLOADER; + Request::UpdateFlash updateFlash; + updateFlash.storage = Request::UpdateFlash::BOOTLOADER; updateFlash.totalSize = static_cast(package.size()); updateFlash.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlash)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlash)) return {false, "Couldn't send bootloader flash request"}; } else { // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlashEx2 updateFlashEx2; + Request::UpdateFlashEx2 updateFlashEx2; updateFlashEx2.memory = memory; updateFlashEx2.offset = dai::bootloader::getStructure(type).offset.at(Section::BOOTLOADER); updateFlashEx2.totalSize = static_cast(package.size()); updateFlashEx2.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; } // After that send numPackets of data @@ -579,19 +581,19 @@ std::tuple DeviceBootloader::flashBootloader(Memory memory, T // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; - if(parseBootloaderResponse(data, update)) { + Response::FlashStatusUpdate update; + if(parseResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { progressCb(update.progress); } // if flash complete response arrived, break from while loop - } else if(parseBootloaderResponse(data, result)) { + } else if(parseResponse(data, result)) { break; } else { // Unknown response, shouldn't happen @@ -618,24 +620,24 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 streamId_t streamId = stream->getStreamId(); // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlashEx2 updateFlashEx2; + Request::UpdateFlashEx2 updateFlashEx2; updateFlashEx2.memory = memory; updateFlashEx2.offset = offset; updateFlashEx2.totalSize = static_cast(data.size()); updateFlashEx2.numPackets = ((static_cast(data.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; // After that send numPackets of data stream->writeSplit(data.data(), data.size(), bootloader::XLINK_STREAM_MAX_SIZE); // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; + Response::FlashStatusUpdate update; if(parseBootloaderResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { @@ -658,7 +660,7 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { // Send request to GET_BOOTLOADER_CONFIG - dai::bootloader::request::GetBootloaderConfig getConfigReq; + Request::GetBootloaderConfig getConfigReq; getConfigReq.memory = memory; if(type != Type::AUTO) { @@ -669,11 +671,11 @@ nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { // leaves as default values, which correspond to AUTO } - if(!sendBootloaderRequest(stream->getStreamId(), getConfigReq)) return {false, "Couldn't send request to get configuration data"}; + if(!sendRequest(getConfigReq)) return {false, "Couldn't send request to get configuration data"}; // Get response - dai::bootloader::response::GetBootloaderConfig resp; - receiveBootloaderResponse(stream->getStreamId(), resp); + Response::GetBootloaderConfig resp; + receiveResponse(resp); if(resp.success) { // Read back bootloader config (1 packet max) @@ -687,7 +689,7 @@ nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { std::tuple DeviceBootloader::flashConfigClear(Memory memory, Type type) { // send request to SET_BOOTLOADER_CONFIG - dai::bootloader::request::SetBootloaderConfig setConfigReq; + Request::SetBootloaderConfig setConfigReq; setConfigReq.memory = memory; if(type != Type::AUTO) { setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); @@ -696,11 +698,11 @@ std::tuple DeviceBootloader::flashConfigClear(Memory memory, setConfigReq.numPackets = 0; setConfigReq.totalSize = 0; setConfigReq.clearConfig = 1; - if(!sendBootloaderRequest(stream->getStreamId(), setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + if(!sendRequest(setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; // Read back response - dai::bootloader::response::FlashComplete result; - receiveBootloaderResponse(stream->getStreamId(), result); + Response::FlashComplete result; + receiveResponse(result); // Return if flashing was successful return {result.success, result.errorMsg}; @@ -711,7 +713,7 @@ std::tuple DeviceBootloader::flashConfigData(nlohmann::json c auto bson = nlohmann::json::to_bson(configData); // Send request to SET_BOOTLOADER_CONFIG - dai::bootloader::request::SetBootloaderConfig setConfigReq; + Request::SetBootloaderConfig setConfigReq; setConfigReq.memory = memory; if(type != Type::AUTO) { setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); @@ -719,14 +721,14 @@ std::tuple DeviceBootloader::flashConfigData(nlohmann::json c setConfigReq.numPackets = 1; setConfigReq.totalSize = bson.size(); setConfigReq.clearConfig = 0; - if(!sendBootloaderRequest(stream->getStreamId(), setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + if(!sendRequest(setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; // Send 1 packet, of bson config data stream->write(bson); // Read back response - dai::bootloader::response::FlashComplete result; - receiveBootloaderResponse(stream->getStreamId(), result); + Response::FlashComplete result; + receiveResponse(result); // Return if flashing was successful return {result.success, result.errorMsg}; @@ -752,6 +754,41 @@ std::tuple DeviceBootloader::flashConfig(const Config& config return flashConfigData(config, memory, type); } +// Boot memory +void DeviceBootloader::bootMemory(const std::vector& embeddedFw) { + // Send request to boot firmware directly from bootloader + Request::BootMemory bootMemory; + bootMemory.totalSize = static_cast(embeddedFw.size()); + bootMemory.numPackets = ((static_cast(embeddedFw.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; + if(!sendRequest(bootMemory)) { + throw std::runtime_error("Error trying to connect to device"); + } + + // After that send numPackets of data + stream->writeSplit(embeddedFw.data(), embeddedFw.size(), bootloader::XLINK_STREAM_MAX_SIZE); + + // Then wait for the link to fall down + try { + stream->read(); + } catch (const std::exception& ex){ + // ignore + } +} + +void DeviceBootloader::bootUsbRomBootloader() { + // Boot into USB ROM BOOTLOADER now + if(!sendRequest(Request::UsbRomBoot{})) { + throw std::runtime_error("Error trying to connect to device"); + } + + // Then wait for the link to fall down + try { + stream->read(); + } catch (const std::exception& ex){ + // ignore + } +} + bool DeviceBootloader::isEmbeddedVersion() const { return isEmbedded; } @@ -765,11 +802,7 @@ DeviceBootloader::Version::Version(const std::string& v) : versionMajor(0), vers if(std::sscanf(v.c_str(), "%u.%u.%u", &versionMajor, &versionMinor, &versionPatch) != 3) throw std::runtime_error("Cannot parse version: " + v); } -DeviceBootloader::Version::Version(unsigned vmajor, unsigned vminor, unsigned vpatch) { - this->versionMajor = vmajor; - this->versionMinor = vminor; - this->versionPatch = vpatch; -} +DeviceBootloader::Version::Version(unsigned vmajor, unsigned vminor, unsigned vpatch) : versionMajor(vmajor), versionMinor(vminor), versionPatch(vpatch) {} bool DeviceBootloader::Version::operator==(const Version& other) const { if(versionMajor == other.versionMajor && versionMinor == other.versionMinor && versionPatch == other.versionPatch) return true; @@ -795,6 +828,59 @@ std::string DeviceBootloader::Version::toString() const { return std::to_string(versionMajor) + "." + std::to_string(versionMinor) + "." + std::to_string(versionPatch); } +template +bool DeviceBootloader::sendRequest(const T& request) { + if(stream == nullptr) return false; + + // Do a version check beforehand + if(getVersion() < Version(T::VERSION)) { + throw std::runtime_error( + fmt::format("Bootloader version {} required to send request '{}'. Current version {}", T::VERSION, T::NAME, getVersion().toString())); + } + + try { + stream->write((uint8_t*)&request, sizeof(T)); + } catch(const std::exception& ex) { + return false; + } + + return true; +} + +bool DeviceBootloader::receiveResponseData(std::vector& data) { + if(stream == nullptr) return false; + + data = stream->read(); + return true; +} + +template +bool DeviceBootloader::parseResponse(const std::vector& data, T& response) { + // Checks that 'data' is type T + Response::Command command; + if(data.size() < sizeof(command)) return false; + memcpy(&command, data.data(), sizeof(command)); + if(response.cmd != command) return false; + if(data.size() < sizeof(response)) return false; + + // If yes, memcpy to response + memcpy(&response, data.data(), sizeof(response)); + return true; +} + +template +bool DeviceBootloader::receiveResponse(T& response) { + if(stream == nullptr) return false; + // Receive data first + std::vector data; + if(!receiveResponseData(data)) return false; + + // Then try to parse + if(!parseResponse(data, response)) return false; + + return true; +} + // Config functions void DeviceBootloader::Config::setStaticIPv4(std::string ip, std::string mask, std::string gateway) { network.ipv4 = platform::getIPv4AddressAsBinary(ip); diff --git a/src/utility/BootloaderHelper.hpp b/src/utility/BootloaderHelper.hpp deleted file mode 100644 index 79e6678ebf..0000000000 --- a/src/utility/BootloaderHelper.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -// std -#include -#include - -// libraries -#include - -// shared -#include "depthai-bootloader-shared/Bootloader.hpp" - -namespace dai -{ - -template -bool sendBootloaderRequest(streamId_t streamId, T request){ - if(XLinkWriteData(streamId, (uint8_t*) &request, sizeof(T)) != X_LINK_SUCCESS) return false; - return true; -} - -inline bool receiveBootloaderResponseData(streamId_t streamId, std::vector& data){ - data = std::vector(); - - streamPacketDesc_t* pPacket; - if(XLinkReadData(streamId, &pPacket) != X_LINK_SUCCESS) return false; - - // Resize vector - data.resize(pPacket->length); - - // copy data - memcpy(data.data(), pPacket->data, pPacket->length); - - // release data - if(XLinkReleaseData(streamId) != X_LINK_SUCCESS) return false; - - return true; -} - -template -bool parseBootloaderResponse(const std::vector& data, T& response){ - // Checks that 'data' is type T - dai::bootloader::response::Command command; - if(data.size() < sizeof(command)) return false; - memcpy(&command, data.data(), sizeof(command)); - if(response.cmd != command) return false; - if(data.size() < sizeof(response)) return false; - - // If yes, memcpy to response - memcpy(&response, data.data(), sizeof(response)); - return true; -} - -template -bool receiveBootloaderResponse(streamId_t streamId, T& response){ - // Receive data first - std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return false; - - // Then try to parse - if(!parseBootloaderResponse(data, response)) return false; - - return true; -} - -} // namespace dai - - From 2fe25a198cc4e5872f0b372a96c69da60f697dfe Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Mon, 23 Aug 2021 23:38:10 +0200 Subject: [PATCH 19/19] Fixed Windows Platform specific code --- src/utility/Platform.cpp | 9 +++++-- tests/src/bootloader_config_test.cpp | 38 +++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/utility/Platform.cpp b/src/utility/Platform.cpp index be60c10da6..2749c60540 100644 --- a/src/utility/Platform.cpp +++ b/src/utility/Platform.cpp @@ -18,12 +18,17 @@ namespace platform { uint32_t getIPv4AddressAsBinary(std::string address) { uint32_t binary = 0; + if(address == "") { + // inet_addr returns 0xFFFFFFFF if addr is "" + return 0; + } #if defined(_WIN32) || defined(__USE_W32_SOCKETS) - #ifdef _WIN32_WINNT 0x0501 + #if (_WIN32_WINNT <= 0x0501) binary = inet_addr(address.c_str()); // for XP - #endif + #else inet_pton(AF_INET, address.c_str(), &binary); // for Vista or higher + #endif #else inet_pton(AF_INET, address.c_str(), &binary); #endif diff --git a/tests/src/bootloader_config_test.cpp b/tests/src/bootloader_config_test.cpp index 278665eb85..0dd9fea840 100644 --- a/tests/src/bootloader_config_test.cpp +++ b/tests/src/bootloader_config_test.cpp @@ -1,39 +1,53 @@ -#include +#define CATCH_CONFIG_MAIN +#include -#include "depthai/depthai.hpp" +// std +#include + +// Include depthai library +#include + +TEST_CASE("Bootloader Config") { -int main() { dai::DeviceBootloader::Config config; + // By default IPv4 is 0.0.0.0 (invalid) + REQUIRE("0.0.0.0" == config.getIPv4()); + std::string ipv4 = "192.168.1.150"; std::string ipv4Mask = "255.255.255.0"; std::string ipv4Gateway = "192.168.1.1"; config.setStaticIPv4(ipv4, ipv4Mask, ipv4Gateway); - assert(ipv4 == config.getIPv4()); - assert(ipv4Mask == config.getIPv4Mask()); - assert(ipv4Gateway == config.getIPv4Gateway()); + std::array ipv4InMemory = {192, 168, 1, 150}; + for(int i = 0; i < ipv4InMemory.size(); i++){ + REQUIRE(ipv4InMemory[i] == reinterpret_cast(&config.network.ipv4)[i]); + } + + REQUIRE(ipv4 == config.getIPv4()); + REQUIRE(ipv4Mask == config.getIPv4Mask()); + REQUIRE(ipv4Gateway == config.getIPv4Gateway()); + REQUIRE(config.isStaticIPV4() == true); std::string dns = "1.1.1.1"; std::string dnsAlt = "8.8.8.8"; config.setDnsIPv4(dns); - assert(config.getDnsIPv4() == dns); - assert(config.network.ipv4DnsAlt == 0); + REQUIRE(config.getDnsIPv4() == dns); + REQUIRE(config.network.ipv4DnsAlt == 0); config.setDnsIPv4(dns, dnsAlt); - assert(config.getDnsIPv4() == dns); - assert(config.getDnsAltIPv4() == dnsAlt); + REQUIRE(config.getDnsIPv4() == dns); + REQUIRE(config.getDnsAltIPv4() == dnsAlt); // MAC address std::string mac = "FF:AA:BB:CC:00:11"; config.setMacAddress(mac); // std::cout << "Orig mac address: " << mac << " len: " << mac.length() << std::endl; // std::cout << "Get mac address: " << config.getMacAddress() << " len: " << config.getMacAddress().length() << std::endl; - assert(config.getMacAddress() == mac); + REQUIRE(config.getMacAddress() == mac); - return 0; } \ No newline at end of file