luxonis / depthai-core

DepthAI C++ Library
MIT License
233 stars 126 forks source link

`DeviceBootloader::getVersion()` returns wrong version when `DeviceBootloader(allowFlashingBootloader=true)` #988

Open diablodale opened 6 months ago

diablodale commented 6 months ago

When allowFlashingBootloader=true --> getVersion() returns the embedded blob version (WRONG) When allowFlashingBootloader=false --> getVersion() returns the version on the device (CORRECT)

depthai-core v2.25.0 (reminder that this embeds bootloader v0.0.26) OAK-D-PRO-POE with bootloader firmware v0.0.17

auto deviceBootloader = std::make_unique<dai::DeviceBootloader>(targetDeviceInfo, false);
const auto test_good = deviceBootloader->getVersion();
assert(test_good == v0.0.17)

auto deviceBootloader = std::make_unique<dai::DeviceBootloader>(targetDeviceInfo, true);
const auto test_bad = deviceBootloader->getVersion();
assert(test_bad == v0.0.17)  //  💣 this assert fails because test_bad contains v0.0.26

I noticed when allowFlashingBootloader=true then isEmbeddedVersion() == true and when allowFlashingBootloader=false then isEmbeddedVersion() == false This seems related to the errant/unexpected getVersion() values. The goal is to get the version of the bootloader flashed on the device. It is a non-goal to dynamically run another bootloader version.

And does this interact with isUserBootloaderSupported() correctly? Because... when allowFlashingBootloader=true then isUserBootloaderSupported() == true when allowFlashingBootloader=false then isUserBootloaderSupported() == false because if(getVersion().getSemver() < Version(Request::IsUserBootloader::VERSION)) that is very suspicious...I doubt that is correct behavior since depthai docs at https://docs.luxonis.com/projects/api/en/latest/components/bootloader/#danger-zone write "OAK devices (since 2023) have factory bootloader and user bootloader". But given the behaviors above, the API will always think user bootloader is supported since it always uses the version of the embedded blob and not the version on the device.

Seems someone heard of part this issue. Found obscure comments at https://github.com/luxonis/depthai-python/blob/cf41a43b1d8b180f86ad0f7dacc6d1bd2d1e03be/utilities/device_manager.py#L605 and https://github.com/luxonis/depthai-python/blob/cf41a43b1d8b180f86ad0f7dacc6d1bd2d1e03be/utilities/device_manager.py#L834

diablodale commented 6 months ago

Don't use DeviceBootloader::getVersion(). That's what I have learned.

After collect bit-and-pieces of information and some reverse engineering, this is an incomplete function I've written to get the actual version of a bootloader saved in the device's flash.

// DeviceBootloader::getVersion() is not reliable, use this instead
// BUGBUG https://github.com/luxonis/depthai-core/issues/988 errant versions when flash=true
std::optional<dai::DeviceBootloader::Version> getBootloaderVersionStoredOnDevice(const dai::DeviceBootloader& deviceBootloader) {
    // allowFlashingBootloader=true always dynamically boots the embedded blob bootloader which overwrites the information of the actual stored bootloader
    if (deviceBootloader.isAllowedFlashingBootloader()) {
        if ([[maybe_unused]] const auto memInfo = deviceBootloader.getMemoryInfo(dai::DeviceBootloader::Memory::FLASH); memInfo.available) {
            [[maybe_unused]] const auto appInfo = deviceBootloader.readApplicationInfo(dai::DeviceBootloader::Memory::FLASH);
            DPOST(std::format("Memory FLASH, size {}, info {}, app flashed {}, firmware version {}, app name {}\n", memInfo.size, memInfo.info, appInfo.hasApplication, appInfo.firmwareVersion, appInfo.applicationName));
        }
        else {
            DPOST("Memory FLASH not available\n");
        }
        assert(deviceBootloader.isEmbeddedVersion());
        throw std::runtime_error("internal error: impossible to get device stored bootloader version while also allowing flashing");
    }

    // when allowFlashingBootloader=false and isEmbeddedVersion()=true, the device has no stored bootloader (e.g. OAK-D) so it was dynamically booted from the embedded blob
    if (deviceBootloader.isEmbeddedVersion())
        return std::nullopt;

    // when allowFlashingBootloader=false and isEmbeddedVersion()=false, the device has a stored bootloader and it was booted from it
    // it could be the factory bootloader or the user bootloader: isUserBootloader()=true
    return deviceBootloader.getVersion();
}

Unfortunately, I can find no technique to get the bootloader version on the device when DeviceBootloader was constructed with allowFlashingBootloader=true. I even tried getMemoryInfo() and readApplicationInfo(). Nothing worked.

Is there any API or RPC that can get the bootloader version on a device always? Or can do it when allowFlashingBootloader=true?

moratom commented 6 months ago

Thanks for the issue report @diablodale, we have added this to our internal task list and will update the issue when we have the solution.