From ce829717475e46435ae6da1dcf0b4e866c1875c7 Mon Sep 17 00:00:00 2001 From: DonLakeFlyer Date: Mon, 6 Jul 2020 12:32:09 -0700 Subject: [PATCH] Major rework of flash to prevent SiK radio bricking in bootloader mode --- src/VehicleSetup/Bootloader.cc | 519 +++++++++++------- src/VehicleSetup/Bootloader.h | 153 +++--- src/VehicleSetup/FirmwareUpgrade.qml | 5 - src/VehicleSetup/FirmwareUpgradeController.cc | 29 +- src/VehicleSetup/FirmwareUpgradeController.h | 22 +- src/VehicleSetup/PX4FirmwareUpgradeThread.cc | 238 ++------ src/VehicleSetup/PX4FirmwareUpgradeThread.h | 119 ++-- 7 files changed, 507 insertions(+), 578 deletions(-) diff --git a/src/VehicleSetup/Bootloader.cc b/src/VehicleSetup/Bootloader.cc index 86138b60c0..b7051551d6 100644 --- a/src/VehicleSetup/Bootloader.cc +++ b/src/VehicleSetup/Bootloader.cc @@ -7,11 +7,6 @@ * ****************************************************************************/ - -/// @file -/// @brief PX4 Bootloader Utility routines -/// @author Don Gagne - #include "Bootloader.h" #include "QGCLoggingCategory.h" @@ -22,17 +17,211 @@ #include "QGC.h" -Bootloader::Bootloader(QObject *parent) : - QObject(parent) +/// This class manages interactions with the bootloader +Bootloader::Bootloader(bool sikRadio, QObject *parent) + : QObject (parent) + , _sikRadio (sikRadio) +{ + +} + +bool Bootloader::open(const QString portName) { + qCDebug(FirmwareUpgradeLog) << "open:" << portName; + _port.setPortName (portName); + _port.setBaudRate (QSerialPort::Baud115200); + _port.setDataBits (QSerialPort::Data8); + _port.setParity (QSerialPort::NoParity); + _port.setStopBits (QSerialPort::OneStop); + _port.setFlowControl(QSerialPort::NoFlowControl); + + if (!_port.open(QIODevice::ReadWrite)) { + _errorString = tr("Open failed on port %1: %2").arg(portName, _port.errorString()); + return false; + } + + if (_sikRadio) { + // Radios are slow to start up + QGC::SLEEP::msleep(1000); + } + return true; } -bool Bootloader::_write(QSerialPort* port, const uint8_t* data, qint64 maxSize) +QString Bootloader::_getNextLine(int timeoutMsecs) { - qint64 bytesWritten = port->write((const char*)data, maxSize); + QString line; + QElapsedTimer timeout; + bool foundCR = false; + + timeout.start(); + while (timeout.elapsed() < timeoutMsecs) { + char oneChar; + _port.waitForReadyRead(100); + if (_port.read(&oneChar, 1) > 0) { + if (oneChar == '\r') { + foundCR = true; + continue; + } else if (oneChar == '\n' && foundCR) { + return line; + } + line += oneChar; + } + } + + return QString(); +} + +bool Bootloader::getBoardInfo(uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize) +{ + if (_sikRadio) { + // Try sync to see if already in bootloader mode + _sync(); + if (_inBootloaderMode) { + qCDebug(FirmwareUpgradeLog) << "Radio in bootloader mode already"; + if (!_get3DRRadioBoardId(_boardID)) { + goto Error; + } + } else { + qCDebug(FirmwareUpgradeLog) << "Radio in normal mode"; + _port.readAll(); + _port.setBaudRate(QSerialPort::Baud57600); + // Put radio into command mode + _write("+++"); + if (!_port.waitForReadyRead(2000)) { + _errorString = tr("Unable to put radio into command mode +++"); + goto Error; + } + QByteArray bytes = _port.readAll(); + if (!bytes.contains("OK")) { + _errorString = tr("Radio did not respond to command mode"); + goto Error; + } + + // Use ATI2 command to get board id + _write("ATI2\r\n"); + QString echo = _getNextLine(2000); + if (echo.isEmpty() || echo != "ATI2") { + _errorString = tr("Radio did not respond to ATI2 command"); + goto Error; + } + QString boardIdStr = _getNextLine(2000); + bool ok = false; + _boardID = boardIdStr.toInt(&ok); + _boardID = 130; + if (boardIdStr.isEmpty() || !ok) { + _errorString = tr("Radio did not return board id"); + goto Error; + } + } + bootloaderVersion = 0; + boardID = _boardID; + flashSize = 0; + + return true; + } else { + if (!_sync()) { + goto Error; + } + if (!_protoGetDevice(INFO_BL_REV, _bootloaderVersion)) { + goto Error; + } + if (_bootloaderVersion < BL_REV_MIN || _bootloaderVersion > BL_REV_MAX) { + _errorString = tr("Found unsupported bootloader version: %1").arg(_bootloaderVersion); + goto Error; + } + if (!_protoGetDevice(INFO_BOARD_ID, _boardID)) { + goto Error; + } + if (!_protoGetDevice(INFO_FLASH_SIZE, _boardFlashSize)) { + qWarning() << _errorString; + goto Error; + } + + // Older V2 boards have large flash space but silicon error which prevents it from being used. Bootloader v5 and above + // will correctly account/report for this. Older bootloaders will not. Newer V2 board which support larger flash space are + // reported as V3 board id. + if (_boardID == boardIDPX4FMUV2 && _bootloaderVersion >= _bootloaderVersionV2CorrectFlash && _boardFlashSize > _flashSizeSmall) { + _boardID = boardIDPX4FMUV3; + } + + bootloaderVersion = _bootloaderVersion; + boardID = _boardID; + flashSize = _boardFlashSize; + + return true; + } + +Error: + qCDebug(FirmwareUpgradeLog) << "getBoardInfo failed:" << _errorString; + _errorString.prepend(tr("Get Board Info: ")); + return false; +} + +bool Bootloader::initFlashSequence(void) +{ + if (_sikRadio && !_inBootloaderMode) { + _write("AT&UPDATE\r\n"); + if (!_port.waitForReadyRead(1500)) { + _errorString = tr("Unable to reboot radio (ready read)"); + return false; + } + _port.setBaudRate(QSerialPort::Baud115200); + } + if (!_sync()) { + return false; + } + return true; +} + +bool Bootloader::erase(void) +{ + // Erase is slow, need larger timeout + if (!_sendCommand(PROTO_CHIP_ERASE, _eraseTimeout)) { + _errorString = tr("Erase failed: %1").arg(_errorString); + return false; + } + + return true; +} + +bool Bootloader::program(const FirmwareImage* image) +{ + if (image->imageIsBinFormat()) { + return _binProgram(image); + } else { + return _ihxProgram(image); + } +} + +bool Bootloader::reboot(void) +{ + bool success; + if (_sikRadio && !_inBootloaderMode) { + qCDebug(FirmwareUpgradeLog) << "reboot ATZ"; + _port.readAll(); + success = _write("ATZ\r\n"); + } else { + qCDebug(FirmwareUpgradeLog) << "reboot"; + success = _write(PROTO_BOOT) && _write(PROTO_EOC); + } + _port.flush(); + if (success) { + QGC::SLEEP::msleep(1000); + } + return success; +} + +bool Bootloader::_write(const char* data) +{ + return _write((uint8_t*)data, qstrlen(data)); +} + +bool Bootloader::_write(const uint8_t* data, qint64 maxSize) +{ + qint64 bytesWritten = _port.write((const char*)data, maxSize); if (bytesWritten == -1) { - _errorString = tr("Write failed: %1").arg(port->errorString()); + _errorString = tr("Write failed: %1").arg(_port.errorString()); qWarning() << _errorString; return false; } @@ -45,49 +234,43 @@ bool Bootloader::_write(QSerialPort* port, const uint8_t* data, qint64 maxSize) return true; } -bool Bootloader::_write(QSerialPort* port, const uint8_t byte) +bool Bootloader::_write(const uint8_t byte) { uint8_t buf[1] = { byte }; - return _write(port, buf, 1); + return _write(buf, 1); } -bool Bootloader::_read(QSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout) +bool Bootloader::_read(uint8_t* data, qint64 cBytesExpected, int readTimeout) { - qint64 bytesAlreadyRead = 0; - - while (bytesAlreadyRead < maxSize) { - QElapsedTimer timeout; - timeout.start(); - while (port->bytesAvailable() < 1) { - if (timeout.elapsed() > readTimeout) { - _errorString = tr("Timeout waiting for bytes to be available"); - return false; - } - port->waitForReadyRead(100); - } - - qint64 bytesRead; - bytesRead = port->read((char*)&data[bytesAlreadyRead], maxSize); - - if (bytesRead == -1) { - _errorString = tr("Read failed: error: %1").arg(port->errorString()); + QElapsedTimer timeout; + + timeout.start(); + while (_port.bytesAvailable() < cBytesExpected) { + if (timeout.elapsed() > readTimeout) { + _errorString = tr("Timeout waiting for bytes to be available"); return false; - } else { - Q_ASSERT(bytesRead != 0); - bytesAlreadyRead += bytesRead; } + _port.waitForReadyRead(100); } - + + qint64 bytesRead; + bytesRead = _port.read((char *)data, cBytesExpected); + + if (bytesRead != cBytesExpected) { + _errorString = tr("Read failed: error: %1").arg(_port.errorString()); + return false; + } + return true; } /// Read a PROTO_SYNC command response from the bootloader /// @param responseTimeout Msecs to wait for response bytes to become available on port -bool Bootloader::_getCommandResponse(QSerialPort* port, int responseTimeout) +bool Bootloader::_getCommandResponse(int responseTimeout) { uint8_t response[2]; - if (!_read(port, response, 2, responseTimeout)) { + if (!_read(response, 2, responseTimeout)) { _errorString.prepend(tr("Get Command Response: ")); return false; } @@ -116,40 +299,38 @@ bool Bootloader::_getCommandResponse(QSerialPort* port, int responseTimeout) /// Send a PROTO_GET_DEVICE command to retrieve a value from the PX4 bootloader /// @param param Value to retrieve using INFO_BOARD_* enums /// @param value Returned value -bool Bootloader::_getPX4BoardInfo(QSerialPort* port, uint8_t param, uint32_t& value) +bool Bootloader::_protoGetDevice(uint8_t param, uint32_t& value) { uint8_t buf[3] = { PROTO_GET_DEVICE, param, PROTO_EOC }; - if (!_write(port, buf, sizeof(buf))) { + if (!_write(buf, sizeof(buf))) { goto Error; } - port->flush(); - if (!_read(port, (uint8_t*)&value, sizeof(value))) { + if (!_read((uint8_t*)&value, sizeof(value))) { goto Error; } - if (!_getCommandResponse(port)) { + if (!_getCommandResponse()) { goto Error; } return true; Error: - _errorString.prepend(tr("Get Board Info: ")); + _errorString.prepend(tr("Get Device: ")); return false; } /// Send a command to the bootloader /// @param cmd Command to send using PROTO_* enums /// @return true: Command sent and valid sync response returned -bool Bootloader::_sendCommand(QSerialPort* port, const uint8_t cmd, int responseTimeout) +bool Bootloader::_sendCommand(const uint8_t cmd, int responseTimeout) { uint8_t buf[2] = { cmd, PROTO_EOC }; - if (!_write(port, buf, 2)) { + if (!_write(buf, 2)) { goto Error; } - port->flush(); - if (!_getCommandResponse(port, responseTimeout)) { + if (!_getCommandResponse(responseTimeout)) { goto Error; } @@ -160,27 +341,7 @@ Error: return false; } -bool Bootloader::erase(QSerialPort* port) -{ - // Erase is slow, need larger timeout - if (!_sendCommand(port, PROTO_CHIP_ERASE, _eraseTimeout)) { - _errorString = tr("Board erase failed: %1").arg(_errorString); - return false; - } - - return true; -} - -bool Bootloader::program(QSerialPort* port, const FirmwareImage* image) -{ - if (image->imageIsBinFormat()) { - return _binProgram(port, image); - } else { - return _ihxProgram(port, image); - } -} - -bool Bootloader::_binProgram(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::_binProgram(const FirmwareImage* image) { QFile firmwareFile(image->binFilename()); if (!firmwareFile.open(QIODevice::ReadOnly)) { @@ -212,43 +373,39 @@ bool Bootloader::_binProgram(QSerialPort* port, const FirmwareImage* image) Q_ASSERT(bytesToSend <= 0x8F); bool failed = true; - if (_write(port, PROTO_PROG_MULTI)) { - if (_write(port, (uint8_t)bytesToSend)) { - if (_write(port, imageBuf, bytesToSend)) { - if (_write(port, PROTO_EOC)) { - port->flush(); - if (_getCommandResponse(port)) { - failed = false; - } - } - } + if (_write(PROTO_PROG_MULTI) && + _write((uint8_t)bytesToSend) && + _write(imageBuf, bytesToSend) && + _write(PROTO_EOC)) { + if (_getCommandResponse()) { + failed = false; } } if (failed) { _errorString = tr("Flash failed: %1 at address 0x%2").arg(_errorString).arg(bytesSent, 8, 16, QLatin1Char('0')); return false; } - + bytesSent += bytesToSend; - + // Calculate the CRC now so we can test it after the board is flashed. _imageCRC = QGC::crc32((uint8_t *)imageBuf, bytesToSend, _imageCRC); - + emit updateProgress(bytesSent, imageSize); } firmwareFile.close(); - + // We calculate the CRC using the entire flash size, filling the remainder with 0xFF. while (bytesSent < _boardFlashSize) { const uint8_t fill = 0xFF; _imageCRC = QGC::crc32(&fill, 1, _imageCRC); bytesSent++; } - + return true; } -bool Bootloader::_ihxProgram(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::_ihxProgram(const FirmwareImage* image) { uint32_t imageSize = image->imageSize(); uint32_t bytesSent = 0; @@ -268,12 +425,12 @@ bool Bootloader::_ihxProgram(QSerialPort* port, const FirmwareImage* image) // Set flash address failed = true; - if (_write(port, PROTO_LOAD_ADDRESS) && - _write(port, flashAddress & 0xFF) && - _write(port, (flashAddress >> 8) & 0xFF) && - _write(port, PROTO_EOC)) { - port->flush(); - if (_getCommandResponse(port)) { + if (_write(PROTO_LOAD_ADDRESS) && + _write(flashAddress & 0xFF) && + _write((flashAddress >> 8) & 0xFF) && + _write(PROTO_EOC)) { + _port.flush(); + if (_getCommandResponse()) { failed = false; } } @@ -290,20 +447,20 @@ bool Bootloader::_ihxProgram(QSerialPort* port, const FirmwareImage* image) while (bytesLeftToWrite > 0) { uint8_t bytesToWrite; - + if (bytesLeftToWrite > PROG_MULTI_MAX) { bytesToWrite = PROG_MULTI_MAX; } else { bytesToWrite = bytesLeftToWrite; } - + failed = true; - if (_write(port, PROTO_PROG_MULTI) && - _write(port, bytesToWrite) && - _write(port, &((uint8_t *)bytes.data())[bytesIndex], bytesToWrite) && - _write(port, PROTO_EOC)) { - port->flush(); - if (_getCommandResponse(port)) { + if (_write(PROTO_PROG_MULTI) && + _write(bytesToWrite) && + _write(&((uint8_t *)bytes.data())[bytesIndex], bytesToWrite) && + _write(PROTO_EOC)) { + _port.flush(); + if (_getCommandResponse()) { failed = false; } } @@ -323,32 +480,32 @@ bool Bootloader::_ihxProgram(QSerialPort* port, const FirmwareImage* image) return true; } -bool Bootloader::verify(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::verify(const FirmwareImage* image) { bool ret; if (!image->imageIsBinFormat() || _bootloaderVersion <= 2) { - ret = _verifyBytes(port, image); + ret = _verifyBytes(image); } else { - ret = _verifyCRC(port); + ret = _verifyCRC(); } - reboot(port); + reboot(); return ret; } /// @brief Verify the flash on bootloader reading it back and comparing it against the original image -bool Bootloader::_verifyBytes(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::_verifyBytes(const FirmwareImage* image) { if (image->imageIsBinFormat()) { - return _binVerifyBytes(port, image); + return _binVerifyBytes(image); } else { - return _ihxVerifyBytes(port, image); + return _ihxVerifyBytes(image); } } -bool Bootloader::_binVerifyBytes(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::_binVerifyBytes(const FirmwareImage* image) { Q_ASSERT(image->imageIsBinFormat()); @@ -359,7 +516,7 @@ bool Bootloader::_binVerifyBytes(QSerialPort* port, const FirmwareImage* image) } uint32_t imageSize = (uint32_t)firmwareFile.size(); - if (!_sendCommand(port, PROTO_CHIP_VERIFY)) { + if (!_sendCommand(PROTO_CHIP_VERIFY)) { return false; } @@ -386,12 +543,12 @@ bool Bootloader::_binVerifyBytes(QSerialPort* port, const FirmwareImage* image) Q_ASSERT(bytesToRead <= 0x8F); bool failed = true; - if (_write(port, PROTO_READ_MULTI) && - _write(port, (uint8_t)bytesToRead) && - _write(port, PROTO_EOC)) { - port->flush(); - if (_read(port, readBuf, bytesToRead)) { - if (_getCommandResponse(port)) { + if (_write(PROTO_READ_MULTI) && + _write((uint8_t)bytesToRead) && + _write(PROTO_EOC)) { + _port.flush(); + if (_read(readBuf, bytesToRead)) { + if (_getCommandResponse()) { failed = false; } } @@ -418,7 +575,7 @@ bool Bootloader::_binVerifyBytes(QSerialPort* port, const FirmwareImage* image) return true; } -bool Bootloader::_ihxVerifyBytes(QSerialPort* port, const FirmwareImage* image) +bool Bootloader::_ihxVerifyBytes(const FirmwareImage* image) { Q_ASSERT(!image->imageIsBinFormat()); @@ -440,12 +597,12 @@ bool Bootloader::_ihxVerifyBytes(QSerialPort* port, const FirmwareImage* image) // Set read address failed = true; - if (_write(port, PROTO_LOAD_ADDRESS) && - _write(port, readAddress & 0xFF) && - _write(port, (readAddress >> 8) & 0xFF) && - _write(port, PROTO_EOC)) { - port->flush(); - if (_getCommandResponse(port)) { + if (_write(PROTO_LOAD_ADDRESS) && + _write(readAddress & 0xFF) && + _write((readAddress >> 8) & 0xFF) && + _write(PROTO_EOC)) { + _port.flush(); + if (_getCommandResponse()) { failed = false; } } @@ -471,12 +628,12 @@ bool Bootloader::_ihxVerifyBytes(QSerialPort* port, const FirmwareImage* image) } failed = true; - if (_write(port, PROTO_READ_MULTI) && - _write(port, bytesToRead) && - _write(port, PROTO_EOC)) { - port->flush(); - if (_read(port, readBuf, bytesToRead)) { - if (_getCommandResponse(port)) { + if (_write(PROTO_READ_MULTI) && + _write(bytesToRead) && + _write(PROTO_EOC)) { + _port.flush(); + if (_read(readBuf, bytesToRead)) { + if (_getCommandResponse()) { failed = false; } } @@ -507,16 +664,17 @@ bool Bootloader::_ihxVerifyBytes(QSerialPort* port, const FirmwareImage* image) } /// @Brief Verify the flash by comparing CRCs. -bool Bootloader::_verifyCRC(QSerialPort* port) +bool Bootloader::_verifyCRC(void) { uint8_t buf[2] = { PROTO_GET_CRC, PROTO_EOC }; + quint32 flashCRC; bool failed = true; - if (_write(port, buf, 2)) { - port->flush(); - if (_read(port, (uint8_t*)&flashCRC, sizeof(flashCRC), _verifyTimeout)) { - if (_getCommandResponse(port)) { + if (_write(buf, 2)) { + _port.flush(); + if (_read((uint8_t*)&flashCRC, sizeof(flashCRC), _verifyTimeout)) { + if (_getCommandResponse()) { failed = false; } } @@ -533,29 +691,11 @@ bool Bootloader::_verifyCRC(QSerialPort* port) return true; } -bool Bootloader::open(QSerialPort* port, const QString portName) -{ - Q_ASSERT(!port->isOpen()); - - port->setPortName(portName); - port->setBaudRate(QSerialPort::Baud115200); - port->setDataBits(QSerialPort::Data8); - port->setParity(QSerialPort::NoParity); - port->setStopBits(QSerialPort::OneStop); - port->setFlowControl(QSerialPort::NoFlowControl); - - if (!port->open(QIODevice::ReadWrite)) { - _errorString = tr("Open failed on port %1: %2").arg(portName, port->errorString()); - return false; - } - - return true; -} - -bool Bootloader::sync(QSerialPort* port) +bool Bootloader::_syncWorker(void) { // Send sync command - if (_sendCommand(port, PROTO_GET_SYNC)) { + if (_sendCommand(PROTO_GET_SYNC)) { + _inBootloaderMode = true; return true; } else { _errorString.prepend("Sync: "); @@ -563,77 +703,44 @@ bool Bootloader::sync(QSerialPort* port) } } -bool Bootloader::getPX4BoardInfo(QSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize) +bool Bootloader::_sync(void) { - - if (!_getPX4BoardInfo(port, INFO_BL_REV, _bootloaderVersion)) { - goto Error; - } - if (_bootloaderVersion < BL_REV_MIN || _bootloaderVersion > BL_REV_MAX) { - _errorString = tr("Found unsupported bootloader version: %1").arg(_bootloaderVersion); - goto Error; - } - - if (!_getPX4BoardInfo(port, INFO_BOARD_ID, _boardID)) { - goto Error; - } - - if (!_getPX4BoardInfo(port, INFO_FLASH_SIZE, _boardFlashSize)) { - qWarning() << _errorString; - goto Error; - } - - // Older V2 boards have large flash space but silicon error which prevents it from being used. Bootloader v5 and above - // will correctly account/report for this. Older bootloaders will not. Newer V2 board which support larger flash space are - // reported as V3 board id. - if (_boardID == boardIDPX4FMUV2 && _bootloaderVersion >= _bootloaderVersionV2CorrectFlash && _boardFlashSize > _flashSizeSmall) { - _boardID = boardIDPX4FMUV3; + if (_sikRadio) { + _port.readAll(); + bool success = false; + for (int i=0; i<3; i++) { + success = _syncWorker(); + } + return success; + } else { + return _syncWorker(); } - - bootloaderVersion = _bootloaderVersion; - boardID = _boardID; - flashSize = _boardFlashSize; - - return true; - -Error: - _errorString.prepend(tr("Get Board Info: ")); - return false; } -bool Bootloader::get3DRRadioBoardId(QSerialPort* port, uint32_t& boardID) +bool Bootloader::_get3DRRadioBoardId(uint32_t& boardID) { uint8_t buf[2] = { PROTO_GET_DEVICE, PROTO_EOC }; - - if (!_write(port, buf, sizeof(buf))) { + + if (!_write(buf, sizeof(buf))) { goto Error; } - port->flush(); - - if (!_read(port, (uint8_t*)buf, 2)) { + _port.flush(); + + if (!_read((uint8_t*)buf, 2)) { goto Error; } - if (!_getCommandResponse(port)) { + if (!_getCommandResponse()) { goto Error; } - + boardID = buf[0]; - + _bootloaderVersion = 0; _boardFlashSize = 0; - + return true; - + Error: _errorString.prepend(tr("Get Board Id: ")); return false; } - -bool Bootloader::reboot(QSerialPort* port) -{ - bool success = _write(port, PROTO_BOOT) && _write(port, PROTO_EOC); - if (success) { - port->waitForBytesWritten(100); - } - return success; -} diff --git a/src/VehicleSetup/Bootloader.h b/src/VehicleSetup/Bootloader.h index c4449afed2..adf605acf8 100644 --- a/src/VehicleSetup/Bootloader.h +++ b/src/VehicleSetup/Bootloader.h @@ -7,12 +7,7 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - -#ifndef Bootloader_H -#define Bootloader_H +#pragma once #include "FirmwareImage.h" @@ -26,60 +21,39 @@ class Bootloader : public QObject Q_OBJECT public: - explicit Bootloader(QObject *parent = 0); + explicit Bootloader(bool sikRadio, QObject *parent = 0); - /// @brief Returns the error message associated with the last failed call to one of the bootloader - /// utility routine below. QString errorString(void) { return _errorString; } - /// @brief Opens a port to the bootloader - bool open(QSerialPort* port, const QString portName); - - /// @brief Read a PROTO_SYNC response from the bootloader - /// @return true: Valid sync response was received - bool sync(QSerialPort* port); - - /// @brief Erases the current program - bool erase(QSerialPort* port); - - /// @brief Program the board with the specified image - bool program(QSerialPort* port, const FirmwareImage* image); - - /// @brief Verify the board flash. - bool verify(QSerialPort* port, const FirmwareImage* image); - - /// @brief Retrieve a set of board info from the bootloader of PX4 FMU and PX4 Flow boards - /// @param bootloaderVersion Returned INFO_BL_REV - /// @param boardID Returned INFO_BOARD_ID - /// @param flashSize Returned INFO_FLASH_SIZE - bool getPX4BoardInfo(QSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize); - - /// @brief Retrieve the board id from a 3DR Radio - bool get3DRRadioBoardId(QSerialPort* port, uint32_t& boardID); - - /// @brief Sends a PROTO_REBOOT command to the bootloader - bool reboot(QSerialPort* port); + bool open (const QString portName); + void close (void) { _port.close(); } + bool getBoardInfo (uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize); + bool initFlashSequence (void); + bool erase (void); + bool program (const FirmwareImage* image); + bool verify (const FirmwareImage* image); + bool reboot (void); // Supported bootloader board ids - static const int boardIDPX4FMUV1 = 5; ///< PX4 V1 board, as from USB PID - static const int boardIDPX4FMUV2 = 9; ///< PX4 V2 board, as from USB PID - static const int boardIDPX4FMUV4 = 11; ///< PX4 V4 board, as from USB PID - static const int boardIDPX4FMUV4PRO = 13; ///< PX4 V4PRO board, as from USB PID - static const int boardIDPX4FMUV5 = 50; ///< PX4 V5 board, as from USB PID - static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID - static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board, as from USB PID - static const int boardIDAUAVX2_1 = 33; ///< AUAV X2.1 board, as from USB PID - static const int boardID3DRRadio = 78; ///< 3DR Radio. This is an arbitrary value unrelated to the PID - static const int boardIDMINDPXFMUV2 = 88; ///< MindPX V2 board, as from USB PID - static const int boardIDTAPV1 = 64; ///< TAP V1 board, as from USB PID - static const int boardIDASCV1 = 65; ///< ASC V1 board, as from USB PID - static const int boardIDCrazyflie2 = 12; ///< Crazyflie 2.0 board, as from USB PID - static const int boardIDOmnibusF4SD = 42; ///< Omnibus F4 SD, as from USB PID - static const int boardIDFMUK66V3 = 28; ///< FMUK66V3 board, as from USB PID - static const int boardIDKakuteF7 = 123; ///< Holybro KakuteF7 board, as from USB PID - static const int boardIDDurandalV1 = 139; ///< Holybro Durandal-v1 board, as from USB PID - static const int boardIDModalFCV1 = 41775; ///< ModalAI FC V1 board, as from USB PID - static const int boardIDUVifyCore = 20; ///< UVify Core board, as from USB PID + static const int boardIDPX4FMUV1 = 5; ///< PX4 V1 board, as from USB PID + static const int boardIDPX4FMUV2 = 9; ///< PX4 V2 board, as from USB PID + static const int boardIDPX4FMUV4 = 11; ///< PX4 V4 board, as from USB PID + static const int boardIDPX4FMUV4PRO = 13; ///< PX4 V4PRO board, as from USB PID + static const int boardIDPX4FMUV5 = 50; ///< PX4 V5 board, as from USB PID + static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID + static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board, as from USB PID + static const int boardIDAUAVX2_1 = 33; ///< AUAV X2.1 board, as from USB PID + static const int boardID3DRRadio = 78; ///< 3DR Radio. This is an arbitrary value unrelated to the PID + static const int boardIDMINDPXFMUV2 = 88; ///< MindPX V2 board, as from USB PID + static const int boardIDTAPV1 = 64; ///< TAP V1 board, as from USB PID + static const int boardIDASCV1 = 65; ///< ASC V1 board, as from USB PID + static const int boardIDCrazyflie2 = 12; ///< Crazyflie 2.0 board, as from USB PID + static const int boardIDOmnibusF4SD = 42; ///< Omnibus F4 SD, as from USB PID + static const int boardIDFMUK66V3 = 28; ///< FMUK66V3 board, as from USB PID + static const int boardIDKakuteF7 = 123; ///< Holybro KakuteF7 board, as from USB PID + static const int boardIDDurandalV1 = 139; ///< Holybro Durandal-v1 board, as from USB PID + static const int boardIDModalFCV1 = 41775; ///< ModalAI FC V1 board, as from USB PID + static const int boardIDUVifyCore = 20; ///< UVify Core board, as from USB PID /// Simulated board id for V3 which is a V2 board which supports larger flash space /// IMPORTANT: Make sure this id does not conflict with any newly added real board ids @@ -90,23 +64,23 @@ signals: void updateProgress(int curr, int total); private: - bool _binProgram(QSerialPort* port, const FirmwareImage* image); - bool _ihxProgram(QSerialPort* port, const FirmwareImage* image); - - bool _write(QSerialPort* port, const uint8_t* data, qint64 maxSize); - bool _write(QSerialPort* port, const uint8_t byte); - - bool _read(QSerialPort* port, uint8_t* data, qint64 maxSize, int readTimeout = _readTimout); - - bool _sendCommand(QSerialPort* port, uint8_t cmd, int responseTimeout = _responseTimeout); - bool _getCommandResponse(QSerialPort* port, const int responseTimeout = _responseTimeout); - - bool _getPX4BoardInfo(QSerialPort* port, uint8_t param, uint32_t& value); - - bool _verifyBytes(QSerialPort* port, const FirmwareImage* image); - bool _binVerifyBytes(QSerialPort* port, const FirmwareImage* image); - bool _ihxVerifyBytes(QSerialPort* port, const FirmwareImage* image); - bool _verifyCRC(QSerialPort* port); + bool _sync (void); + bool _syncWorker (void); + bool _binProgram (const FirmwareImage* image); + bool _ihxProgram (const FirmwareImage* image); + bool _write (const uint8_t* data, qint64 maxSize); + bool _write (const uint8_t byte); + bool _write (const char* data); + bool _read (uint8_t* data, qint64 maxSize, int readTimeout = _readTimout); + bool _sendCommand (uint8_t cmd, int responseTimeout = _responseTimeout); + bool _getCommandResponse (const int responseTimeout = _responseTimeout); + bool _protoGetDevice (uint8_t param, uint32_t& value); + bool _verifyBytes (const FirmwareImage* image); + bool _binVerifyBytes (const FirmwareImage* image); + bool _ihxVerifyBytes (const FirmwareImage* image); + bool _verifyCRC (void); + QString _getNextLine (int timeoutMsecs); + bool _get3DRRadioBoardId (uint32_t& boardID); enum { // protocol bytes @@ -143,22 +117,21 @@ private: READ_MULTI_MAX = 0x28 ///< read size for PROTO_READ_MULTI, must be multiple of 4. Sik Radio max size is 0x28 }; - uint32_t _boardID; ///< board id for currently connected board - uint32_t _boardFlashSize; ///< flash size for currently connected board - uint32_t _imageCRC; ///< CRC for image in currently selected firmware file - uint32_t _bootloaderVersion; ///< Bootloader version - - QString _firmwareFilename; ///< Currently selected firmware file to flash - - QString _errorString; ///< Last error - - static const int _eraseTimeout = 20000; ///< Msecs to wait for response from erase command - static const int _rebootTimeout = 10000; ///< Msecs to wait for reboot command to cause serial port to disconnect - static const int _verifyTimeout = 5000; ///< Msecs to wait for response to PROTO_GET_CRC command - static const int _readTimout = 2000; ///< Msecs to wait for read bytes to become available - static const int _responseTimeout = 2000; ///< Msecs to wait for command response bytes - static const int _flashSizeSmall = 1032192; ///< Flash size for boards with silicon error - static const int _bootloaderVersionV2CorrectFlash = 5; ///< Anything below this bootloader version on V2 boards cannot trust flash size + QSerialPort _port; + bool _sikRadio = false; + bool _inBootloaderMode = false; ///< true: board is in bootloader mode, false: special case for SiK Radio, board is in command mode + uint32_t _boardID = 0; ///< board id for currently connected board + uint32_t _boardFlashSize = 0; ///< flash size for currently connected board + uint32_t _bootloaderVersion = 0; ///< Bootloader version + uint32_t _imageCRC = 0; ///< CRC for image in currently selected firmware file + QString _firmwareFilename; ///< Currently selected firmware file to flash + QString _errorString; ///< Last error + + static const int _eraseTimeout = 20000; ///< Msecs to wait for response from erase command + static const int _rebootTimeout = 10000; ///< Msecs to wait for reboot command to cause serial port to disconnect + static const int _verifyTimeout = 5000; ///< Msecs to wait for response to PROTO_GET_CRC command + static const int _readTimout = 2000; ///< Msecs to wait for read bytes to become available + static const int _responseTimeout = 2000; ///< Msecs to wait for command response bytes + static const int _flashSizeSmall = 1032192; ///< Flash size for boards with silicon error + static const int _bootloaderVersionV2CorrectFlash = 5; ///< Anything below this bootloader version on V2 boards cannot trust flash size }; - -#endif // PX4FirmwareUpgrade_H diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 4869554ec4..d4ad0d3b8b 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -245,11 +245,6 @@ SetupPage { cancelFlash() } - Connections { - target: firmwarePage - onCancelDialog: reject() - } - ListModel { id: firmwareBuildTypeList diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index 58a2adc893..e2b697cc09 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -84,8 +84,7 @@ FirmwareUpgradeController::FirmwareUpgradeController(void) connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoard, this, &FirmwareUpgradeController::_foundBoard); connect(_threadController, &PX4FirmwareUpgradeThreadController::noBoardFound, this, &FirmwareUpgradeController::_noBoardFound); connect(_threadController, &PX4FirmwareUpgradeThreadController::boardGone, this, &FirmwareUpgradeController::_boardGone); - connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBootloader, this, &FirmwareUpgradeController::_foundBootloader); - connect(_threadController, &PX4FirmwareUpgradeThreadController::bootloaderSyncFailed, this, &FirmwareUpgradeController::_bootloaderSyncFailed); + connect(_threadController, &PX4FirmwareUpgradeThreadController::foundBoardInfo, this, &FirmwareUpgradeController::_foundBoardInfo); connect(_threadController, &PX4FirmwareUpgradeThreadController::error, this, &FirmwareUpgradeController::_error); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); connect(_threadController, &PX4FirmwareUpgradeThreadController::status, this, &FirmwareUpgradeController::_status); @@ -193,15 +192,15 @@ QStringList FirmwareUpgradeController::availableBoardsName(void) void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName) { - _foundBoardInfo = info; - _foundBoardType = static_cast(boardType); - _foundBoardTypeName = boardName; + _boardInfo = info; + _boardType = static_cast(boardType); + _boardTypeName = boardName; qDebug() << info.manufacturer() << info.description(); _startFlashWhenBootloaderFound = false; - if (_foundBoardType == QGCSerialPortInfo::BoardTypeSiKRadio) { + if (_boardType == QGCSerialPortInfo::BoardTypeSiKRadio) { if (!firstAttempt) { // Radio always flashes latest firmware, so we can start right away without // any further user input. @@ -212,7 +211,7 @@ void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPort } } - qCDebug(FirmwareUpgradeLog) << _foundBoardType << _foundBoardTypeName; + qCDebug(FirmwareUpgradeLog) << _boardType << _boardTypeName; emit boardFound(); } @@ -229,12 +228,12 @@ void FirmwareUpgradeController::_boardGone(void) /// @brief Called when the bootloader is connected to by the findBootloader process. Moves the state machine /// to the next step. -void FirmwareUpgradeController::_foundBootloader(int bootloaderVersion, int boardID, int flashSize) +void FirmwareUpgradeController::_foundBoardInfo(int bootloaderVersion, int boardID, int flashSize) { - _bootloaderFound = true; - _bootloaderVersion = static_cast(bootloaderVersion); - _bootloaderBoardID = static_cast(boardID); - _bootloaderBoardFlashSize = static_cast(flashSize); + _bootloaderFound = true; + _bootloaderVersion = static_cast(bootloaderVersion); + _bootloaderBoardID = static_cast(boardID); + _bootloaderBoardFlashSize = static_cast(flashSize); _appendStatusLog(tr("Connected to bootloader:")); _appendStatusLog(tr(" Version: %1").arg(_bootloaderVersion)); @@ -747,9 +746,9 @@ void FirmwareUpgradeController::_buildAPMFirmwareNames(void) bool chibios = _apmChibiOSSetting->rawValue().toInt() == 0; FirmwareVehicleType_t vehicleType = static_cast(_apmVehicleTypeSetting->rawValue().toInt()); - QString boardDescription = _foundBoardInfo.description(); - quint16 boardVID = _foundBoardInfo.vendorIdentifier(); - quint16 boardPID = _foundBoardInfo.productIdentifier(); + QString boardDescription = _boardInfo.description(); + quint16 boardVID = _boardInfo.vendorIdentifier(); + quint16 boardPID = _boardInfo.productIdentifier(); uint32_t rawBoardId = _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID; qCDebug(FirmwareUpgradeLog) << QStringLiteral("_buildAPMFirmwareNames description(%1) vid(%2/0x%3) pid(%4/0x%5)").arg(boardDescription).arg(boardVID).arg(boardVID, 1, 16).arg(boardPID).arg(boardPID, 1, 16); diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index fa7290af93..01938471aa 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -89,7 +89,7 @@ public: Q_PROPERTY(bool downloadingFirmwareList MEMBER _downloadingFirmwareList NOTIFY downloadingFirmwareListChanged) Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound) Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) - Q_PROPERTY(QString boardType MEMBER _foundBoardTypeName NOTIFY boardFound) + Q_PROPERTY(QString boardType MEMBER _boardTypeName NOTIFY boardFound) Q_PROPERTY(bool pixhawkBoard READ pixhawkBoard NOTIFY boardFound) Q_PROPERTY(bool px4FlowBoard READ px4FlowBoard NOTIFY boardFound) Q_PROPERTY(FirmwareBuildType_t selectedFirmwareBuildType READ selectedFirmwareBuildType WRITE setSelectedFirmwareBuildType NOTIFY selectedFirmwareBuildTypeChanged) @@ -134,8 +134,8 @@ public: QQuickItem* statusLog(void) { return _statusLog; } void setStatusLog(QQuickItem* statusLog) { _statusLog = statusLog; } - QString boardPort(void) { return _foundBoardInfo.portName(); } - QString boardDescription(void) { return _foundBoardInfo.description(); } + QString boardPort(void) { return _boardInfo.portName(); } + QString boardDescription(void) { return _boardInfo.description(); } FirmwareBuildType_t selectedFirmwareBuildType(void) { return _selectedFirmwareBuildType; } void setSelectedFirmwareBuildType(FirmwareBuildType_t firmwareType); @@ -144,8 +144,8 @@ public: QString px4StableVersion (void) { return _px4StableVersion; } QString px4BetaVersion (void) { return _px4BetaVersion; } - bool pixhawkBoard(void) const { return _foundBoardType == QGCSerialPortInfo::BoardTypePixhawk; } - bool px4FlowBoard(void) const { return _foundBoardType == QGCSerialPortInfo::BoardTypePX4Flow; } + bool pixhawkBoard(void) const { return _boardType == QGCSerialPortInfo::BoardTypePixhawk; } + bool px4FlowBoard(void) const { return _boardType == QGCSerialPortInfo::BoardTypePX4Flow; } /** * @brief Return a human friendly string of available boards @@ -175,7 +175,7 @@ private slots: void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int boardType, QString boardName); void _noBoardFound(void); void _boardGone(); - void _foundBootloader(int bootloaderVersion, int boardID, int flashSize); + void _foundBoardInfo(int bootloaderVersion, int boardID, int flashSize); void _error(const QString& errorString); void _status(const QString& statusString); void _bootloaderSyncFailed(void); @@ -266,13 +266,13 @@ private: bool _searchingForBoard; ///< true: searching for board, false: search for bootloader - QSerialPortInfo _foundBoardInfo; - QGCSerialPortInfo::BoardType_t _foundBoardType; - QString _foundBoardTypeName; + QSerialPortInfo _boardInfo; + QGCSerialPortInfo::BoardType_t _boardType; + QString _boardTypeName; - FirmwareBuildType_t _selectedFirmwareBuildType; + FirmwareBuildType_t _selectedFirmwareBuildType; - FirmwareImage* _image; + FirmwareImage* _image; QString _px4StableVersion; // Version strange for latest PX4 stable QString _px4BetaVersion; // Version strange for latest PX4 beta diff --git a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc index a005e8c61c..dfa91adc3a 100644 --- a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc +++ b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc @@ -21,16 +21,9 @@ #include #include -PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller) : - _controller(controller), - _bootloader(nullptr), - _bootloaderPort(nullptr), - _timerRetry(nullptr), - _foundBoard(false), - _findBoardFirstAttempt(true) +PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller) + : _controller(controller) { - Q_ASSERT(_controller); - connect(_controller, &PX4FirmwareUpgradeThreadController::_initThreadWorker, this, &PX4FirmwareUpgradeThreadWorker::_init); connect(_controller, &PX4FirmwareUpgradeThreadController::_startFindBoardLoopOnThread, this, &PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop); connect(_controller, &PX4FirmwareUpgradeThreadController::_flashOnThread, this, &PX4FirmwareUpgradeThreadWorker::_flash); @@ -40,36 +33,27 @@ PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgrad PX4FirmwareUpgradeThreadWorker::~PX4FirmwareUpgradeThreadWorker() { - if (_bootloaderPort) { - // deleteLater so delete happens on correct thread - _bootloaderPort->deleteLater(); - } + } -/// @brief Initializes the PX4FirmwareUpgradeThreadWorker with the various child objects which must be created -/// on the worker thread. void PX4FirmwareUpgradeThreadWorker::_init(void) { // We create the timers here so that they are on the right thread - Q_ASSERT(_timerRetry == nullptr); - _timerRetry = new QTimer(this); - Q_CHECK_PTR(_timerRetry); - _timerRetry->setSingleShot(true); - _timerRetry->setInterval(_retryTimeout); - connect(_timerRetry, &QTimer::timeout, this, &PX4FirmwareUpgradeThreadWorker::_findBoardOnce); - - Q_ASSERT(_bootloader == nullptr); - _bootloader = new Bootloader(this); - connect(_bootloader, &Bootloader::updateProgress, this, &PX4FirmwareUpgradeThreadWorker::_updateProgress); + _findBoardTimer = new QTimer(this); + _findBoardTimer->setSingleShot(true); + _findBoardTimer->setInterval(500); + connect(_findBoardTimer, &QTimer::timeout, this, &PX4FirmwareUpgradeThreadWorker::_findBoardOnce); } void PX4FirmwareUpgradeThreadWorker::_cancel(void) { - if (_bootloaderPort) { - _bootloaderPort->close(); - _bootloaderPort->deleteLater(); - _bootloaderPort = nullptr; + qCDebug(FirmwareUpgradeVerboseLog) << "_cancel"; + if (_bootloader) { + _bootloader->reboot(); + _bootloader->close(); + _bootloader->deleteLater(); + _bootloader = nullptr; } } @@ -94,11 +78,22 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void) _foundBoardPortInfo = portInfo; emit foundBoard(_findBoardFirstAttempt, portInfo, boardType, boardName); if (!_findBoardFirstAttempt) { - if (boardType == QGCSerialPortInfo::BoardTypeSiKRadio) { - _3drRadioForceBootloader(portInfo); - return; + + _bootloader = new Bootloader(boardType == QGCSerialPortInfo::BoardTypeSiKRadio, this); + connect(_bootloader, &Bootloader::updateProgress, this, &PX4FirmwareUpgradeThreadWorker::_updateProgress); + + if (_bootloader->open(portInfo.portName())) { + uint32_t bootloaderVersion; + uint32_t boardId; + uint32_t flashSize; + if (_bootloader->getBoardInfo(bootloaderVersion, boardId, flashSize)) { + emit foundBoardInfo(bootloaderVersion, boardId, flashSize); + } else { + emit error(_bootloader->errorString()); + return; + } } else { - _findBootloader(portInfo, false /* radio mode */, true /* errorOnNotFound */); + emit error(_bootloader->errorString()); return; } } @@ -114,7 +109,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void) } _findBoardFirstAttempt = false; - _timerRetry->start(); + _findBoardTimer->start(); } bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType, QString& boardName) @@ -141,172 +136,59 @@ bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& port return false; } -void PX4FirmwareUpgradeThreadWorker::_3drRadioForceBootloader(const QGCSerialPortInfo& portInfo) +void PX4FirmwareUpgradeThreadWorker::_reboot(void) { - // First make sure we can't get the bootloader - - if (_findBootloader(portInfo, true /* radio Mode */, false /* errorOnNotFound */)) { - return; - } - - // Couldn't find the bootloader. We'll need to reboot the radio into bootloader. - - QSerialPort port(portInfo); - - port.setBaudRate(QSerialPort::Baud57600); - - emit status(tr("Putting radio into command mode")); - - // Wait a little while for the USB port to initialize. 3DR Radio boot is really slow. - QGC::SLEEP::msleep(2000); - port.open(QIODevice::ReadWrite); - - if (!port.isOpen()) { - emit error(tr("Unable to open port: %1 error: %2").arg(portInfo.systemLocation()).arg(port.errorString())); - return; - } - - // Put radio into command mode - QGC::SLEEP::msleep(2000); - port.write("+++", 3); - if (!port.waitForReadyRead(1500)) { - emit error(tr("Unable to put radio into command mode")); - return; - } - QByteArray bytes = port.readAll(); - if (!bytes.contains("OK")) { - qCDebug(FirmwareUpgradeLog) << bytes; - emit error(tr("Unable to put radio into command mode")); - return; - } - - emit status(tr("Rebooting radio to bootloader")); - - port.write("AT&UPDATE\r\n"); - if (!port.waitForBytesWritten(1500)) { - emit error(tr("Unable to reboot radio (bytes written)")); - return; - } - if (!port.waitForReadyRead(1500)) { - emit error(tr("Unable to reboot radio (ready read)")); - return; - } - port.close(); - QGC::SLEEP::msleep(2000); - - // The bootloader should be waiting for us now - - _findBootloader(portInfo, true /* radio mode */, true /* errorOnNotFound */); + _bootloader->reboot(); } -bool PX4FirmwareUpgradeThreadWorker::_findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound) +void PX4FirmwareUpgradeThreadWorker::_flash(void) { - qCDebug(FirmwareUpgradeLog) << "_findBootloader" << portInfo.systemLocation(); - - uint32_t bootloaderVersion = 0; - uint32_t boardID; - uint32_t flashSize = 0; - - _bootloaderPort = new QSerialPort(); - if (radioMode) { - _bootloaderPort->setBaudRate(QSerialPort::Baud115200); - } - - // Wait a little while for the USB port to initialize. - bool openFailed = true; - for (int i=0; i<10; i++) { - if (_bootloader->open(_bootloaderPort, portInfo.systemLocation())) { - openFailed = false; - break; - } else { - QGC::SLEEP::msleep(100); - } - } - - if (radioMode) { - QGC::SLEEP::msleep(2000); - } - - if (openFailed) { - qCDebug(FirmwareUpgradeLog) << "Bootloader open port failed:" << _bootloader->errorString(); - if (errorOnNotFound) { - emit error(_bootloader->errorString()); - } - _bootloaderPort->deleteLater(); - _bootloaderPort = nullptr; - return false; - } + qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_flash"; - if (_bootloader->sync(_bootloaderPort)) { - bool success; - - if (radioMode) { - success = _bootloader->get3DRRadioBoardId(_bootloaderPort, boardID); - } else { - success = _bootloader->getPX4BoardInfo(_bootloaderPort, bootloaderVersion, boardID, flashSize); - } - if (success) { - qCDebug(FirmwareUpgradeLog) << "Found bootloader"; - emit foundBootloader(bootloaderVersion, boardID, flashSize); - return true; - } - } - - _bootloaderPort->close(); - _bootloaderPort->deleteLater(); - _bootloaderPort = nullptr; - qCDebug(FirmwareUpgradeLog) << "Bootloader error:" << _bootloader->errorString(); - if (errorOnNotFound) { + if (!_bootloader->initFlashSequence()) { emit error(_bootloader->errorString()); + return; } - - return false; -} - -void PX4FirmwareUpgradeThreadWorker::_reboot(void) -{ - if (_bootloaderPort) { - if (_bootloaderPort->isOpen()) { - _bootloader->reboot(_bootloaderPort); - } - _bootloaderPort->deleteLater(); - _bootloaderPort = nullptr; - } -} -void PX4FirmwareUpgradeThreadWorker::_flash(void) -{ - qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_flash"; - if (_erase()) { emit status(tr("Programming new version...")); - if (_bootloader->program(_bootloaderPort, _controller->image())) { + if (_bootloader->program(_controller->image())) { qCDebug(FirmwareUpgradeLog) << "Program complete"; emit status("Program complete"); } else { - _bootloaderPort->deleteLater(); - _bootloaderPort = nullptr; qCDebug(FirmwareUpgradeLog) << "Program failed:" << _bootloader->errorString(); - emit error(_bootloader->errorString()); - return; + goto Error; } emit status(tr("Verifying program...")); - if (_bootloader->verify(_bootloaderPort, _controller->image())) { + if (_bootloader->verify(_controller->image())) { qCDebug(FirmwareUpgradeLog) << "Verify complete"; emit status(tr("Verify complete")); } else { qCDebug(FirmwareUpgradeLog) << "Verify failed:" << _bootloader->errorString(); - emit error(_bootloader->errorString()); - return; + goto Error; } } - emit _reboot(); + emit status(tr("Rebooting board")); + _reboot(); + + _bootloader->close(); + _bootloader->deleteLater(); + _bootloader = nullptr; emit flashComplete(); + + return; + +Error: + emit error(_bootloader->errorString()); + _reboot(); + _bootloader->close(); + _bootloader->deleteLater(); + _bootloader = nullptr; } bool PX4FirmwareUpgradeThreadWorker::_erase(void) @@ -316,7 +198,7 @@ bool PX4FirmwareUpgradeThreadWorker::_erase(void) emit eraseStarted(); emit status(tr("Erasing previous program...")); - if (_bootloader->erase(_bootloaderPort)) { + if (_bootloader->erase()) { qCDebug(FirmwareUpgradeLog) << "Erase complete"; emit status(tr("Erase complete")); emit eraseComplete(); @@ -331,19 +213,15 @@ bool PX4FirmwareUpgradeThreadWorker::_erase(void) PX4FirmwareUpgradeThreadController::PX4FirmwareUpgradeThreadController(QObject* parent) : QObject(parent) { - _worker = new PX4FirmwareUpgradeThreadWorker(this); - Q_CHECK_PTR(_worker); - - _workerThread = new QThread(this); - Q_CHECK_PTR(_workerThread); + _worker = new PX4FirmwareUpgradeThreadWorker(this); + _workerThread = new QThread(this); _worker->moveToThread(_workerThread); connect(_worker, &PX4FirmwareUpgradeThreadWorker::updateProgress, this, &PX4FirmwareUpgradeThreadController::_updateProgress); connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBoard, this, &PX4FirmwareUpgradeThreadController::_foundBoard); connect(_worker, &PX4FirmwareUpgradeThreadWorker::noBoardFound, this, &PX4FirmwareUpgradeThreadController::_noBoardFound); connect(_worker, &PX4FirmwareUpgradeThreadWorker::boardGone, this, &PX4FirmwareUpgradeThreadController::_boardGone); - connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBootloader, this, &PX4FirmwareUpgradeThreadController::_foundBootloader); - connect(_worker, &PX4FirmwareUpgradeThreadWorker::bootloaderSyncFailed, this, &PX4FirmwareUpgradeThreadController::_bootloaderSyncFailed); + connect(_worker, &PX4FirmwareUpgradeThreadWorker::foundBoardInfo, this, &PX4FirmwareUpgradeThreadController::_foundBoardInfo); connect(_worker, &PX4FirmwareUpgradeThreadWorker::error, this, &PX4FirmwareUpgradeThreadController::_error); connect(_worker, &PX4FirmwareUpgradeThreadWorker::status, this, &PX4FirmwareUpgradeThreadController::_status); connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseStarted, this, &PX4FirmwareUpgradeThreadController::_eraseStarted); diff --git a/src/VehicleSetup/PX4FirmwareUpgradeThread.h b/src/VehicleSetup/PX4FirmwareUpgradeThread.h index 9dbf6ba7a3..6645f3acd4 100644 --- a/src/VehicleSetup/PX4FirmwareUpgradeThread.h +++ b/src/VehicleSetup/PX4FirmwareUpgradeThread.h @@ -41,44 +41,39 @@ public: ~PX4FirmwareUpgradeThreadWorker(); signals: - void updateProgress(int curr, int total); - void foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString boardName); - void noBoardFound(void); - void boardGone(void); - void foundBootloader(int bootloaderVersion, int boardID, int flashSize); - void bootloaderSyncFailed(void); - void error(const QString& errorString); - void status(const QString& statusText); - void eraseStarted(void); - void eraseComplete(void); - void flashComplete(void); + void updateProgress (int curr, int total); + void foundBoard (bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString boardName); + void noBoardFound (void); + void boardGone (void); + void foundBoardInfo (int bootloaderVersion, int boardID, int flashSize); + void error (const QString& errorString); + void status (const QString& statusText); + void eraseStarted (void); + void eraseComplete (void); + void flashComplete (void); private slots: - void _init(void); + void _init (void); void _startFindBoardLoop(void); - void _reboot(void); - void _flash(void); - void _findBoardOnce(void); - void _updateProgress(int curr, int total) { emit updateProgress(curr, total); } - void _cancel(void); + void _reboot (void); + void _flash (void); + void _findBoardOnce (void); + void _updateProgress (int curr, int total) { emit updateProgress(curr, total); } + void _cancel (void); private: bool _findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType, QString& boardName); - bool _findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound); - void _3drRadioForceBootloader(const QGCSerialPortInfo& portInfo); - bool _erase(void); + bool _erase (void); PX4FirmwareUpgradeThreadController* _controller; - Bootloader* _bootloader; - QSerialPort* _bootloaderPort; - QTimer* _timerRetry; + Bootloader* _bootloader = nullptr; + QTimer* _findBoardTimer = nullptr; QTime _elapsed; - static const int _retryTimeout = 100; - - bool _foundBoard; ///< true: board is currently connected - bool _findBoardFirstAttempt; ///< true: this is our first try looking for a board - QGCSerialPortInfo _foundBoardPortInfo; ///< port info for found board + bool _foundBoard = false; + bool _boardIsSiKRadio = false; + bool _findBoardFirstAttempt = true; ///< true: we found the board right away, it needs to be unplugged and plugged back in + QGCSerialPortInfo _foundBoardPortInfo; ///< port info for found board }; /// @brief Provides methods to interact with the bootloader. The commands themselves are signalled @@ -105,58 +100,40 @@ public: const FirmwareImage* image(void) { return _image; } signals: - /// @brief Emitted by the find board process when it finds a board. - void foundBoard(bool firstAttempt, const QGCSerialPortInfo &portInfo, int boardType, QString boardName); - - void noBoardFound(void); - - /// @brief Emitted by the find board process when a board it previously reported as found disappears. - void boardGone(void); - - /// @brief Emitted by the findBootloader process when has a connection to the bootloader - void foundBootloader(int bootloaderVersion, int boardID, int flashSize); - - /// @brief Emitted by the bootloader commands when an error occurs. - void error(const QString& errorString); - - void status(const QString& status); - - /// @brief Signalled when the findBootloader process connects to the port, but cannot sync to the - /// bootloader. - void bootloaderSyncFailed(void); - - void eraseStarted(void); - void eraseComplete(void); - - void flashComplete(void); - - /// @brief Signalled to update progress for long running bootloader commands - void updateProgress(int curr, int total); + void foundBoard (bool firstAttempt, const QGCSerialPortInfo &portInfo, int boardType, QString boardName); + void noBoardFound (void); + void boardGone (void); + void foundBoardInfo (int bootloaderVersion, int boardID, int flashSize); + void error (const QString& errorString); + void status (const QString& status); + void eraseStarted (void); + void eraseComplete (void); + void flashComplete (void); + void updateProgress (int curr, int total); // Internal signals to communicate with thread worker - void _initThreadWorker(void); + void _initThreadWorker (void); void _startFindBoardLoopOnThread(void); - void _rebootOnThread(void); - void _flashOnThread(void); - void _cancel(void); + void _rebootOnThread (void); + void _flashOnThread (void); + void _cancel (void); private slots: - void _foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString name) { emit foundBoard(firstAttempt, portInfo, type, name); } - void _noBoardFound(void) { emit noBoardFound(); } - void _boardGone(void) { emit boardGone(); } - void _foundBootloader(int bootloaderVersion, int boardID, int flashSize) { emit foundBootloader(bootloaderVersion, boardID, flashSize); } - void _bootloaderSyncFailed(void) { emit bootloaderSyncFailed(); } - void _error(const QString& errorString) { emit error(errorString); } - void _status(const QString& statusText) { emit status(statusText); } - void _eraseStarted(void) { emit eraseStarted(); } - void _eraseComplete(void) { emit eraseComplete(); } - void _flashComplete(void) { emit flashComplete(); } + void _foundBoard (bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString name) { emit foundBoard(firstAttempt, portInfo, type, name); } + void _noBoardFound (void) { emit noBoardFound(); } + void _boardGone (void) { emit boardGone(); } + void _foundBoardInfo (int bootloaderVersion, int boardID, int flashSize) { emit foundBoardInfo(bootloaderVersion, boardID, flashSize); } + void _error (const QString& errorString) { emit error(errorString); } + void _status (const QString& statusText) { emit status(statusText); } + void _eraseStarted (void) { emit eraseStarted(); } + void _eraseComplete (void) { emit eraseComplete(); } + void _flashComplete (void) { emit flashComplete(); } private: void _updateProgress(int curr, int total) { emit updateProgress(curr, total); } - PX4FirmwareUpgradeThreadWorker* _worker; - QThread* _workerThread; ///< Thread which PX4FirmwareUpgradeThreadWorker runs on + PX4FirmwareUpgradeThreadWorker* _worker = nullptr; + QThread* _workerThread = nullptr; ///< Thread which PX4FirmwareUpgradeThreadWorker runs on const FirmwareImage* _image; }; -- GitLab