Device: Allow skipping of device state refreshing on call to open

This commit is contained in:
Max Brombach
2026-11-03 11:20:04 -05:00
committed by Kyle Schwarz
parent dffb0bcefb
commit 645549193d
21 changed files with 124 additions and 166 deletions
@@ -1,6 +1,7 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/native_enum.h>
#include "icsneo/device/extensions/bootloader/api.h"
#include "icsneo/device/extensions/bootloader/parser/fwloader.h"
@@ -19,16 +20,21 @@ void init_bootloader(pybind11::module_& m) {
pybind11::classh<Bootloader::FWLoader>(bootloader, "FWLoader")
.def_static("load", &Bootloader::FWLoader::Load, pybind11::arg("path"), pybind11::call_guard<pybind11::gil_scoped_release>());
pybind11::native_enum<Bootloader::StatusType>(bootloader, "StatusType", "enum.IntEnum")
.value("Progress", Bootloader::StatusType::Progress)
.value("Failed", Bootloader::StatusType::Failed)
.finalize();
bootloader.def_static("update",
[](Device& device, const Bootloader::FWRepo& fw, Bootloader::Logger logger, bool alreadyInBootloader, bool forceFlash) {
if(!logger) {
logger = [](Device::OpenStatusType, const std::string&, std::optional<double>) { return Device::OpenDirective::Continue; };
[](Device& device, const Bootloader::FWRepo& fw, Bootloader::StatusHandler statusHandler, bool alreadyInBootloader, bool forceFlash) {
if(!statusHandler) {
statusHandler = [](Bootloader::StatusType, const std::string&, std::optional<double>) {};
}
return Bootloader::update(device, fw, logger, alreadyInBootloader, forceFlash);
return Bootloader::update(device, fw, statusHandler, alreadyInBootloader, forceFlash);
},
pybind11::arg("device"),
pybind11::arg("fw"),
pybind11::arg("logger") = pybind11::none(),
pybind11::arg("statusHandler") = pybind11::none(),
pybind11::arg("already_in_bootloader") = false,
pybind11::arg("force_flash") = false,
pybind11::call_guard<pybind11::gil_scoped_release>());
@@ -15,19 +15,6 @@ namespace icsneo {
void init_device(pybind11::module_& m) {
pybind11::classh<Device> device(m, "Device");
pybind11::native_enum<Device::OpenDirective>(device, "OpenDirective", "enum.IntEnum")
.value("Continue", Device::OpenDirective::Continue)
.value("Cancel", Device::OpenDirective::Cancel)
.value("Skip", Device::OpenDirective::Skip)
.finalize();
pybind11::native_enum<Device::OpenStatusType>(device, "OpenStatusType", "enum.IntEnum")
.value("QuestionContinueSkipCancel", Device::OpenStatusType::QuestionContinueSkipCancel)
.value("QuestionContinueCancel", Device::OpenStatusType::QuestionContinueCancel)
.value("Progress", Device::OpenStatusType::Progress)
.value("Failed", Device::OpenStatusType::Failed)
.finalize();
device
.def("__repr__", &Device::describe)
.def("add_message_callback", &Device::addMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>())
+24 -32
View File
@@ -275,7 +275,7 @@ std::vector<VersionReport> Device::getChipVersions(bool refreshComponents) {
return chipVersions;
}
bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
bool Device::open(OpenFlags flags) {
if(!com) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
return false;
@@ -287,38 +287,31 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
startHeartbeat();
if((flags & OpenFlags::SkipRefreshDeviceState) == 0) {
auto refreshErr = refreshDeviceState();
if(refreshErr != APIEvent::Type::NoErrorFound) {
com->close();
stopHeartbeat();
report(refreshErr, APIEvent::Severity::Error);
return false;
}
}
return true;
}
APIEvent::Type Device::refreshDeviceState() {
if(isOnline()) {
return APIEvent::Type::DeviceCurrentlyOnline;
}
if(!com || !com->isOpen()) {
return APIEvent::Type::Unknown;
}
APIEvent::Type attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) {
// We could not communicate with the device, let's see if an extension can
bool tryAgain = false;
forEachExtension([&tryAgain, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) -> bool {
if(ext->onDeviceCommunicationDead(flags, handler))
tryAgain = true;
return true;
});
if(!tryAgain) {
com->close();
report(attemptErr, APIEvent::Severity::Error);
return false; // Extensions couldn't save us
return attemptErr;
}
attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) {
com->close();
report(attemptErr, APIEvent::Severity::Error);
return false;
}
}
bool block = false;
forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) {
if(ext->onDeviceOpen(flags, handler))
return true;
block = true;
return false;
});
if(block) // Extensions say no
return false;
refreshComponentVersions();
if(!settings->disabled) {
@@ -336,7 +329,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
if(supportsLiveData())
clearAllLiveData();
return true;
return APIEvent::Type::NoErrorFound;
}
bool Device::reconnect(std::chrono::milliseconds timeout, std::chrono::milliseconds interval) {
@@ -412,7 +405,6 @@ APIEvent::Type Device::attemptToBeginCommunication() {
else
versions = std::move(*maybeVersions);
// Get component versions before the extension "onDeviceOpen" hooks so that we can properly check verisons
if(supportsComponentVersions()) {
if(auto compVersions = com->getComponentVersionsSync())
componentVersions = std::move(*compVersions);
+13 -13
View File
@@ -111,7 +111,7 @@ static bool runPipeline(
icsneo::Device& device,
const BootloaderPipeline& pipeline,
const FWRepo& fw,
const Logger& logger,
const StatusHandler& statusHandler,
bool alreadyInBootloader,
bool forceFlash
) {
@@ -167,7 +167,7 @@ static bool runPipeline(
auto com = getCommunication(device, flashPhase, info);
if(!withCommunication(com.get(), info, "FlashPhase", [&](Bootloader::Communication& opened) {
if(!opened.flash(fw, logger)) {
if(!opened.flash(fw, statusHandler)) {
Log::Error("FlashPhase", "failed to flash " + chipString(flashPhase.chip));
return false;
}
@@ -257,13 +257,13 @@ static bool runPipeline(
}
}
}
return exitBootloader(device, logger, !chipsFlashed.empty());
return exitBootloader(device, statusHandler, !chipsFlashed.empty());
}
bool Bootloader::update(
icsneo::Device& device,
const FWRepo& fw,
Logger logger,
StatusHandler statusHandler,
bool alreadyInBootloader,
bool forceFlash
) {
@@ -273,7 +273,7 @@ bool Bootloader::update(
return false;
}
if(!runPipeline(device, pipeline, fw, logger, alreadyInBootloader, forceFlash)) {
if(!runPipeline(device, pipeline, fw, statusHandler, alreadyInBootloader, forceFlash)) {
device.getEventHandler()(APIEvent::Type::FirmwareUpdateFailed, APIEvent::Severity::Error);
return false;
}
@@ -287,19 +287,19 @@ bool Bootloader::enterBootloader(icsneo::Device& device) {
return device.reconnect(std::chrono::milliseconds(30000));
}
bool Bootloader::exitBootloader(icsneo::Device& device, Logger logger, bool updatedChips) {
bool Bootloader::exitBootloader(icsneo::Device& device, StatusHandler statusHandler, bool updatedChips) {
if(!updatedChips) {
return true;
}
logger(icsneo::Device::OpenStatusType::Progress, "Connecting to the device...", std::nullopt);
return verifyConnection(device, logger);
statusHandler(StatusType::Progress, "Connecting to the device...", std::nullopt);
return verifyConnection(device, statusHandler);
}
/**
* Toggle network communication and wait up to 5 seconds for a reset status confirming the change.
* Like icsneo::Device::goOnline(), a timeout is not an error.
*/
static bool setCommunicationEnabled(icsneo::Device& device, const Logger& logger, bool enable, double progressStart) {
static bool setCommunicationEnabled(icsneo::Device& device, const StatusHandler& statusHandler, bool enable, double progressStart) {
static constexpr std::chrono::seconds Timeout(5);
const auto filter = std::make_shared<MessageFilter>(Message::Type::ResetStatus);
@@ -329,16 +329,16 @@ static bool setCommunicationEnabled(icsneo::Device& device, const Logger& logger
}
// Each call covers half of the overall progress
logger(icsneo::Device::OpenStatusType::Progress, "Connecting to the device...", progressStart + (elapsedTime / Timeout) / 2.0);
statusHandler(StatusType::Progress, "Connecting to the device...", progressStart + (elapsedTime / Timeout) / 2.0);
elapsedTime = std::chrono::steady_clock::now() - startTime;
}
logger(icsneo::Device::OpenStatusType::Progress, "Connecting to the device...", progressStart + 0.5);
statusHandler(StatusType::Progress, "Connecting to the device...", progressStart + 0.5);
return true;
}
bool Bootloader::verifyConnection(icsneo::Device& device, Logger logger) {
bool Bootloader::verifyConnection(icsneo::Device& device, StatusHandler statusHandler) {
// Firmware needs to confirm the update by verifying that it can enable/disable the connection on the device
return setCommunicationEnabled(device, logger, true, 0.0) && setCommunicationEnabled(device, logger, false, 0.5);
return setCommunicationEnabled(device, statusHandler, true, 0.0) && setCommunicationEnabled(device, statusHandler, false, 0.5);
}
bool Bootloader::isSFPChip(const icsneo::Bootloader::Chip& chip) {
@@ -38,7 +38,7 @@ bool Application::finalize() {
return ok;
}
bool Application::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool Application::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
const auto& srcVer = fw.getVersion();
@@ -55,7 +55,7 @@ bool Application::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandle
}
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(icsneo::Device::OpenStatusType::Progress, currentString, std::nullopt);
handler(StatusType::Progress, currentString, std::nullopt);
// The supplied IEF is not for this product
if(ief.getVersion().prodId != static_cast<uint8_t>(device.getProductID())) {
@@ -132,10 +132,10 @@ bool Application::validateFirmware(const DeviceAppVersion& chipSrcVer) {
return sendCommand(Update::CheckComponentVersion, [](double) {}, 0, {chipSrcVer.major, chipSrcVer.minor});
}
bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::Device::OpenStatusHandler& handler) {
bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::Bootloader::StatusHandler& handler) {
Log::Debug(__func__, "starting for " + targetChip.getName() + " (" + std::to_string(bin.size()) + " bytes)");
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(icsneo::Device::OpenStatusType::Progress, currentString, std::nullopt);
handler(StatusType::Progress, currentString, std::nullopt);
if(bin.empty() || bin.size() > std::numeric_limits<uint32_t>::max()) {
Log::Error(__func__, "invalid firmware size for " + targetChip.getName());
return false;
@@ -144,14 +144,14 @@ bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::De
Log::Debug(__func__, "erasing " + targetChip.getName());
if(!sendCommand(Update::Erase, 0, totalSize, [&](double progress) {
handler(icsneo::Device::OpenStatusType::Progress, currentString, progress);
handler(StatusType::Progress, currentString, progress);
}, std::chrono::milliseconds(45000))) {
Log::Error(__func__, "erase failed for " + targetChip.getName());
return false;
}
currentString = "Updating " + targetChip.getName() + "...";
handler(icsneo::Device::OpenStatusType::Progress, currentString, std::nullopt);
handler(StatusType::Progress, currentString, std::nullopt);
const uint32_t stepSize = std::min<uint32_t>(totalSize, 2048);
uint32_t bytesTransferred = 0;
@@ -163,7 +163,7 @@ bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::De
}
bytesTransferred += bytesToSend;
handler(icsneo::Device::OpenStatusType::Progress, currentString, double(bytesTransferred) / totalSize);
handler(StatusType::Progress, currentString, double(bytesTransferred) / totalSize);
}
Log::Debug(__func__, "write complete, validating " + targetChip.getName());
if(!validateComponent()) {
@@ -174,7 +174,7 @@ bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::De
return true;
}
bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint32_t blockSize, const icsneo::Device::OpenStatusHandler& handler) {
bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint32_t blockSize, const icsneo::Bootloader::StatusHandler& handler) {
if(sectorPairs.empty()) {
Log::Error(__func__, "no sectors to flash for " + targetChip.getName());
return false;
@@ -182,7 +182,7 @@ bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint
Log::Debug(__func__, "starting for " + targetChip.getName() + " (" + std::to_string(sectorPairs.size()) + " sectors)");
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(icsneo::Device::OpenStatusType::Progress, currentString, std::nullopt);
handler(StatusType::Progress, currentString, std::nullopt);
uint64_t totalSize = 0;
for(const auto& sector : sectorPairs) {
totalSize += sector.second.size();
@@ -199,7 +199,7 @@ bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint
Log::Debug(__func__, "erasing " + targetChip.getName() + " (" + std::to_string(totalSize) + " bytes)");
if(!sendCommand(Update::Erase, 0, static_cast<uint32_t>(totalSize), [&](double progress) {
handler(icsneo::Device::OpenStatusType::Progress, currentString, progress);
handler(StatusType::Progress, currentString, progress);
}, std::chrono::milliseconds(45000))) {
Log::Error(__func__, "erase failed for " + targetChip.getName());
return false;
@@ -212,7 +212,7 @@ bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint
}
currentString = "Updating " + targetChip.getName() + "...";
handler(icsneo::Device::OpenStatusType::Progress, currentString, std::nullopt);
handler(StatusType::Progress, currentString, std::nullopt);
uint32_t bytesTransferred = 0;
for(const auto& pair : sectorPairs) {
@@ -233,7 +233,7 @@ bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint
}
bytesTransferred += static_cast<uint32_t>(rawData.size());
handler(icsneo::Device::OpenStatusType::Progress, currentString, double(bytesTransferred) / double(totalSize));
handler(StatusType::Progress, currentString, double(bytesTransferred) / double(totalSize));
}
Log::Debug(__func__, "write complete, validating " + targetChip.getName());
bool ok = validateComponent();
@@ -264,7 +264,7 @@ bool Application::validateComponent() {
* passes. Progress replies are OperationPending with a SoftwareUpdateCommandHeader whose
* commandSizeOrProgress holds the progress so far, or a GenericReturn with no progress.
*/
bool Application::sendCommand(Update command, uint32_t offset, uint32_t length, const StatusHandler& handler, std::chrono::milliseconds timeout, std::vector<uint8_t> args) {
bool Application::sendCommand(Update command, uint32_t offset, uint32_t length, const CommandStatusHandler& handler, std::chrono::milliseconds timeout, std::vector<uint8_t> args) {
bool finishedResponse = false;
std::condition_variable finishedResponseCV;
std::mutex finishedResponseMutex;
@@ -323,8 +323,8 @@ bool RADCommunication::sendSoftware(const std::vector<uint8_t>& chipSoftware) {
size_t remainingBytes = chipSoftware.size();
size_t currentlyReading = 0;
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
std::nullopt
);
@@ -332,8 +332,8 @@ bool RADCommunication::sendSoftware(const std::vector<uint8_t>& chipSoftware) {
while(remainingBytes > 0) {
const double progress = static_cast<double>(currentlyReading) / static_cast<double>(chipSoftware.size());
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
progress
);
@@ -407,8 +407,8 @@ bool RADCommunication::sendSoftware(const std::vector<uint8_t>& chipSoftware) {
}
}
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
1.0
);
@@ -431,8 +431,8 @@ bool RADCommunication::getProgrammingStatus(size_t sizeToValidate) {
uint32_t status = 0;
const auto beginTime = std::chrono::steady_clock::now();
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
std::nullopt
);
@@ -440,8 +440,8 @@ bool RADCommunication::getProgrammingStatus(size_t sizeToValidate) {
while(status < static_cast<uint32_t>(sizeToValidate)) {
const double progress = static_cast<double>(status) / static_cast<double>(sizeToValidate);
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
progress
);
@@ -457,8 +457,8 @@ bool RADCommunication::getProgrammingStatus(size_t sizeToValidate) {
}
}
logger(
icsneo::Device::OpenStatusType::Progress,
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
1.0
);
@@ -510,7 +510,7 @@ bool RADCommunication::loadZips(const FWRepo& fw, std::vector<uint8_t>& chipSoft
return false;
}
bool RADCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool RADCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
auto chipSrcVer = fw.getChipVersion(targetChip.getIEFName());
if(!chipSrcVer) {
@@ -524,7 +524,7 @@ bool RADCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusH
return false;
}
logger = handler;
statusHandler = handler;
if(!validateChipVersion(*chipSrcVer)) {
Log::Error(__func__, "validateChipVersion failed for " + targetChip.getName());
return false;
@@ -41,7 +41,7 @@ bool RADMultiChipCommunication::authenticate() {
return true;
}
bool RADMultiChipCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool RADMultiChipCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
auto chipSrcVer = fw.getChipVersion(targetChip.getIEFName());
if(!chipSrcVer) {
@@ -56,7 +56,7 @@ bool RADMultiChipCommunication::flash(const FWRepo& fw, const icsneo::Device::Op
if(!standardComm->validateFirmware(*chipSrcVer)) {
Log::Error(__func__, "firmware validation failed for " + targetChip.getName());
handler(icsneo::Device::OpenStatusType::Failed, "Failed to validate firmware", std::nullopt);
handler(StatusType::Failed, "Failed to validate firmware", std::nullopt);
return false;
}
@@ -103,7 +103,7 @@ bool RADMultiChipCommunication::flash(const FWRepo& fw, const icsneo::Device::Op
return false;
}
Log::Debug(__func__, "finalize successful, proceeding to reboot");
handler(icsneo::Device::OpenStatusType::Progress, "Rebooting device...", std::nullopt);
handler(StatusType::Progress, "Rebooting device...", std::nullopt);
return true;
}
@@ -180,7 +180,7 @@ bool RADPeriphCommunication::flashSector(const IEF::Sector& sector) {
return sendRequest(Request::FlashSector, std::move(args), std::chrono::milliseconds(3000));
}
bool RADPeriphCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool RADPeriphCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
// Validate the firmware before touching the device so a bad file can not leave it erased
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
@@ -228,7 +228,7 @@ bool RADPeriphCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenS
}
handler(
icsneo::Device::OpenStatusType::Progress,
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
std::nullopt
);
@@ -242,7 +242,7 @@ bool RADPeriphCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenS
sizeFlashed += static_cast<uint32_t>(sector.second.size());
handler(
icsneo::Device::OpenStatusType::Progress,
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
(double)sizeFlashed / (double)totalSize
);
@@ -113,7 +113,7 @@ bool REDCommunication::getVersion() {
return true;
}
bool REDCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool REDCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
const auto& srcVer = fw.getVersion();
std::vector<uint8_t> bin;
@@ -152,7 +152,7 @@ bool REDCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusH
}
}
handler(icsneo::Device::OpenStatusType::Progress,
handler(StatusType::Progress,
"Preparing to update " + targetChip.getName() + "...", std::nullopt);
Log::Debug(__func__, "Erasing " + targetChip.getName());
if(!sendCommand(uint8_t(Command::Erase), std::chrono::seconds(45))) {
@@ -164,7 +164,7 @@ bool REDCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusH
size_t sectorsFlashed = 0;
for(const auto& [id, sector] : sectorPairs) {
Log::Trace(__func__, "Flashing sector " + std::to_string(id));
handler(icsneo::Device::OpenStatusType::Progress,
handler(StatusType::Progress,
"Updating " + targetChip.getName() + "...",
double(sectorsFlashed++) / sectorPairs.size());
@@ -205,11 +205,11 @@ bool REDCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusH
}
// Tell the application that we flashed 100%
handler(icsneo::Device::OpenStatusType::Progress,
handler(StatusType::Progress,
"Updating " + targetChip.getName() + "...",
1.0);
handler(icsneo::Device::OpenStatusType::Progress, "Finalizing update...", std::nullopt);
handler(StatusType::Progress, "Finalizing update...", std::nullopt);
if(!sendCommand(uint8_t(Command::ValidateFirmware))) {
Log::Error(__func__, "Firmware validation failed for " + targetChip.getName());
return false;
@@ -9,7 +9,7 @@ namespace icsneo {
namespace Bootloader {
bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) {
bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
constexpr const size_t MaxSectorSize = 0x600;
std::vector<uint8_t> bin;
@@ -31,7 +31,7 @@ bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenSta
std::vector<uint8_t> sector;
for(size_t i = 0; i < fileSize; i += MaxSectorSize) {
Log::Trace(__func__, "Sending sector " + std::to_string(sectorsFlashed));
handler(icsneo::Device::OpenStatusType::Progress, message, double(sectorsFlashed++) / sectorCount);
handler(StatusType::Progress, message, double(sectorsFlashed++) / sectorCount);
sector.assign(bin.begin() + i, bin.begin() + i + std::min<size_t>(MaxSectorSize, fileSize - i));
if(!sendCommand(uint8_t(Command::SendFirmware), sector)) {
@@ -42,7 +42,7 @@ bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenSta
// Then flash it
Log::Debug(__func__, "Starting core flash for " + targetChip.getName());
handler(icsneo::Device::OpenStatusType::Progress, message, std::nullopt);
handler(StatusType::Progress, message, std::nullopt);
// There is a long timeout on the StartFirmwareFlash because the bootloader may block here,
// potentially for the entire duration of the flash
@@ -92,7 +92,7 @@ bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Device::OpenSta
Log::Trace(__func__, "Flashed " + std::to_string(flashed) + " of " + std::to_string(fileSize));
if(flashed != oldFlashed)
handler(icsneo::Device::OpenStatusType::Progress, message, std::min(double(flashed) / fileSize, 1.0));
handler(StatusType::Progress, message, std::min(double(flashed) / fileSize, 1.0));
}
return flashed == fileSize;
-1
View File
@@ -30,7 +30,6 @@ def update(firmware_path):
print()
last_status = status
print(f"\r{status}" + (f" ({progress * 100:.1f}%)" if progress is not None else ""), end="", flush=True)
return icsneopy.Device.OpenDirective.Continue
# force_flash updates every chip, even those already at the version in the firmware file
ok = icsneopy.Bootloader.update(device, fw, logger, force_flash=True)
+5 -39
View File
@@ -206,21 +206,10 @@ public:
public:
enum Enum {
/**
* Even if the firmware does not match the current firmware version,
* the device will not be updated.
*
* Note: The device may still be flashed if the device has no firmware
*
* This has no effect if the DFU extension is not present
* Skip refreshing the device state. Instead, only open com
* and start the heartbeat thread.
*/
SuppressAutoUpdate = 1 << 0,
/**
* Force reflash the device.
*
* This has no effect if the DFU extension is not present
*/
ForceReflash = 1 << 1
SkipRefreshDeviceState = 1 << 0,
};
using EnumType = std::underlying_type<Enum>::type;
@@ -231,31 +220,8 @@ public:
const Enum val;
};
enum class OpenDirective {
Continue,
Cancel,
Skip
};
enum class OpenStatusType {
QuestionContinueSkipCancel,
QuestionContinueCancel,
Progress,
Failed
};
/**
* @brief Alias of callback function for progress of a bootloader pipeline task
*
* The progress value passed into the callback function indicates the following:
* 1) If the value is std::nullopt, this implies the status message is for an asynchronous task or a task in which the current progress is unknown.
* 2) If the value is between 0 and 1 (i.e., 0 <= progress <= 1), this implies the actual progress of the current task as a percentage.
* 3) Values not between 0 and 1 (i.e., progress < 0 or progress > 1) are undefined.
*/
using OpenStatusHandler = std::function<Device::OpenDirective(OpenStatusType type, const std::string& status, std::optional<double> progress)>;
bool open(OpenFlags flags = {}, OpenStatusHandler handler =
[](OpenStatusType, const std::string&, std::optional<double>) { return Device::OpenDirective::Continue; });
bool open(OpenFlags flags = {});
APIEvent::Type refreshDeviceState();
virtual bool close();
virtual bool isOnline() const { return online; }
virtual bool isOpen() const { return com->isOpen(); }
@@ -15,20 +15,33 @@ namespace icsneo {
namespace Bootloader {
using Logger = icsneo::Device::OpenStatusHandler;
enum class StatusType {
Progress,
Failed
};
/**
* @brief Alias of callback function for progress of a bootloader pipeline task
*
* The progress value passed into the callback function indicates the following:
* 1) If the value is std::nullopt, this implies the status message is for an asynchronous task or a task in which the current progress is unknown.
* 2) If the value is between 0 and 1 (i.e., 0 <= progress <= 1), this implies the actual progress of the current task as a percentage.
* 3) Values not between 0 and 1 (i.e., progress < 0 or progress > 1) are undefined.
*/
using StatusHandler = std::function<void(StatusType type, const std::string& status, std::optional<double> progress)>;
bool requiresUpdate(icsneo::Device& device, const FWRepo& fw);
bool update(
icsneo::Device& device,
const FWRepo& fw,
Logger logger = [](icsneo::Device::OpenStatusType, const std::string&, std::optional<double>) { return icsneo::Device::OpenDirective::Continue; },
StatusHandler statusHandler = [](StatusType, const std::string&, std::optional<double>) {},
bool alreadyInBootloader = false,
bool forceFlash = false
);
bool enterBootloader(icsneo::Device& device);
bool exitBootloader(icsneo::Device& device, Logger logger, bool updatedChips);
bool verifyConnection(icsneo::Device& device, Logger logger);
bool exitBootloader(icsneo::Device& device, StatusHandler statusHandler, bool updatedChips);
bool verifyConnection(icsneo::Device& device, StatusHandler statusHandler);
bool isSFPChip(const icsneo::Bootloader::Chip& chip);
bool isSFPIEF(const icsneo::Bootloader::IEF& ief);
@@ -31,7 +31,7 @@ public:
return true;
}
bool finalize() override;
bool flash(const FWRepo& repo, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& repo, const StatusHandler& handler) override;
bool open() override {
return true;
@@ -42,8 +42,8 @@ public:
}
bool authenticate();
bool sendSoftware(const std::vector<uint8_t>& bin, const icsneo::Device::OpenStatusHandler& handler);
bool sendSoftware(const std::vector<IEF::Sector>& sectors, uint32_t blockSize, const icsneo::Device::OpenStatusHandler& handler);
bool sendSoftware(const std::vector<uint8_t>& bin, const StatusHandler& handler);
bool sendSoftware(const std::vector<IEF::Sector>& sectors, uint32_t blockSize, const StatusHandler& handler);
bool validateFirmware(const DeviceAppVersion& chipSrcVer);
bool validateComponent();
@@ -74,12 +74,12 @@ private:
const bool doAuthChecks;
const bool encrypted;
using StatusHandler = std::function<void(double progress)>;
bool sendCommand(Update command, const StatusHandler& handler, uint32_t offset = 0,
using CommandStatusHandler = std::function<void(double progress)>;
bool sendCommand(Update command, const CommandStatusHandler& handler, uint32_t offset = 0,
const std::vector<uint8_t>& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)) {
return sendCommand(command, offset, static_cast<uint32_t>(args.size()), handler, timeout, args);
}
bool sendCommand(Update command, uint32_t offset, uint32_t length, const StatusHandler& handler,
bool sendCommand(Update command, uint32_t offset, uint32_t length, const CommandStatusHandler& handler,
std::chrono::milliseconds timeout = std::chrono::milliseconds(3000), std::vector<uint8_t> args = {});
};
@@ -5,6 +5,7 @@
#include "icsneo/device/extensions/bootloader/chip.h"
#include "icsneo/device/device.h"
#include "icsneo/device/extensions/bootloader/api.h"
#include <cstdint>
@@ -29,7 +30,7 @@ public:
virtual bool enterApplication() = 0;
virtual bool hasFinalizationStep() const = 0;
virtual bool finalize() = 0;
virtual bool flash(const FWRepo& repo, const icsneo::Device::OpenStatusHandler& handler) = 0;
virtual bool flash(const FWRepo& repo, const StatusHandler& handler) = 0;
virtual bool open() = 0;
virtual bool close() = 0;
@@ -69,7 +69,7 @@ protected:
Response& sendRequest(const Request& request, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
bool sendRequestNoResponse();
icsneo::Device::OpenStatusHandler logger; // Progress updates, set in flash()
StatusHandler statusHandler; // Progress updates, set in flash()
Response lastResponse; // Last response received from sendRequest
bool supportsZynqBinaryFlasher = false; // Dictates how messages are formatted, this gets set in initialize()
@@ -92,7 +92,7 @@ public:
bool enterApplication() override;
bool finalize() override;
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
@@ -22,7 +22,7 @@ private:
public:
RADMultiChipCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash, bool auth, uint8_t slot = UINT8_MAX);
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
bool enterApplication() override;
@@ -79,7 +79,7 @@ public:
RADPeriphCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash);
~RADPeriphCommunication();
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
bool enterApplication() override;
@@ -23,7 +23,7 @@ class REDCommunication : public REDCommunicationBase {
public:
REDCommunication(icsneo::Device& dev, const ChipInfo& chip, bool auth, bool encryptWithDeviceKey = true);
bool enterApplication() override { return sendCommand(uint8_t(Command::EnterApplication)); }
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
private:
@@ -27,7 +27,7 @@ public:
using REDCommunicationBase::REDCommunicationBase;
bool enterApplication() override { return false; }
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
private:
/**
* Core flasher commands are tunneled through a RED request using CoreFlasherCommand,
@@ -16,12 +16,6 @@ public:
virtual ~DeviceExtension() = default;
virtual const char* getName() const = 0;
// Return false to block opening
virtual bool onDeviceOpen(Device::OpenFlags, const Device::OpenStatusHandler&) { return true; }
// Return true to indicate that communication should now be back
virtual bool onDeviceCommunicationDead(Device::OpenFlags, const Device::OpenStatusHandler&) { return false; }
virtual void onGoOnline() {}
virtual void onGoOffline() {}
virtual void onDeviceClose() {}