PX4FirmwareUpgradeThread.cc 13.4 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10 11

/// @file
Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
12
///     @brief PX4 Firmware Upgrade operations which occur on a separate thread.
13 14 15
///     @author Don Gagne <don@thegagnes.com>

#include "PX4FirmwareUpgradeThread.h"
16 17 18
#include "Bootloader.h"
#include "QGCLoggingCategory.h"
#include "QGC.h"
19 20 21

#include <QTimer>
#include <QDebug>
Don Gagne's avatar
Don Gagne committed
22
#include <QSerialPort>
23

24 25
PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(PX4FirmwareUpgradeThreadController* controller) :
    _controller(controller),
26 27 28
    _bootloader(nullptr),
    _bootloaderPort(nullptr),
    _timerRetry(nullptr),
29 30
    _foundBoard(false),
    _findBoardFirstAttempt(true)
31
{
32
    Q_ASSERT(_controller);
33
    
34 35 36 37 38
    connect(_controller, &PX4FirmwareUpgradeThreadController::_initThreadWorker,            this, &PX4FirmwareUpgradeThreadWorker::_init);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_startFindBoardLoopOnThread,  this, &PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_flashOnThread,               this, &PX4FirmwareUpgradeThreadWorker::_flash);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_rebootOnThread,              this, &PX4FirmwareUpgradeThreadWorker::_reboot);
    connect(_controller, &PX4FirmwareUpgradeThreadController::_cancel,                      this, &PX4FirmwareUpgradeThreadWorker::_cancel);
39 40 41 42 43 44 45 46 47 48 49 50
}

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.
51
void PX4FirmwareUpgradeThreadWorker::_init(void)
52 53 54
{
    // We create the timers here so that they are on the right thread
    
55
    Q_ASSERT(_timerRetry == nullptr);
56 57 58 59
    _timerRetry = new QTimer(this);
    Q_CHECK_PTR(_timerRetry);
    _timerRetry->setSingleShot(true);
    _timerRetry->setInterval(_retryTimeout);
60
    connect(_timerRetry, &QTimer::timeout, this, &PX4FirmwareUpgradeThreadWorker::_findBoardOnce);
61
    
62
    Q_ASSERT(_bootloader == nullptr);
63 64
    _bootloader = new Bootloader(this);
    connect(_bootloader, &Bootloader::updateProgress, this, &PX4FirmwareUpgradeThreadWorker::_updateProgress);
65 66
}

67
void PX4FirmwareUpgradeThreadWorker::_cancel(void)
68
{
69 70 71
    if (_bootloaderPort) {
        _bootloaderPort->close();
        _bootloaderPort->deleteLater();
72
        _bootloaderPort = nullptr;
73 74 75 76 77 78
    }
}

void PX4FirmwareUpgradeThreadWorker::_startFindBoardLoop(void)
{
    _foundBoard = false;
Don Gagne's avatar
Don Gagne committed
79
    _findBoardFirstAttempt = true;
80 81 82 83 84
    _findBoardOnce();
}

void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void)
{
Don Gagne's avatar
Don Gagne committed
85
    qCDebug(FirmwareUpgradeVerboseLog) << "_findBoardOnce";
86
    
Don Gagne's avatar
Don Gagne committed
87 88
    QGCSerialPortInfo               portInfo;
    QGCSerialPortInfo::BoardType_t  boardType;
89
    QString                         boardName;
90
    
91
    if (_findBoardFromPorts(portInfo, boardType, boardName)) {
92 93 94
        if (!_foundBoard) {
            _foundBoard = true;
            _foundBoardPortInfo = portInfo;
95
            emit foundBoard(_findBoardFirstAttempt, portInfo, boardType, boardName);
96
            if (!_findBoardFirstAttempt) {
97
                if (boardType == QGCSerialPortInfo::BoardTypeSiKRadio) {
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
                    _3drRadioForceBootloader(portInfo);
                    return;
                } else {
                    _findBootloader(portInfo, false /* radio mode */, true /* errorOnNotFound */);
                    return;
                }
            }
        }
    } else {
        if (_foundBoard) {
            _foundBoard = false;
            qCDebug(FirmwareUpgradeLog) << "Board gone";
            emit boardGone();
        } else if (_findBoardFirstAttempt) {
            emit noBoardFound();
113 114 115
        }
    }
    
Don Gagne's avatar
Don Gagne committed
116
    _findBoardFirstAttempt = false;
117 118 119
    _timerRetry->start();
}

