444 lines
18 KiB
C++
444 lines
18 KiB
C++
|
#include "dc_download.h"
|
||
|
#include "hwapi.h"
|
||
|
|
||
|
#include <QDebug>
|
||
|
#include <QDateTime>
|
||
|
#include <QThread>
|
||
|
|
||
|
|
||
|
DcDownload::DcDownload(hwinf *hw)
|
||
|
: m_hw(hw)
|
||
|
, m_fileToDownload(m_hw->dcDownloadFileName()) {
|
||
|
// connect(this, &QThread::finished,
|
||
|
// dynamic_cast<QObject const *>(m_hw), &QThread::deleteLater);
|
||
|
}
|
||
|
|
||
|
DcDownload::~DcDownload() {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// USING THE DC BOOTLOADER
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
1 : bl_reboot() // send to application, want DC2 to reset (in order to
|
||
|
// start the bootloader)
|
||
|
//
|
||
|
// NOTE: this function is NOT reliable !!! Sometimes it
|
||
|
// simply does not work, in which case bl_startBL,
|
||
|
// bl_checkBL and bl_isUp do not work as well.
|
||
|
// Alas, there is no feedback if bl_reboot worked!
|
||
|
//
|
||
|
// NOTE: this function can be called only once per
|
||
|
// minute, because once called again, the controller
|
||
|
// performs some self-checks consuming some time.
|
||
|
//
|
||
|
// NOTE: after a successful bl_reboot(), the device is
|
||
|
// waiting about 4 seconds in the bootloader. To stay in
|
||
|
// the bootloader, we have to send the command
|
||
|
// bl_startBL(), which is kind of a misnomer, as it
|
||
|
// should be bl_doNotLeaveBL().
|
||
|
//
|
||
|
2 : bl_startBL(): // send within 4s after DC power-on, otherwise
|
||
|
// bootloader is left.
|
||
|
//
|
||
|
// NOTE: a running bootloader is a MUST for the download
|
||
|
// process of a device controller firmware as it does
|
||
|
// the actual writing of the memory (the bl_reboot()
|
||
|
// from above erases the available memory).
|
||
|
//
|
||
|
3 : bl_check(): // send command to verify if bl is up
|
||
|
//
|
||
|
// NOTE: this command is kind of a request that we want
|
||
|
// to check if the bootloader is up. The device
|
||
|
// (actually the bootloader) responds with its version.
|
||
|
//
|
||
|
4 : bl_isUp(): // returns true if bl is up and running
|
||
|
//
|
||
|
// NOTE: we know what the bootloader version actually is
|
||
|
// as the bootloader does not change. By comparing the
|
||
|
// string received in the previous step with this known
|
||
|
// version string we know if the bootloader is up.
|
||
|
//
|
||
|
// NOTE FOR ALL PREVIOUS STEPS: execute them in their
|
||
|
// own slots each to be sure to receive any possible
|
||
|
// responds from the device.
|
||
|
//
|
||
|
5 : bl_sendAddress(blockNumber)
|
||
|
// send start address, nr of 64-byte block, start with 0
|
||
|
// will be sent only for following block-numbers:
|
||
|
// 0, 1024, 2048, 3072 and 4096, so basically every
|
||
|
// 64kByte.
|
||
|
// for other addresses nothing happens
|
||
|
|
||
|
6 : bl_wasSendingAddOK()
|
||
|
// return val: 0: no response by now
|
||
|
// 1: error
|
||
|
// 10: OK
|
||
|
|
||
|
7 : bl_sendDataBlock()
|
||
|
// send 64 byte from bin file
|
||
|
|
||
|
8 : bl_sendLastBlock()
|
||
|
// send this command after all data are transferred
|
||
|
|
||
|
9 : bl_wasSendingDataOK()
|
||
|
// return val: 0: no response by now
|
||
|
// 1: error
|
||
|
// 10: OK
|
||
|
|
||
|
10 : bl_stopBL() // leave bl and start (the new) application
|
||
|
//
|
||
|
// NOTE: this function MUST work under all conditions.
|
||
|
// Alas, there is no direct result for this command, so
|
||
|
// the only way of knowing it was successful is to ask
|
||
|
// the device if the bootloader is still running.
|
||
|
// There is no problem to repeat this command until the
|
||
|
// bootloader is really not running anymore.
|
||
|
*/
|
||
|
void DcDownload::doDownload() {
|
||
|
#if 0
|
||
|
void DcDownload::run() {
|
||
|
// download thread running in ca-master sends the dc-file down to firmware
|
||
|
// TODO: send the json files as well
|
||
|
|
||
|
m_hw->dcDownloadRequestAck();
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): DOWNLOAD THREAD STARTED:";
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName();
|
||
|
|
||
|
QDateTime const start = QDateTime::currentDateTime();
|
||
|
|
||
|
#if 1
|
||
|
QFile fn(m_hw->dcDownloadFileName());
|
||
|
|
||
|
if (!fn.exists()) {
|
||
|
// output via CONSOLE() etc
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName() << "DOES NOT EXIST";;
|
||
|
} else {
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): DC-CONTROLLER SW-VERSION BEFORE"
|
||
|
<< m_hw->dc_getSWversion();
|
||
|
|
||
|
// load binary device controller file into memory
|
||
|
QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName());
|
||
|
if (ba.size() > 0) {
|
||
|
uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
|
||
|
m_hw->dcDownloadSetTotalBlockNumber(totalBlocks);
|
||
|
|
||
|
// fill last block of data to be sent with 0xFF
|
||
|
ba = ba.leftJustified(totalBlocks*64, (char)(0xFF));
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size();
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks;
|
||
|
|
||
|
m_hw->dc_autoRequest(true); // turn auto-request setting on
|
||
|
|
||
|
m_hw->request_DC2_HWversion();
|
||
|
m_hw->request_DC2_SWversion();
|
||
|
QThread::sleep(1);
|
||
|
|
||
|
// m_hw->dc_autoRequest(false); // turn auto-request setting on
|
||
|
|
||
|
resetDeviceController();
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): RESET DEVICE-CONTROLLER";
|
||
|
|
||
|
if (startBootloader()) {
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): STARTED BOOT-LOADER";
|
||
|
|
||
|
m_hw->dc_autoRequest(false);// turn auto-request setting off for
|
||
|
// download of binary dc
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): TOTAL NUMBER OF FIRMWARE BLOCKS" << totalBlocks;
|
||
|
|
||
|
int currentBlock = 0; // download of binary dc
|
||
|
DownloadResult res = DownloadResult::OK;
|
||
|
|
||
|
while (res != DownloadResult::ERROR && currentBlock < totalBlocks) {
|
||
|
if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) {
|
||
|
if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) {
|
||
|
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): currentBlockNumber ..." << currentBlock;
|
||
|
|
||
|
currentBlock += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< QString("DcDownload::run(): last 64-byte block %1").arg(currentBlock);
|
||
|
|
||
|
int const rest = ba.size() % 64;
|
||
|
int const offset = ba.size() - rest;
|
||
|
char const *startAddress = ba.constData() + offset;
|
||
|
|
||
|
if (rest > 0) {
|
||
|
// SHOULD NEVER HAPPEN !!!
|
||
|
uint8_t local[66];
|
||
|
memset(local, 0xFF, sizeof(local));
|
||
|
memcpy(local, startAddress, rest);
|
||
|
qCritical() << "DcDownload::run(): ERROR SEND REMAINING" << rest << "BYTES";
|
||
|
m_hw->bl_sendDataBlock(64, local);
|
||
|
} else {
|
||
|
m_hw->bl_sendLastBlock();
|
||
|
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
|
||
|
|
||
|
qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock;
|
||
|
// QThread::msleep(250);
|
||
|
}
|
||
|
qCritical() << "DcDownload::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK());
|
||
|
|
||
|
stopBootloader(); // stop bootloader several times: if it
|
||
|
QThread::sleep(1); // is not stopped, then the PSA has to be
|
||
|
}
|
||
|
// restarted manually (!!!)
|
||
|
stopBootloader();
|
||
|
QThread::sleep(1);
|
||
|
}
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): STOPPED BOOT-LOADER";
|
||
|
|
||
|
stopBootloader(); // there is no harm in stopping the bootloader even
|
||
|
// if it was not started at all
|
||
|
|
||
|
m_hw->dc_autoRequest(true);
|
||
|
}
|
||
|
|
||
|
#else // test
|
||
|
// load binary device controller file into memory
|
||
|
QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName());
|
||
|
if (ba.size() > 0) {
|
||
|
uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
|
||
|
m_hw->dcDownloadSetTotalBlockNumber(totalBlocks);
|
||
|
|
||
|
// fill last block of data to be sent with 0xFF
|
||
|
ba = ba.leftJustified(totalBlocks*64, (char)(0xFF));
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size();
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks;
|
||
|
|
||
|
m_hw->dc_autoRequest(true); // turn auto-request setting on
|
||
|
|
||
|
m_hw->request_DC2_HWversion();
|
||
|
m_hw->request_DC2_SWversion();
|
||
|
QThread::sleep(1);
|
||
|
|
||
|
resetDeviceController();
|
||
|
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): RESET DEVICE-CONTROLLER";
|
||
|
QThread::sleep(1);
|
||
|
|
||
|
if (startBootloader()) {
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): STARTED BOOT-LOADER";
|
||
|
|
||
|
m_hw->dc_autoRequest(false);// turn auto-request setting off for
|
||
|
// download of binary dc
|
||
|
for (uint16_t currentBlock = 0; currentBlock <= totalBlocks; ++currentBlock) {
|
||
|
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
|
||
|
qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock;
|
||
|
QThread::msleep(250);
|
||
|
}
|
||
|
m_hw->dc_autoRequest(true); // turn auto-request setting on again
|
||
|
}
|
||
|
|
||
|
stopBootloader(); // there is no harm in stopping the bootloader even
|
||
|
// if it was not started at all
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "DcDownload::run(): STOPPED BOOT-LOADER";
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
m_hw->dcDownloadSetRunning(false);
|
||
|
m_hw->dcDownloadSetFinished(true);
|
||
|
|
||
|
QDateTime const end = QDateTime::currentDateTime();
|
||
|
quint64 secs = start.secsTo(end);
|
||
|
QString runtime;
|
||
|
if (secs % 60) {
|
||
|
runtime = QString("%1min %2s").arg(secs / 60).arg(secs % 60);
|
||
|
} else {
|
||
|
runtime = QString("%1min").arg((secs / 60) + 1);
|
||
|
}
|
||
|
|
||
|
qCritical() << end.time().toString(Qt::ISODateWithMs)
|
||
|
<< QString("DOWNLOAD THREAD FINISHED (RUNTIME %1)")
|
||
|
.arg(runtime);
|
||
|
|
||
|
// the object deletes itself ! This is the last line in run().
|
||
|
// Never touch the object after this statement
|
||
|
// m_hw->dcDcDownloadFinalize(this);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
DcDownload::DownloadResult DcDownload::sendStatus(int ret) const {
|
||
|
switch (ret) { // return values of dc are:
|
||
|
case 0: // 0: no answer by now
|
||
|
return DownloadResult::NOP; // 1: error
|
||
|
case 10: // 10: success
|
||
|
return DownloadResult::OK;
|
||
|
default:;
|
||
|
}
|
||
|
return DownloadResult::ERROR;
|
||
|
}
|
||
|
|
||
|
DcDownload::DownloadResult
|
||
|
DcDownload::sendNextAddress(int bNum) const {
|
||
|
// sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096
|
||
|
int noAnswerCount = 0;
|
||
|
int errorCount = 0;
|
||
|
if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) {
|
||
|
while (noAnswerCount <= 250) {
|
||
|
m_hw->bl_sendAddress(bNum);
|
||
|
QThread::msleep(100);
|
||
|
DownloadResult const res = sendStatus(m_hw->bl_wasSendingAddOK());
|
||
|
if (res != DownloadResult::NOP) {
|
||
|
if (res == DownloadResult::ERROR) {
|
||
|
if (++errorCount >= 10) {
|
||
|
qCritical() << "addr-block" << bNum << "...FAILED";
|
||
|
return res;
|
||
|
}
|
||
|
} else { // res == DownloadResult::OK
|
||
|
// qInfo() << "addr-block" << bNum << "...OK";
|
||
|
|
||
|
// TODO: hier ins shared-mem schreiben
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
} else {
|
||
|
noAnswerCount += 1; // no answer by now
|
||
|
}
|
||
|
}
|
||
|
// wait max. about 3 seconds
|
||
|
return DownloadResult::TIMEOUT;
|
||
|
}
|
||
|
// blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing
|
||
|
return DownloadResult::NOP;
|
||
|
}
|
||
|
|
||
|
DcDownload::DownloadResult
|
||
|
DcDownload::sendNextDataBlock(QByteArray const &binary, int bNum) const {
|
||
|
uint8_t local[66];
|
||
|
int const bAddr = bNum * 64;
|
||
|
int noAnswerCount = 0;
|
||
|
int errorCount = 0;
|
||
|
|
||
|
memcpy(local, binary.constData() + bAddr, 64);
|
||
|
local[64] = local[65] = 0x00;
|
||
|
|
||
|
while (noAnswerCount <= 250) {
|
||
|
m_hw->bl_sendDataBlock(64, local);
|
||
|
QThread::msleep(10);
|
||
|
DownloadResult const res = sendStatus(m_hw->bl_wasSendingDataOK());
|
||
|
if (res != DownloadResult::NOP) {
|
||
|
if (res == DownloadResult::ERROR) {
|
||
|
if (++errorCount >= 10) {
|
||
|
qCritical() << "data for block" << bNum << "...FAILED";
|
||
|
return res;
|
||
|
}
|
||
|
} else {
|
||
|
// qInfo() << "data for block" << bNum << "OK";
|
||
|
// TODO: hier ins shared mem schreiben
|
||
|
return res;
|
||
|
}
|
||
|
} else {
|
||
|
noAnswerCount += 1; // no answer by now
|
||
|
}
|
||
|
}
|
||
|
// wait max. about 3 seconds
|
||
|
return DownloadResult::TIMEOUT;
|
||
|
}
|
||
|
|
||
|
bool DcDownload::startBootloader() const {
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "starting bootloader...";
|
||
|
int nTry = 5;
|
||
|
while (--nTry >= 0) {
|
||
|
m_hw->bl_startBL();
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_startBL() ..." << nTry;
|
||
|
QThread::msleep(500);
|
||
|
m_hw->bl_checkBL();
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry;
|
||
|
QThread::msleep(500);
|
||
|
if (m_hw->bl_isUp()) {
|
||
|
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bootloader... isUP" << nTry;
|
||
|
return true;
|
||
|
} else {
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "bootloader not up (" << nTry << ")";
|
||
|
}
|
||
|
}
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "starting bootloader FAILED";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DcDownload::stopBootloader() const {
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "stopping bootloader...";
|
||
|
int nTry = 5;
|
||
|
while (--nTry >= 0) {
|
||
|
m_hw->bl_stopBL();
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "bl_stopBL() ...";
|
||
|
QThread::msleep(500);
|
||
|
m_hw->bl_checkBL();
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry;
|
||
|
if (!m_hw->bl_isUp()) {
|
||
|
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "stopping bootloader OK";
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "stopping bootloader FAILED";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DcDownload::resetDeviceController() const {
|
||
|
qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "resetting device controller...";
|
||
|
m_hw->bl_rebootDC();
|
||
|
// wait maximally 3 seconds, before starting bootloader
|
||
|
QThread::sleep(1);
|
||
|
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "resetting device controller...OK";
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QByteArray DcDownload::loadBinaryDCFile(QString filename) const {
|
||
|
qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs)
|
||
|
<< "loading dc binary" << filename << "...";
|
||
|
|
||
|
QFile file(filename); // closed in destructor call
|
||
|
if (!file.exists()) {
|
||
|
qCritical() << file.fileName() << "does not exist";
|
||
|
return QByteArray();
|
||
|
}
|
||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||
|
qCritical() << "cannot open file" << file.fileName();
|
||
|
return QByteArray();
|
||
|
}
|
||
|
qInfo() << "loading dc binary" << filename << "...OK";
|
||
|
return file.readAll();
|
||
|
}
|