From 47f09067ea9ab272dde34c01c17b19aa7f4255d1 Mon Sep 17 00:00:00 2001 From: Gerhard Hoffmann Date: Fri, 8 Dec 2023 19:00:43 +0100 Subject: [PATCH] Added functionality for actually downloading a real dc. --- include/download_thread.h | 12 ++ src/download_thread.cpp | 272 +++++++++++++++++++++++++++++++++++++- 2 files changed, 283 insertions(+), 1 deletion(-) diff --git a/include/download_thread.h b/include/download_thread.h index 828003e..941cd26 100644 --- a/include/download_thread.h +++ b/include/download_thread.h @@ -3,12 +3,15 @@ #include #include +#include class hwinf; class DownloadThread : public QThread { Q_OBJECT public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + DownloadThread(hwinf *hw); ~DownloadThread(); @@ -18,6 +21,15 @@ protected: void run() override; private: + DownloadResult sendStatus(int ret) const; + DownloadResult sendNextAddress(int bNum) const; + DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const; + bool startBootloader() const; + bool stopBootloader() const; + QByteArray loadBinaryDCFile(QString dcFileName) const; + bool resetDeviceController() const; + DownloadResult dcDownloadBinary(QByteArray const &b) const; + hwinf *m_hw; QString m_fileToDownload; }; diff --git a/src/download_thread.cpp b/src/download_thread.cpp index 9dc82a7..a05bf1a 100644 --- a/src/download_thread.cpp +++ b/src/download_thread.cpp @@ -16,8 +16,135 @@ DownloadThread::DownloadThread(hwinf *hw) DownloadThread::~DownloadThread() { } -// download thread running in ca-master sends the dc-file down to firmware +/* + /////////////////////////////////////////////////////////////////////////////// + // + // 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 DownloadThread::run() { +// download thread running in ca-master sends the dc-file down to firmware +#if 0 + Update::DownloadResult Update::dc_downloadBinary(QByteArray const &b) const { + int const nBlocks = (((b.size())%64)==0) ? (b.size()/64) : (b.size()/64)+1; + + // fill lst block of data to be sent with 0xFF + QByteArray ba = b.leftJustified(nBlocks*64, (char)(0xFF)); + + qInfo() << "total number of bytes to send to dc" << ba.size(); + qInfo() << "total number of blocks to send to dc" << nBlocks; + + int bNum = 0; + DownloadResult res = DownloadResult::OK; + fprintf(stderr, "\n64-byte block %04d ", bNum); + while (res != DownloadResult::ERROR && bNum < nBlocks) { + if ((res = sendNextAddress(bNum)) != DownloadResult::ERROR) { + if ((res = sendNextDataBlock(ba, bNum)) != DownloadResult::ERROR) { + bNum += 1; + fprintf(stderr, "."); + if ((bNum % 80) == 0) { + fprintf(stderr, "\n64-byte block %04d ", bNum); + } + } + } + } + fprintf(stderr, "\nlast 64-byte block %04d\n", bNum); + + 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() << "ERROR SEND REMAINING" << rest << "BYTES"; + m_hw->bl_sendDataBlock(64, local); + } + + m_hw->bl_sendLastBlock(); + qInfo() << "last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK()); + return res; + } + +#endif m_hw->dcDownloadRequestAck(); @@ -41,3 +168,146 @@ void DownloadThread::run() { // Never touch the object after this statement // m_hw->dcDownloadThreadFinalize(this); } + +DownloadThread::DownloadResult DownloadThread::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; +} + +DownloadThread::DownloadResult +DownloadThread::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 ) { + // qDebug() << "addr-block" << bNum << "..."; + 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; +} + +DownloadThread::DownloadResult +DownloadThread::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; + + // QByteArray b((const char *)(&local[0]), 64); + // qCritical() << "SNDB" << bNum << b.size() << b.toHex(); + + 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 DownloadThread::startBootloader() const { + qDebug() << "starting bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_startBL(); + QThread::msleep(5000); + m_hw->bl_checkBL(); + if (m_hw->bl_isUp()) { + qInfo() << "starting bootloader...OK"; + QThread::msleep(5000); + return true; + } else { + qCritical() << "bootloader not up (" << nTry << ")"; + } + } + qCritical() << "starting bootloader...FAILED"; + return false; +} + +bool DownloadThread::stopBootloader() const { + qDebug() << "stopping bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_stopBL(); + QThread::msleep(500); + if (!m_hw->bl_isUp()) { + qInfo() << "stopping bootloader...OK"; + return true; + } + } + qCritical() << "stopping bootloader...FAILED"; + return false; +} + +bool DownloadThread::resetDeviceController() const { + qDebug() << "resetting device controller..."; + m_hw->bl_rebootDC(); + // wait maximally 3 seconds, before starting bootloader + QThread::sleep(1); + qInfo() << "resetting device controller...OK"; + return true; +} + +QByteArray DownloadThread::loadBinaryDCFile(QString filename) const { + qDebug() << "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(); +}