120
bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType, QString& boardName)
121
{
122
    for (const QGCSerialPortInfo& info: QGCSerialPortInfo::availablePorts()) {
123 124
        info.getBoardInfo(boardType, boardName);

Don Gagne's avatar
Don Gagne committed
125
        qCDebug(FirmwareUpgradeVerboseLog) << "Serial Port --------------";
126 127
        qCDebug(FirmwareUpgradeVerboseLog) << "\tboard type" << boardType;
        qCDebug(FirmwareUpgradeVerboseLog) << "\tboard name" << boardName;
128
        qCDebug(FirmwareUpgradeVerboseLog) << "\tmanufacturer:" << info.manufacturer();
Don Gagne's avatar
Don Gagne committed
129 130 131 132 133
        qCDebug(FirmwareUpgradeVerboseLog) << "\tport name:" << info.portName();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tdescription:" << info.description();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tsystem location:" << info.systemLocation();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tvendor ID:" << info.vendorIdentifier();
        qCDebug(FirmwareUpgradeVerboseLog) << "\tproduct ID:" << info.productIdentifier();
134
        
135
        if (info.canFlash()) {
136
            portInfo = info;
137 138 139 140 141 142 143
            return true;
        }
    }
    
    return false;
}

Don Gagne's avatar
Don Gagne committed
144
void PX4FirmwareUpgradeThreadWorker::_3drRadioForceBootloader(const QGCSerialPortInfo& portInfo)
145
{
146 147 148 149 150 151 152 153 154
    // 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);
Don Gagne's avatar
Don Gagne committed
155
    
156 157
    port.setBaudRate(QSerialPort::Baud57600);
    
158
    emit status(tr("Putting radio into command mode"));
159 160
    
    // Wait a little while for the USB port to initialize. 3DR Radio boot is really slow.
Don Gagne's avatar
Don Gagne committed
161 162
    QGC::SLEEP::msleep(2000);
    port.open(QIODevice::ReadWrite);
163 164
    
    if (!port.isOpen()) {
165
        emit error(tr("Unable to open port: %1 error: %2").arg(portInfo.systemLocation()).arg(port.errorString()));
166 167 168 169
        return;
    }

    // Put radio into command mode
Don Gagne's avatar
Don Gagne committed
170
    QGC::SLEEP::msleep(2000);
171 172
    port.write("+++", 3);
    if (!port.waitForReadyRead(1500)) {
173
        emit error(tr("Unable to put radio into command mode"));
174 175 176 177
        return;
    }
    QByteArray bytes = port.readAll();
    if (!bytes.contains("OK")) {
Don Gagne's avatar
Don Gagne committed
178
        qCDebug(FirmwareUpgradeLog) << bytes;
179
        emit error(tr("Unable to put radio into command mode"));
180 181 182
        return;
    }

183
    emit status(tr("Rebooting radio to bootloader"));
184 185 186
    
    port.write("AT&UPDATE\r\n");
    if (!port.waitForBytesWritten(1500)) {
187
        emit error(tr("Unable to reboot radio (bytes written)"));
188 189
        return;
    }
Don Gagne's avatar
Don Gagne committed
190
    if (!port.waitForReadyRead(1500)) {
191
        emit error(tr("Unable to reboot radio (ready read)"));
Don Gagne's avatar
Don Gagne committed
192 193
        return;
    }
194
    port.close();
Don Gagne's avatar
Don Gagne committed
195 196
    QGC::SLEEP::msleep(2000);

197 198 199
    // The bootloader should be waiting for us now
    
    _findBootloader(portInfo, true /* radio mode */, true /* errorOnNotFound */);
200 201
}

Don Gagne's avatar
Don Gagne committed
202
bool PX4FirmwareUpgradeThreadWorker::_findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound)
203
{
Don Gagne's avatar
Don Gagne committed
204
    qCDebug(FirmwareUpgradeLog) << "_findBootloader" << portInfo.systemLocation();
205
    
206 207 208 209
    uint32_t bootloaderVersion = 0;
    uint32_t boardID;
    uint32_t flashSize = 0;
    
210
    _bootloaderPort = new QSerialPort();
Don Gagne's avatar
Don Gagne committed
211
    if (radioMode) {
212
        _bootloaderPort->setBaudRate(QSerialPort::Baud115200);
Don Gagne's avatar
Don Gagne committed
213 214
    }

215
    // Wait a little while for the USB port to initialize.
216
    bool openFailed = true;
217 218
    for (int i=0; i<10; i++) {
        if (_bootloader->open(_bootloaderPort, portInfo.systemLocation())) {
219
            openFailed = false;
220 221 222 223 224 225
            break;
        } else {
            QGC::SLEEP::msleep(100);
        }
    }
    
Don Gagne's avatar
Don Gagne committed
226 227 228 229
    if (radioMode) {
        QGC::SLEEP::msleep(2000);
    }

230 231 232 233 234 235
    if (openFailed) {
        qCDebug(FirmwareUpgradeLog) << "Bootloader open port failed:" << _bootloader->errorString();
        if (errorOnNotFound) {
            emit error(_bootloader->errorString());
        }
        _bootloaderPort->deleteLater();
236
        _bootloaderPort = nullptr;
237 238 239
        return false;
    }

240 241 242 243 244 245 246 247 248 249 250 251
    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;
252 253 254
        }
    }
    
