6#include <QtCore/QElapsedTimer>
8#include <QtCore/QThread>
16 , _sikRadio (sikRadio)
23 qCDebug(FirmwareUpgradeLog) <<
"open:" << portName;
32 if (!_port.
open(QIODevice::ReadWrite)) {
33 _errorString = tr(
"Open failed on port %1: %2").arg(portName, _port.errorString());
39 QThread::msleep(1000);
44QString Bootloader::_getNextLine(
int timeoutMsecs)
47 QElapsedTimer timeout;
51 while (timeout.elapsed() < timeoutMsecs) {
54 if (_port.read(&oneChar, 1) > 0) {
55 if (oneChar ==
'\r') {
58 }
else if (oneChar ==
'\n' && foundCR) {
73 if (_inBootloaderMode) {
74 qCDebug(FirmwareUpgradeLog) <<
"Radio in bootloader mode already";
75 if (!_get3DRRadioBoardId(_boardID)) {
79 qCDebug(FirmwareUpgradeLog) <<
"Radio in normal mode";
85 _errorString = tr(
"Unable to put radio into command mode +++");
88 QByteArray bytes = _port.readAll();
89 if (!bytes.contains(
"OK")) {
90 _errorString = tr(
"Radio did not respond to command mode");
96 QString echo = _getNextLine(2000);
97 if (echo.isEmpty() || echo !=
"ATI2") {
98 _errorString = tr(
"Radio did not respond to ATI2 command");
101 QString boardIdStr = _getNextLine(2000);
103 _boardID = boardIdStr.toInt(&ok);
104 if (boardIdStr.isEmpty() || !ok) {
105 _errorString = tr(
"Radio did not return board id");
109 bootloaderVersion = 0;
118 if (!_protoGetDevice(INFO_BL_REV, _bootloaderVersion)) {
121 if (_bootloaderVersion < BL_REV_MIN || _bootloaderVersion > BL_REV_MAX) {
122 _errorString = tr(
"Found unsupported bootloader version: %1").arg(_bootloaderVersion);
125 if (!_protoGetDevice(INFO_BOARD_ID, _boardID)) {
128 if (!_protoGetDevice(INFO_FLASH_SIZE, _boardFlashSize)) {
129 qWarning() << _errorString;
136 if (_boardID ==
boardIDPX4FMUV2 && _bootloaderVersion >= _bootloaderVersionV2CorrectFlash && _boardFlashSize > _flashSizeSmall) {
140 bootloaderVersion = _bootloaderVersion;
142 flashSize = _boardFlashSize;
148 qCDebug(FirmwareUpgradeLog) <<
"getBoardInfo failed:" << _errorString;
149 _errorString.prepend(tr(
"Get Board Info: "));
155 if (_sikRadio && !_inBootloaderMode) {
156 _write(
"AT&UPDATE\r\n");
158 _errorString = tr(
"Unable to reboot radio (ready read)");
172 uint32_t timeout = _eraseTimeout;
175 if(_boardFlashSize > 2000 * 1024) {
177 timeout += (_boardFlashSize / 1e6) * 4000;
181 if (!_sendCommand(PROTO_CHIP_ERASE, timeout)) {
182 _errorString = tr(
"Erase failed: %1").arg(_errorString);
192 return _binProgram(image);
194 return _ihxProgram(image);
201 if (_sikRadio && !_inBootloaderMode) {
202 qCDebug(FirmwareUpgradeLog) <<
"reboot ATZ";
204 success = _write(
"ATZ\r\n");
206 qCDebug(FirmwareUpgradeLog) <<
"reboot";
207 success = _write(PROTO_BOOT) && _write(PROTO_EOC);
211 QThread::msleep(1000);
216bool Bootloader::_write(
const char* data)
218 return _write((uint8_t*)data, qstrlen(data));
221bool Bootloader::_write(
const uint8_t* data, qint64 maxSize)
223 qint64 bytesWritten = _port.write((
const char*)data, maxSize);
224 if (bytesWritten == -1) {
225 _errorString = tr(
"Write failed: %1").arg(_port.errorString());
226 qWarning() << _errorString;
229 if (bytesWritten != maxSize) {
230 _errorString = tr(
"Incorrect number of bytes returned for write: actual(%1) expected(%2)").arg(bytesWritten).arg(maxSize);
231 qWarning() << _errorString;
238bool Bootloader::_write(
const uint8_t
byte)
240 uint8_t buf[1] = {
byte };
241 return _write(buf, 1);
244bool Bootloader::_read(uint8_t* data, qint64 cBytesExpected,
int readTimeout)
246 QElapsedTimer timeout;
250 if (timeout.elapsed() > readTimeout) {
251 _errorString = tr(
"Timeout waiting for bytes to be available");
258 bytesRead = _port.read((
char *)data, cBytesExpected);
260 if (bytesRead != cBytesExpected) {
261 _errorString = tr(
"Read failed: error: %1").arg(_port.errorString());
270bool Bootloader::_getCommandResponse(
int responseTimeout)
274 if (!_read(response, 2, responseTimeout)) {
275 _errorString.prepend(tr(
"Get Command Response: "));
280 if (response[0] != PROTO_INSYNC) {
281 _errorString = tr(
"Invalid sync response: 0x%1 0x%2").arg(response[0], 2, 16, QLatin1Char(
'0')).arg(response[1], 2, 16, QLatin1Char(
'0'));
283 }
else if (response[0] == PROTO_INSYNC && response[1] == PROTO_BAD_SILICON_REV) {
284 _errorString = tr(
"This board is using a microcontroller with faulty silicon and an incorrect configuration and should be put out of service.");
286 }
else if (response[1] != PROTO_OK) {
287 QString responseCode = tr(
"Unknown response code");
288 if (response[1] == PROTO_FAILED) {
289 responseCode =
"PROTO_FAILED";
290 }
else if (response[1] == PROTO_INVALID) {
291 responseCode =
"PROTO_INVALID";
293 _errorString = tr(
"Command failed: 0x%1 (%2)").arg(response[1], 2, 16, QLatin1Char(
'0')).arg(responseCode);
303bool Bootloader::_protoGetDevice(uint8_t param, uint32_t& value)
305 uint8_t buf[3] = { PROTO_GET_DEVICE, param, PROTO_EOC };
307 if (!_write(buf,
sizeof(buf))) {
310 if (!_read((uint8_t*)&value,
sizeof(value))) {
313 if (!_getCommandResponse()) {
320 _errorString.prepend(tr(
"Get Device: "));
327bool Bootloader::_sendCommand(
const uint8_t cmd,
int responseTimeout)
329 uint8_t buf[2] = { cmd, PROTO_EOC };
331 if (!_write(buf, 2)) {
336 if (!_getCommandResponse(responseTimeout)) {
343 _errorString.prepend(tr(
"Send Command: "));
350 if (!firmwareFile.open(QIODevice::ReadOnly)) {
351 _errorString = tr(
"Unable to open firmware file %1: %2").arg(image->
binFilename(), firmwareFile.errorString());
354 uint32_t imageSize = (uint32_t)firmwareFile.size();
356 uint8_t imageBuf[PROG_MULTI_MAX];
357 uint32_t bytesSent = 0;
360 Q_ASSERT(PROG_MULTI_MAX <= 0x8F);
362 while (bytesSent < imageSize) {
363 int bytesToSend = imageSize - bytesSent;
364 if (bytesToSend > (
int)
sizeof(imageBuf)) {
365 bytesToSend = (int)
sizeof(imageBuf);
368 Q_ASSERT((bytesToSend % 4) == 0);
370 int bytesRead = firmwareFile.read((
char *)imageBuf, bytesToSend);
371 if (bytesRead == -1 || bytesRead != bytesToSend) {
372 _errorString = tr(
"Firmware file read failed: %1").arg(firmwareFile.errorString());
376 Q_ASSERT(bytesToSend <= 0x8F);
379 if (_write(PROTO_PROG_MULTI) &&
380 _write((uint8_t)bytesToSend) &&
381 _write(imageBuf, bytesToSend) &&
383 if (_getCommandResponse()) {
388 _errorString = tr(
"Flash failed: %1 at address 0x%2").arg(_errorString).arg(bytesSent, 8, 16, QLatin1Char(
'0'));
392 bytesSent += bytesToSend;
395 _imageCRC =
QGC::crc32((uint8_t *)imageBuf, bytesToSend, _imageCRC);
399 firmwareFile.close();
402 while (bytesSent < _boardFlashSize) {
403 const uint8_t fill = 0xFF;
414 uint32_t bytesSent = 0;
416 for (uint16_t index=0; index<image->
ihxBlockCount(); index++) {
418 uint16_t flashAddress;
421 if (!image->
ihxGetBlock(index, flashAddress, bytes)) {
422 _errorString = tr(
"Unable to retrieve block from ihx: index %1").arg(index);
426 qCDebug(FirmwareUpgradeVerboseLog) << QString(
"Bootloader::_ihxProgram - address:0x%1 size:%2 block:%3").arg(flashAddress, 8, 16, QLatin1Char(
'0')).arg(bytes.length()).arg(index);
431 if (_write(PROTO_LOAD_ADDRESS) &&
432 _write(flashAddress & 0xFF) &&
433 _write((flashAddress >> 8) & 0xFF) &&
436 if (_getCommandResponse()) {
442 _errorString = tr(
"Unable to set flash start address: 0x%2").arg(flashAddress, 8, 16, QLatin1Char(
'0'));
449 uint16_t bytesLeftToWrite = bytes.length();
451 while (bytesLeftToWrite > 0) {
452 uint8_t bytesToWrite;
454 if (bytesLeftToWrite > PROG_MULTI_MAX) {
455 bytesToWrite = PROG_MULTI_MAX;
457 bytesToWrite = bytesLeftToWrite;
461 if (_write(PROTO_PROG_MULTI) &&
462 _write(bytesToWrite) &&
463 _write(&((uint8_t *)bytes.data())[bytesIndex], bytesToWrite) &&
466 if (_getCommandResponse()) {
471 _errorString = tr(
"Flash failed: %1 at address 0x%2").arg(_errorString).arg(flashAddress, 8, 16, QLatin1Char(
'0'));
475 bytesIndex += bytesToWrite;
476 bytesLeftToWrite -= bytesToWrite;
477 bytesSent += bytesToWrite;
491 ret = _verifyBytes(image);
505 return _binVerifyBytes(image);
507 return _ihxVerifyBytes(image);
516 if (!firmwareFile.open(QIODevice::ReadOnly)) {
517 _errorString = tr(
"Unable to open firmware file %1: %2").arg(image->
binFilename(), firmwareFile.errorString());
520 uint32_t imageSize = (uint32_t)firmwareFile.size();
522 if (!_sendCommand(PROTO_CHIP_VERIFY)) {
526 uint8_t fileBuf[READ_MULTI_MAX];
527 uint8_t readBuf[READ_MULTI_MAX];
528 uint32_t bytesVerified = 0;
530 Q_ASSERT(PROG_MULTI_MAX <= 0x8F);
532 while (bytesVerified < imageSize) {
533 int bytesToRead = imageSize - bytesVerified;
534 if (bytesToRead > (
int)
sizeof(readBuf)) {
535 bytesToRead = (int)
sizeof(readBuf);
538 Q_ASSERT((bytesToRead % 4) == 0);
540 int bytesRead = firmwareFile.read((
char *)fileBuf, bytesToRead);
541 if (bytesRead == -1 || bytesRead != bytesToRead) {
542 _errorString = tr(
"Firmware file read failed: %1").arg(firmwareFile.errorString());
546 Q_ASSERT(bytesToRead <= 0x8F);
549 if (_write(PROTO_READ_MULTI) &&
550 _write((uint8_t)bytesToRead) &&
553 if (_read(readBuf, bytesToRead)) {
554 if (_getCommandResponse()) {
560 _errorString = tr(
"Read failed: %1 at address: 0x%2").arg(_errorString).arg(bytesVerified, 8, 16, QLatin1Char(
'0'));
564 for (
int i=0; i<bytesToRead; i++) {
565 if (fileBuf[i] != readBuf[i]) {
566 _errorString = tr(
"Compare failed: expected(0x%1) actual(0x%2) at address: 0x%3").arg(fileBuf[i], 2, 16, QLatin1Char(
'0')).arg(readBuf[i], 2, 16, QLatin1Char(
'0')).arg(bytesVerified + i, 8, 16, QLatin1Char(
'0'));
571 bytesVerified += bytesToRead;
576 firmwareFile.close();
586 uint32_t bytesVerified = 0;
588 for (uint16_t index=0; index<image->
ihxBlockCount(); index++) {
590 uint16_t readAddress;
591 QByteArray imageBytes;
593 if (!image->
ihxGetBlock(index, readAddress, imageBytes)) {
594 _errorString = tr(
"Unable to retrieve block from ihx: index %1").arg(index);
598 qCDebug(FirmwareUpgradeLog) << QString(
"Bootloader::_ihxVerifyBytes - address:0x%1 size:%2 block:%3").arg(readAddress, 8, 16, QLatin1Char(
'0')).arg(imageBytes.length()).arg(index);
603 if (_write(PROTO_LOAD_ADDRESS) &&
604 _write(readAddress & 0xFF) &&
605 _write((readAddress >> 8) & 0xFF) &&
608 if (_getCommandResponse()) {
614 _errorString = tr(
"Unable to set read start address: 0x%2").arg(readAddress, 8, 16, QLatin1Char(
'0'));
621 uint16_t bytesLeftToRead = imageBytes.length();
623 while (bytesLeftToRead > 0) {
625 uint8_t readBuf[READ_MULTI_MAX];
627 if (bytesLeftToRead > READ_MULTI_MAX) {
628 bytesToRead = READ_MULTI_MAX;
630 bytesToRead = bytesLeftToRead;
634 if (_write(PROTO_READ_MULTI) &&
635 _write(bytesToRead) &&
638 if (_read(readBuf, bytesToRead)) {
639 if (_getCommandResponse()) {
645 _errorString = tr(
"Read failed: %1 at address: 0x%2").arg(_errorString).arg(readAddress, 8, 16, QLatin1Char(
'0'));
651 for (
int i=0; i<bytesToRead; i++) {
652 if ((uint8_t)imageBytes[bytesIndex + i] != readBuf[i]) {
653 _errorString = tr(
"Compare failed: expected(0x%1) actual(0x%2) at address: 0x%3")
654 .arg(
static_cast<qulonglong
>(
static_cast<quint8
>(imageBytes[bytesIndex + i])), 2, 16, QLatin1Char(
'0'))
655 .arg(
static_cast<qulonglong
>(
static_cast<quint8
>(readBuf[i])), 2, 16, QLatin1Char(
'0'))
656 .arg(
static_cast<qulonglong
>(readAddress + i), 8, 16, QLatin1Char(
'0'));
661 bytesVerified += bytesToRead;
662 bytesIndex += bytesToRead;
663 bytesLeftToRead -= bytesToRead;
673bool Bootloader::_verifyCRC(
void)
675 uint8_t buf[2] = { PROTO_GET_CRC, PROTO_EOC };
677 quint32 flashCRC = 0;
680 if (_write(buf, 2)) {
682 if (_read((uint8_t*)&flashCRC,
sizeof(flashCRC), _verifyTimeout)) {
683 if (_getCommandResponse()) {
692 if (_imageCRC != flashCRC) {
693 _errorString = tr(
"CRC mismatch: board(0x%1) file(0x%2)").arg(flashCRC, 4, 16, QLatin1Char(
'0')).arg(_imageCRC, 4, 16, QLatin1Char(
'0'));
700bool Bootloader::_syncWorker(
void)
703 if (_sendCommand(PROTO_GET_SYNC)) {
704 _inBootloaderMode =
true;
707 _errorString.prepend(
"Sync: ");
712bool Bootloader::_sync(
void)
716 bool success =
false;
717 for (
int i=0; i<3; i++) {
718 success = _syncWorker();
727bool Bootloader::_get3DRRadioBoardId(uint32_t& boardID)
729 uint8_t buf[2] = { PROTO_GET_DEVICE, PROTO_EOC };
731 if (!_write(buf,
sizeof(buf))) {
736 if (!_read((uint8_t*)buf, 2)) {
739 if (!_getCommandResponse()) {
745 _bootloaderVersion = 0;
751 _errorString.prepend(tr(
"Get Board Id: "));
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Bootloader Utility routines. Works with PX4 and 3DR Radio bootloaders.
bool program(const FirmwareImage *image)
void updateProgress(int curr, int total)
Signals progress indicator for long running bootloader utility routines.
static const int boardIDPX4FMUV2
PX4 V2 board, as from USB PID.
bool initFlashSequence(void)
static const int boardIDPX4FMUV3
bool open(const QString portName)
bool verify(const FirmwareImage *image)
bool getBoardInfo(uint32_t &bootloaderVersion, uint32_t &boardID, uint32_t &flashSize)
Support for Intel Hex firmware file.
bool imageIsBinFormat(void) const
QString binFilename(void) const
bool ihxGetBlock(uint16_t index, uint16_t &address, QByteArray &bytes) const
uint32_t imageSize(void) const
Returns the number of bytes in the image.
uint16_t ihxBlockCount(void) const
bool open(OpenMode mode) override
bool setStopBits(StopBits stopBits)
void setPortName(const QString &name)
bool waitForReadyRead(int msecs=30000) override
bool setBaudRate(qint32 baudRate, Directions directions=AllDirections)
bool setDataBits(DataBits dataBits)
bool setParity(Parity parity)
qint64 bytesAvailable() const override
bool setFlowControl(FlowControl flowControl)
Error
Error codes for decompression operations.
quint32 crc32(const quint8 *src, unsigned len, unsigned state)