24 #include "XLink/XLink.h"
25 #include "spdlog/fmt/chrono.h"
26 #include "spdlog/spdlog.h"
31 #ifdef DEPTHAI_RESOURCE_COMPILED_BINARIES
32 #include "cmrc/cmrc.hpp"
33 CMRC_DECLARE(depthai);
39 namespace Request = bootloader::request;
40 namespace Response = bootloader::response;
52 for(
auto searchState : {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_FLASH_BOOTED}) {
53 for(
const auto& device : devices) {
54 if(device.state == searchState) {
55 return {
true, device};
64 std::vector<DeviceInfo> availableDevices;
66 for(
const auto& d : connectedDevices) {
67 if(d.state != X_LINK_BOOTED) availableDevices.push_back(d);
69 return availableDevices;
73 const Pipeline& pipeline,
const dai::Path& pathToCmd,
bool compress, std::string applicationName,
bool checkChecksum) {
77 std::vector<std::uint8_t> assetStorage;
78 pipeline.
serialize(schema, assets, assetStorage);
85 if(deviceFirmware.empty()) {
86 throw std::runtime_error(
"Error getting device firmware");
90 std::vector<uint8_t> pipelineBinary, assetsBinary;
95 std::string fwVersionBuffer{DEPTHAI_DEVICE_VERSION};
110 lastSection = lastSection - 1;
113 auto getSectionAlignedOffset = [](
long S) {
114 constexpr
long SECTION_ALIGNMENT_SIZE = 1 * 1024 * 1024;
115 return ((((S) + (SECTION_ALIGNMENT_SIZE)-1)) & ~((SECTION_ALIGNMENT_SIZE)-1));
118 auto getSectionAlignedOffsetSmall = [](
long S) {
119 constexpr
long SECTION_ALIGNMENT_SIZE = 64 * 1024;
120 return ((((S) + (SECTION_ALIGNMENT_SIZE)-1)) & ~((SECTION_ALIGNMENT_SIZE)-1));
125 using namespace std::chrono;
127 auto t1 = steady_clock::now();
128 auto compressBufferSize = compressBound(
static_cast<decltype(compressBound(1))
>(deviceFirmware.size()));
129 std::vector<uint8_t> compressBuffer(compressBufferSize);
131 constexpr
int COMPRESSION_LEVEL = 9;
132 if(compress2(compressBuffer.data(),
134 deviceFirmware.data(),
135 static_cast<decltype(compressBufferSize)
>(deviceFirmware.size()),
138 throw std::runtime_error(
"Error while compressing device firmware\n");
142 compressBuffer.resize(compressBufferSize);
145 auto prevSize = deviceFirmware.size();
146 deviceFirmware = std::move(compressBuffer);
148 auto diff = duration_cast<milliseconds>(steady_clock::now() - t1);
149 logger::debug(
"Compressed firmware for Dephai Application Package. Took {}, size reduced from {:.2f}MiB to {:.2f}MiB",
151 prevSize / (1024.0f * 1024.0f),
152 deviceFirmware.size() / (1024.0f * 1024.0f));
208 std::vector<uint8_t> fwPackage;
209 fwPackage.resize(lastSection->
offset + lastSection->
size);
212 sbr_serialize(&sbr, fwPackage.data(),
static_cast<uint32_t
>(fwPackage.size()));
215 for(std::size_t i = 0; i < deviceFirmware.size(); i++) fwPackage[fwSection->
offset + i] = deviceFirmware[i];
216 for(std::size_t i = 0; i < fwVersionBuffer.size(); i++) fwPackage[fwVersionSection->
offset + i] = fwVersionBuffer[i];
217 for(std::size_t i = 0; i < applicationName.size(); i++) fwPackage[appNameSection->
offset + i] = applicationName[i];
218 for(std::size_t i = 0; i < pipelineBinary.size(); i++) fwPackage[pipelineSection->
offset + i] = pipelineBinary[i];
219 for(std::size_t i = 0; i < assetsBinary.size(); i++) fwPackage[assetsSection->
offset + i] = assetsBinary[i];
220 for(std::size_t i = 0; i < assetStorage.size(); i++) fwPackage[assetStorageSection->
offset + i] = assetStorage[i];
226 for(; cur != lastSection + 1; cur++) {
227 logger::debug(
"{}, {}B, {}, {}, {}, {}", cur->
name, cur->
size, cur->
offset, cur->
checksum, cur->
type, cur->
flags);
236 std::string applicationName,
237 bool checkChecksum) {
242 const dai::Path& path,
const Pipeline& pipeline,
const dai::Path& pathToCmd,
bool compress, std::string applicationName,
bool checkChecksum) {
244 std::ofstream outfile(path, std::ios::binary);
245 outfile.write(
reinterpret_cast<const char*
>(dap.data()), dap.size());
249 const dai::Path& path,
const Pipeline& pipeline,
bool compress, std::string applicationName,
bool checkChecksum) {
251 std::ofstream outfile(path, std::ios::binary);
252 outfile.write(
reinterpret_cast<const char*
>(dap.data()), dap.size());
278 throw std::runtime_error(
"Watchdog already created. Destroy it first.");
299 std::chrono::milliseconds watchdogTimeout = std::chrono::milliseconds(3000);
300 if(
watchdogRunning && std::chrono::steady_clock::now() - prevPingTime > watchdogTimeout * 2) {
316 std::vector<uint8_t> watchdogKeepalive = {0, 0, 0, 0};
317 std::vector<uint8_t>
reset = {1, 0, 0, 0};
320 stream.write(watchdogKeepalive);
321 }
catch(
const std::exception&) {
336 const auto dummy =
stream.readMove();
337 }
catch(
const std::exception&) {
340 }
catch(
const std::exception&) {
345 std::this_thread::sleep_for(std::chrono::milliseconds(500));
365 deviceDesc_t foundDesc;
367 if(ret == X_LINK_SUCCESS) {
371 throw std::runtime_error(
"Specified device not found");
422 if(
version < recommendedMinVersion) {
424 "[{}] [{}] Flashed bootloader version {}, less than {} is susceptible to bootup/restart failure. Upgrading is advised, flashing "
425 "main/factory (not user) bootloader. Available: {}",
429 recommendedMinVersion.toString(),
438 throw std::runtime_error(
"Error trying to connect to device");
442 if(!
receiveResponse(runningBootloaderType))
throw std::runtime_error(
"Error trying to connect to device");
454 bootMemory.totalSize =
static_cast<uint32_t
>(binary.size());
457 throw std::runtime_error(
"Error trying to connect to device");
493 Type desiredBootloaderType =
type.value_or(Type::USB);
500 throw std::runtime_error(
"Error trying to connect to device");
539 throw std::runtime_error(
"Device not in UNBOOTED, BOOTLOADER or FLASH_BOOTED state");
560 if(
closed.exchange(
true))
return;
562 using namespace std::chrono;
563 auto t1 = steady_clock::now();
580 logger::debug(
"DeviceBootloader closed, {}", duration_cast<milliseconds>(steady_clock::now() - t1).count());
610 throw std::runtime_error(
"Couldn't get bootloader version");
616 throw std::runtime_error(
"Couldn't parse version response");
621 if(blVersion >=
Version(Request::GetBootloaderCommit::VERSION)) {
624 stream->write((uint8_t*)&request,
sizeof(request));
629 throw std::runtime_error(
"Couldn't get bootloader commit");
647 std::function<
void(
float)> progressCb,
const Pipeline& pipeline,
bool compress, std::string applicationName,
Memory memory,
bool checkCheksum) {
669 info.firmwareVersion =
"";
670 info.applicationName =
"";
673 info.hasApplication = details.hasApplication;
674 if(details.hasFirmwareVersion) {
675 info.firmwareVersion = std::string(details.firmwareVersionStr);
677 if(details.hasApplicationName) {
678 info.applicationName = std::string(details.applicationNameStr);
681 if(!details.success) {
682 throw std::runtime_error(details.errorMsg);
691 logger::warn(
"USB Bootloader type does NOT support eMMC");
713 if(
getType() != Type::NETWORK) {
747 std::vector<uint8_t> package,
751 return {
false,
"Can't flash DepthAI application package without knowing flashed bootloader version."};
755 throw std::invalid_argument(
"Network bootloader requires version 0.0.14 or higher to flash applications. Current version: "
756 + bootloaderVersion.toString());
759 std::tuple<bool, std::string> ret;
763 updateFlash.
storage = Request::UpdateFlash::SBR;
764 updateFlash.
totalSize =
static_cast<uint32_t
>(package.size());
766 if(!
sendRequest(updateFlash))
return {
false,
"Couldn't send bootloader flash request"};
777 std::vector<uint8_t>
data;
783 if(progressCb !=
nullptr) {
790 return {
false,
"Unknown response from bootloader while flashing"};
805 Memory finalAppMem = Memory::FLASH;
808 finalAppMem = memory;
813 finalAppMem = Memory::EMMC;
818 nlohmann::json configJson;
821 }
catch(
const std::exception& ex) {
822 logger::debug(
"Error while trying to read existing bootloader configuration: {}", ex.what());
825 configJson[
"appMem"] = finalAppMem;
828 std::string errorMsg;
831 logger::debug(
"Success flashing the appMem configuration to '{}'",
static_cast<std::int32_t
>(finalAppMem));
833 throw std::runtime_error(errorMsg);
835 }
catch(
const std::exception& ex) {
836 logger::debug(
"Error while trying to specify final appMem configuration: {}", ex.what());
847 std::vector<uint8_t> clear;
849 clear.push_back(0xFF);
861 throw std::invalid_argument(
"DeviceBootloader wasn't initialized to allow flashing bootloader. Set 'allowFlashingBootloader' in constructor");
870 if(memory != Memory::FLASH) {
871 throw std::invalid_argument(
"Only FLASH memory is supported for now");
874 throw std::runtime_error(
"Current bootloader version doesn't support flashing different type of bootloader");
877 std::vector<uint8_t> package;
879 std::ifstream fwStream(path, std::ios::binary);
880 if(!fwStream.is_open())
throw std::runtime_error(fmt::format(
"Cannot flash bootloader, binary at path: {} doesn't exist", path));
881 package = std::vector<std::uint8_t>(std::istreambuf_iterator<char>(fwStream), {});
883 package = getEmbeddedBootloaderBinary(type);
893 updateFlash.
storage = Request::UpdateFlash::BOOTLOADER;
894 updateFlash.
totalSize =
static_cast<uint32_t
>(package.size());
896 if(!
sendRequest(updateFlash))
return {
false,
"Couldn't send bootloader flash request"};
901 updateFlashEx2.
memory = memory;
903 updateFlashEx2.
totalSize =
static_cast<uint32_t
>(package.size());
905 if(!
sendRequest(updateFlashEx2))
return {
false,
"Couldn't send bootloader flash request"};
917 std::vector<uint8_t>
data;
923 if(progressCb !=
nullptr) {
931 return {
false,
"Unknown response from bootloader while flashing"};
942 const auto type = Type::NETWORK;
943 if(
getType() != Type::NETWORK) {
944 throw std::runtime_error(
"Flashing User Bootloader is only available for NETWORK bootloaders");
947 const auto memory = Memory::FLASH;
954 throw std::runtime_error(
955 "Couldn't retrieve version of the flashed bootloader. Make sure you have a factory bootloader flashed and the device is booted to bootloader.");
958 throw std::runtime_error(fmt::format(
"Current bootloader version doesn't support User Bootloader. Current version: {}, minimum required version: {}",
960 Version(Request::IsUserBootloader::VERSION).toStringSemver()));
964 std::vector<uint8_t> package;
966 std::ifstream fwStream(path, std::ios::binary);
967 if(!fwStream.is_open())
throw std::runtime_error(fmt::format(
"Cannot flash User Bootloader, binary at path: {} doesn't exist", path));
968 package = std::vector<std::uint8_t>(std::istreambuf_iterator<char>(fwStream), {});
970 package = getEmbeddedBootloaderBinary(type);
975 updateFlashEx2.
memory = memory;
977 updateFlashEx2.
totalSize =
static_cast<uint32_t
>(package.size());
982 if(
static_cast<long>(updateFlashEx2.
totalSize) > MAX_USER_BOOTLOADER_SIZE) {
983 throw std::runtime_error(fmt::format(
"Selected User Bootloader is too large {} / {}B", updateFlashEx2.
totalSize, MAX_USER_BOOTLOADER_SIZE));
986 if(!
sendRequest(updateFlashEx2))
return {
false,
"Couldn't send bootloader flash request"};
997 std::vector<uint8_t>
data;
1003 if(progressCb !=
nullptr) {
1011 return {
false,
"Unknown response from bootloader while flashing"};
1018 nlohmann::json configJson;
1021 }
catch(
const std::exception& ex) {
1022 logger::debug(
"Error while trying to read existing bootloader configuration: {}", ex.what());
1025 const auto userBlSize =
static_cast<std::uint32_t
>(package.size());
1026 const auto userBlChecksum =
sbr_compute_checksum(package.data(),
static_cast<uint32_t
>(package.size()));
1027 configJson[
"userBlSize"] = userBlSize;
1028 configJson[
"userBlChecksum"] = userBlChecksum;
1031 std::string errorMsg;
1034 logger::debug(
"Success flashing the configuration userBlSize to '{}' and userBlChecksum to '{}'", userBlSize, userBlChecksum);
1036 throw std::runtime_error(errorMsg);
1048 updateBootHeader.
type = Request::UpdateFlashBootHeader::GPIO_MODE;
1049 updateBootHeader.
gpioMode = gpioMode;
1052 if(!
sendRequest(updateBootHeader))
return {
false,
"Couldn't send request to flash boot header"};
1063 updateBootHeader.
type = Request::UpdateFlashBootHeader::USB_RECOVERY;
1066 if(!
sendRequest(updateBootHeader))
return {
false,
"Couldn't send request to flash boot header"};
1077 updateBootHeader.
type = Request::UpdateFlashBootHeader::NORMAL;
1078 updateBootHeader.
offset = offset;
1079 updateBootHeader.
location = location;
1086 if(!
sendRequest(updateBootHeader))
return {
false,
"Couldn't send request to flash boot header"};
1097 updateBootHeader.
type = Request::UpdateFlashBootHeader::FAST;
1098 updateBootHeader.
offset = offset;
1099 updateBootHeader.
location = location;
1106 if(!
sendRequest(updateBootHeader))
return {
false,
"Couldn't send request to flash boot header"};
1114 const std::vector<uint8_t>&
data,
1115 std::function<
void(
float)> progressCb) {
1116 if(
data.size() == 0) {
1117 throw std::invalid_argument(
"Size to flash is zero");
1122 Memory memory,
size_t offset,
const uint8_t*
data,
size_t size, std::function<
void(
float)> progressCb) {
1124 throw std::invalid_argument(
"Data is nullptr or size is zero");
1129 return flashCustom(memory, offset,
nullptr, 0, filename, progressCb);
1132 Memory memory,
size_t offset,
const uint8_t*
data,
size_t size, std::string filename, std::function<
void(
float)> progressCb) {
1138 throw std::runtime_error(
"Current bootloader version doesn't support custom flashing");
1141 std::vector<uint8_t> optFileData;
1142 if(!filename.empty()) {
1144 std::ifstream optFile(filename, std::ios::in | std::ios::binary);
1145 optFileData = std::vector<std::uint8_t>(std::istreambuf_iterator<char>(optFile), {});
1146 data = optFileData.data();
1147 size = optFileData.size();
1152 updateFlashEx2.
memory = memory;
1153 updateFlashEx2.
offset =
static_cast<uint32_t
>(offset);
1156 if(!
sendRequest(updateFlashEx2))
return {
false,
"Couldn't send bootloader flash request"};
1167 std::vector<uint8_t> responseData;
1168 if(!
receiveResponseData(responseData))
return {
false,
"Couldn't receive bootloader response"};
1173 if(progressCb !=
nullptr) {
1181 return {
false,
"Unknown response from bootloader while flashing"};
1191 Memory memory,
size_t offset,
size_t size, std::vector<uint8_t>&
data, std::function<
void(
float)> progressCb) {
1202 Memory memory,
size_t offset,
size_t size, std::string filename, std::function<
void(
float)> progressCb) {
1203 return readCustom(memory, offset,
size,
nullptr, filename, progressCb);
1208 std::function<
void(
float)> progressCb) {
1209 std::vector<uint8_t>
data;
1211 return {std::get<0>(ret), std::get<1>(ret),
data};
1215 Memory memory,
size_t offset,
size_t size, uint8_t*
data, std::string filename, std::function<
void(
float)> progressCb) {
1223 readFlash.
memory = memory;
1224 readFlash.
offset =
static_cast<uint32_t
>(offset);
1226 if(!
sendRequest(readFlash))
return {
false,
"Couldn't send bootloader flash request"};
1232 if(!response.success) {
1233 return {
false, std::string(response.errorMsg)};
1237 if(filename.empty()) {
1239 size_t dataOffset = 0;
1240 for(
unsigned i = 0; i < response.numPackets; i++) {
1242 memcpy(
data + dataOffset, d.data(), d.size());
1243 dataOffset += d.size();
1244 if(progressCb) progressCb((1.0f / response.numPackets) * (i + 1));
1248 std::ofstream outputFile(filename);
1249 for(
unsigned i = 0; i < response.numPackets; i++) {
1251 outputFile.write(
reinterpret_cast<char*
>(d.data()), d.size());
1252 if(progressCb) progressCb((1.0f / response.numPackets) * (i + 1));
1257 return {response.success, response.errorMsg};
1263 getConfigReq.
memory = memory;
1282 auto bsonConfig =
stream->read();
1284 return nlohmann::json::from_bson(bsonConfig);
1286 throw std::runtime_error(resp.
errorMsg);
1293 setConfigReq.
memory = memory;
1301 if(!
sendRequest(setConfigReq))
return {
false,
"Couldn't send request to flash configuration clear"};
1308 return {
false,
"Couldn't receive response to flash configuration clear"};
1317 auto bson = nlohmann::json::to_bson(configData);
1321 setConfigReq.
memory = memory;
1326 setConfigReq.
totalSize =
static_cast<decltype(setConfigReq.totalSize)
>(bson.size());
1328 if(!
sendRequest(setConfigReq))
return {
false,
"Couldn't send request to flash configuration data"};
1338 return {
false,
"Couldn't receive response to flash configuration data"};
1347 std::ifstream configInputStream(configPath);
1348 if(!configInputStream.is_open())
throw std::runtime_error(fmt::format(
"Cannot flash configuration, JSON at path: {} doesn't exist", configPath));
1349 nlohmann::json configJson;
1350 configInputStream >> configJson;
1367 bootMemory.totalSize =
static_cast<uint32_t
>(embeddedFw.size());
1370 throw std::runtime_error(
"Error trying to connect to device");
1379 }
catch(
const std::exception&) {
1387 throw std::runtime_error(
"Error trying to connect to device");
1393 }
catch(
const std::exception&) {
1406 template <
typename T>
1408 if(
stream ==
nullptr)
return false;
1412 throw std::runtime_error(
1413 fmt::format(
"Bootloader version {} required to send request '{}'. Current version {}", T::VERSION, T::NAME,
getVersion().toString()));
1417 stream->write((uint8_t*)&request,
sizeof(T));
1418 }
catch(
const std::exception&) {
1425 template <
typename T>
1427 if(
stream ==
nullptr)
throw std::runtime_error(
"Couldn't send request. Stream is null");
1431 throw std::runtime_error(
1432 fmt::format(
"Bootloader version {} required to send request '{}'. Current version {}", T::VERSION, T::NAME,
getVersion().toString()));
1436 stream->write((uint8_t*)&request,
sizeof(T));
1437 }
catch(
const std::exception&) {
1438 throw std::runtime_error(
"Couldn't send " + std::string(T::NAME) +
" request");
1443 if(
stream ==
nullptr)
return false;
1449 template <
typename T>
1453 if(
data.size() <
sizeof(command))
return false;
1454 memcpy(&command,
data.data(),
sizeof(command));
1455 if(response.cmd != command)
return false;
1456 if(
data.size() <
sizeof(response))
return false;
1459 memcpy(&response,
data.data(),
sizeof(response));
1463 template <
typename T>
1465 if(
stream ==
nullptr)
return false;
1467 std::vector<uint8_t>
data;
1476 template <
typename T>
1478 if(
stream ==
nullptr)
throw std::runtime_error(
"Couldn't receive response. Stream is null");
1481 std::vector<uint8_t>
data;
1483 throw std::runtime_error(
"Couldn't receive " + std::string(T::NAME) +
" response");
1488 throw std::runtime_error(
"Couldn't parse " + std::string(T::NAME) +
" response");
1497 network.staticIpv4 =
true;
1503 network.staticIpv4 =
false;
1507 return network.staticIpv4;
1534 usb.timeoutMs =
static_cast<decltype(usb.timeoutMs)
>(ms.count());
1538 return std::chrono::milliseconds(usb.timeoutMs);
1542 network.timeoutMs =
static_cast<decltype(network.timeoutMs)
>(ms.count());
1546 return std::chrono::milliseconds(network.timeoutMs);
1550 usb.maxUsbSpeed =
static_cast<int>(speed);
1554 return static_cast<UsbSpeed>(usb.maxUsbSpeed);
1558 std::array<uint8_t, 6> a = {0, 0, 0, 0, 0, 0};
1561 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);
1562 if(rc != 6 ||
static_cast<long>(mac.size()) != last) {
1563 throw std::invalid_argument(
"Invalid MAC address format " + mac);
1572 std::array<char, 32> macStr = {};
1573 std::snprintf(macStr.data(),
1575 "%02X:%02X:%02X:%02X:%02X:%02X",
1583 return std::string(macStr.data());
1588 nlohmann::json configValues = *
this;
1589 auto dataCopy =
data;
1590 dataCopy.update(configValues);