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 780c2313f3..cc3090bfc5 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 "ad1e3d8c3a335c42d17e3b968e5d26e69885d706") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 78e0ce9fc8..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 "a2baa42ba41f7b79dcbee987b34f24da075de196") +set(DEPTHAI_DEVICE_SIDE_COMMIT "b595df452a0366d44b5b921cba31cc87f3596ad4") # "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/CMakeLists.txt b/examples/CMakeLists.txt index c977e61e95..e0300553b0 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) dai_add_example(feature_tracker src/feature_tracker.cpp ON) @@ -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_config src/bootloader_config.cpp OFF) diff --git a/examples/src/bootloader_config.cpp b/examples/src/bootloader_config.cpp new file mode 100644 index 0000000000..b2eb9564a3 --- /dev/null +++ b/examples/src/bootloader_config.cpp @@ -0,0 +1,68 @@ +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + // Options + bool usage = false, 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 if(op == "clear") { + clear = true; + read = false; + } else { + usage = true; + } + } else { + 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(); + + 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.readConfigData().dump(4) << std::endl; + } else { + bool success; + std::string error; + if(clear) { + std::tie(success, error) = bl.flashConfigClear(); + } else { + if(path.empty()) { + std::tie(success, error) = bl.flashConfig(dai::DeviceBootloader::Config{}); + } else { + std::tie(success, error) = bl.flashConfigFile(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/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp new file mode 100644 index 0000000000..001145797e --- /dev/null +++ b/examples/src/flash_bootloader.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + using namespace std::chrono; + + dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::AUTO; + if(argc > 1) { + std::string blTypeStr(argv[1]); + if(blTypeStr == "usb") { + blType = dai::DeviceBootloader::Type::USB; + } else if(blTypeStr == "network") { + blType = dai::DeviceBootloader::Type::NETWORK; + } else { + std::cout << "Specify either 'usb' or 'network' bootloader type\n"; + return 0; + } + } + + 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 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; + return -1; + } + + bool found = false; + dai::DeviceInfo info; + 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 + 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(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(); + if(std::cin.get() != '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::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, currentBlType, 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; + } + return 0; +} \ No newline at end of file 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/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/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index fb5fcb40d9..5e07fccbf3 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -8,11 +8,13 @@ // 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" // 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 +32,50 @@ 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; + + // 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 { @@ -39,16 +85,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 @@ -80,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 @@ -107,36 +171,45 @@ 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(); /** - * 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 @@ -145,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 @@ -170,16 +249,87 @@ 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 + * @param memory Optional - from which memory to read configuration data + * @param type Optional - from which type of bootloader to read configuration data + */ + 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 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 flashConfigFile(std::string configPath, Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * 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 flashConfigClear(Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Reads configuration from bootloader + * @param memory Optional - from which memory to read configuration + * @param type Optional - from which type of bootloader to read configuration + * @returns Configuration structure + */ + Config readConfig(Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Flashes configuration to bootloader + * @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 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 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() const; + + /** + * @returns True if allowed to flash bootloader + */ + bool isAllowedFlashingBootloader() const; + /** * Explicitly closes connection to device. * @note This function does not need to be explicitly called @@ -195,10 +345,19 @@ class DeviceBootloader { private: // private static - // private variables - void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type); + // 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 = {}; @@ -214,6 +373,64 @@ class DeviceBootloader { // bootloader stream std::unique_ptr stream; + + // Allow flashing bootloader flag + bool allowFlashingBootloader = false; + + // Current connected bootloader version + Version version{0, 0, 2}; }; } // namespace dai + +// 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; + 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::AUTO: + out << "AUTO"; + break; + 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::AUTO: + out << "AUTO"; + break; + 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/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/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 7efb3e1887..1d97cf4630 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 7efb3e188715844d7b2b0e50861e4bfc36087370 +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 fd797a6245..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" @@ -318,11 +317,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; @@ -407,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 6e3653261a..d82ec156fe 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" @@ -14,11 +15,13 @@ // project #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 @@ -28,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; @@ -54,7 +61,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; @@ -98,12 +105,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"); @@ -133,32 +175,49 @@ 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; } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo) : deviceInfo(devInfo) { - init(true, "", tl::nullopt); +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, Type type) : deviceInfo(devInfo) { - init(true, "", type); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", tl::nullopt, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader) : deviceInfo(devInfo) { - init(false, std::string(pathToBootloader), tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, Type type, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", type, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader) : deviceInfo(devInfo) { - init(false, pathToBootloader, tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(false, std::string(pathToBootloader), tl::nullopt, allowFlashingBootloader); } -void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type) { +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, bool allowBlFlash) { stream = nullptr; + allowFlashingBootloader = allowBlFlash; bootloaderType = type.value_or(DEFAULT_TYPE); @@ -171,6 +230,12 @@ 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); + + // 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) { @@ -182,33 +247,28 @@ 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)); - - // Send request for bootloader version - if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderVersion{})) { - throw std::runtime_error("Error trying to connect to device"); - } + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - // 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); - - // If version is adequite + // 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; - // 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([&]() { @@ -227,60 +287,82 @@ 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); + 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"); } // 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); - isEmbedded = false; - } else { + // Retrieve bootloader version + version = requestVersion(); + + // 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{})) { + if(!sendRequest(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(*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; + // 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 + // The type of bootloader is now 'desiredBootloaderType' + bootloaderType = desiredBootloaderType; + + // 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; } } @@ -320,11 +402,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() { @@ -367,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"); } @@ -385,49 +464,53 @@ DeviceBootloader::Version DeviceBootloader::getVersion() { return DeviceBootloader::Version(ver.major, ver.minor, ver.patch); } -std::tuple DeviceBootloader::flash(std::function progressCb, Pipeline& pipeline) { - return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline)); +DeviceBootloader::Type DeviceBootloader::getType() const { + return bootloaderType; } -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()); +bool DeviceBootloader::isAllowedFlashingBootloader() const { + return allowFlashingBootloader; } -std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { - streamId_t streamId = stream->getStreamId(); +std::tuple DeviceBootloader::flash(std::function progressCb, const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline, compress)); +} + +std::tuple DeviceBootloader::flash(const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(createDepthaiApplicationPackage(pipeline, compress)); +} +std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { // 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()); } // 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 @@ -440,16 +523,25 @@ 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); } std::tuple 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"); } - 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"); } @@ -462,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 @@ -492,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 @@ -531,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) { @@ -569,6 +658,137 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 } */ +nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { + // Send request to GET_BOOTLOADER_CONFIG + 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(!sendRequest(getConfigReq)) return {false, "Couldn't send request to get configuration data"}; + + // Get response + Response::GetBootloaderConfig resp; + receiveResponse(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::flashConfigClear(Memory memory, Type type) { + // send request to SET_BOOTLOADER_CONFIG + 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; + if(!sendRequest(setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + + // Read back response + Response::FlashComplete result; + receiveResponse(result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +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 + 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; + 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 + Response::FlashComplete result; + receiveResponse(result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +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 flashConfigData(configJson, memory, type); +} + +DeviceBootloader::Config DeviceBootloader::readConfig(Memory memory, Type type) { + auto json = readConfigData(memory, type); + // Implicit parse from json to Config + return json; +} + +std::tuple DeviceBootloader::flashConfig(const Config& config, Memory memory, Type type) { + // Implicit parse from Config to json + 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; } @@ -582,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; @@ -612,4 +828,149 @@ 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); + 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/BootloaderHelper.hpp b/src/utility/BootloaderHelper.hpp deleted file mode 100644 index 8fd11b3f49..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 - - diff --git a/src/utility/Platform.cpp b/src/utility/Platform.cpp new file mode 100644 index 0000000000..2749c60540 --- /dev/null +++ b/src/utility/Platform.cpp @@ -0,0 +1,52 @@ +#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(address == "") { + // inet_addr returns 0xFFFFFFFF if addr is "" + return 0; + } + +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + #if (_WIN32_WINNT <= 0x0501) + binary = inet_addr(address.c_str()); // for XP + #else + inet_pton(AF_INET, address.c_str(), &binary); // for Vista or higher + #endif +#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 3f8e00f19b..9608cf90b7 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), {}); } @@ -191,11 +192,36 @@ constexpr static std::array RESOURCE_LIST_BOOTLOADER = { }; std::vector Resources::getBootloaderFirmware(dai::bootloader::Type type) { + // 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 {} doesn't exist.", blBinaryPath, blEnvVar)); + } + spdlog::warn("Overriding bootloader {}: {}", blEnvVar, 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); 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..0dd9fea840 --- /dev/null +++ b/tests/src/bootloader_config_test.cpp @@ -0,0 +1,53 @@ +#define CATCH_CONFIG_MAIN +#include + +// std +#include + +// Include depthai library +#include + +TEST_CASE("Bootloader Config") { + + 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); + + 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); + + REQUIRE(config.getDnsIPv4() == dns); + REQUIRE(config.network.ipv4DnsAlt == 0); + + config.setDnsIPv4(dns, 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; + REQUIRE(config.getMacAddress() == mac); + +} \ No newline at end of file