Don Gagne's avatar
Don Gagne committed
255 256
    _bootloaderPort->close();
    _bootloaderPort->deleteLater();
257
    _bootloaderPort = nullptr;
258 259 260 261 262 263
    qCDebug(FirmwareUpgradeLog) << "Bootloader error:" << _bootloader->errorString();
    if (errorOnNotFound) {
        emit error(_bootloader->errorString());
    }
    
    return false;
264 265
}

266
void PX4FirmwareUpgradeThreadWorker::_reboot(void)
267
{
268 269
    if (_bootloaderPort) {
        if (_bootloaderPort->isOpen()) {
270
            _bootloader->reboot(_bootloaderPort);
271 272
        }
        _bootloaderPort->deleteLater();
273
        _bootloaderPort = nullptr;
274
    }
275 276
}

277
void PX4FirmwareUpgradeThreadWorker::_flash(void)
278
{
279 280 281
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_flash";
    
    if (_erase()) {
282
        emit status(tr("Programming new version..."));
283 284 285 286 287 288
        
        if (_bootloader->program(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Program complete";
            emit status("Program complete");
        } else {
            _bootloaderPort->deleteLater();
289
            _bootloaderPort = nullptr;
290 291
            qCDebug(FirmwareUpgradeLog) << "Program failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
Don Gagne's avatar
Don Gagne committed
292
            return;
293 294
        }
        
295
        emit status(tr("Verifying program..."));
296 297 298
        
        if (_bootloader->verify(_bootloaderPort, _controller->image())) {
            qCDebug(FirmwareUpgradeLog) << "Verify complete";
299
            emit status(tr("Verify complete"));
300 301 302
        } else {
            qCDebug(FirmwareUpgradeLog) << "Verify failed:" << _bootloader->errorString();
            emit error(_bootloader->errorString());
Don Gagne's avatar
Don Gagne committed
303
            return;
304
        }
305
    }
306 307 308 309
    
    emit _reboot();
    
    emit flashComplete();
310 311
}

312
bool PX4FirmwareUpgradeThreadWorker::_erase(void)
313
{
314 315 316
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadWorker::_erase";
    
    emit eraseStarted();
317
    emit status(tr("Erasing previous program..."));
318 319 320
    
    if (_bootloader->erase(_bootloaderPort)) {
        qCDebug(FirmwareUpgradeLog) << "Erase complete";
321
        emit status(tr("Erase complete"));
322 323
        emit eraseComplete();
        return true;
324
    } else {
325 326 327
        qCDebug(FirmwareUpgradeLog) << "Erase failed:" << _bootloader->errorString();
        emit error(_bootloader->errorString());
        return false;
328 329 330 331 332 333
    }
}

PX4FirmwareUpgradeThreadController::PX4FirmwareUpgradeThreadController(QObject* parent) :
    QObject(parent)
{
334
    _worker = new PX4FirmwareUpgradeThreadWorker(this);
335 336 337 338 339 340
    Q_CHECK_PTR(_worker);
    
    _workerThread = new QThread(this);
    Q_CHECK_PTR(_workerThread);
    _worker->moveToThread(_workerThread);
    
341 342 343 344 345
    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);
346
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::bootloaderSyncFailed, this, &PX4FirmwareUpgradeThreadController::_bootloaderSyncFailed);
347 348 349 350 351
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::error,                this, &PX4FirmwareUpgradeThreadController::_error);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::status,               this, &PX4FirmwareUpgradeThreadController::_status);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseStarted,         this, &PX4FirmwareUpgradeThreadController::_eraseStarted);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::eraseComplete,        this, &PX4FirmwareUpgradeThreadController::_eraseComplete);
    connect(_worker, &PX4FirmwareUpgradeThreadWorker::flashComplete,        this, &PX4FirmwareUpgradeThreadController::_flashComplete);
352 353 354 355 356 357 358 359 360 361

    _workerThread->start();
    
    emit _initThreadWorker();
}

PX4FirmwareUpgradeThreadController::~PX4FirmwareUpgradeThreadController()
{
    _workerThread->quit();
    _workerThread->wait();
362 363
    
    delete _workerThread;
364 365
}

366
void PX4FirmwareUpgradeThreadController::startFindBoardLoop(void)
367
{
368 369
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::findBoard";
    emit _startFindBoardLoopOnThread();
370 371
}

372
void PX4FirmwareUpgradeThreadController::cancel(void)
373
{
374 375
    qCDebug(FirmwareUpgradeLog) << "PX4FirmwareUpgradeThreadController::cancel";
    emit _cancel();
376 377
}

378
void PX4FirmwareUpgradeThreadController::flash(const FirmwareImage* image)
379
{
380 381
    _image = image;
    emit _flashOnThread();
382
}