#include "dc_download.h" #include "hwapi.h" #include #include #include DcDownload::DcDownload(hwinf *hw) : m_hw(hw) , m_fileToDownload(m_hw->dcDownloadFileName()) { // connect(this, &QThread::finished, // dynamic_cast(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(); }