Mercurial > public > ostc_companion
view OSTC4Operations.cpp @ 3:4ace58a7c03c
Send disconnect command before closing the connection
The old BT module transmitted a notification in case a connection were
closed which cause the ostc to exit the uart loop. The new one doesn't
do this => send disconnect command to avoid waiting in the installation
loop till timeout or button press.
| author | Ideenmodellierer |
|---|---|
| date | Fri, 28 Nov 2025 20:00:02 +0100 |
| parents | 0b3630a29ad8 |
| children | e30f00f760d3 |
line wrap: on
line source
////////////////////////////////////////////////////////////////////////////// /// \file OSTC4Operations.h /// \brief Implementing various operations for H&W OSTC4 dive computer /// \author JD Gascuel. /// \sa OSTC3Operations, HardwareOperations.h /// /// \copyright (c) 2016 JD Gascuel. All rights reserved. /// $Id$ ////////////////////////////////////////////////////////////////////////////// // // BSD 2-Clause License: // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////////////// #include <QSettings> #include <QRegularExpression> #include "OSTC4Operations.h" #include "Utils/Log.h" #include "Utils/Exception.h" #include "Utils/ProgressEvent.h" extern QSettings* settings; #define FIRMWARE_BLOCK 0x40 // 64 bytes #define FIRMWARE_BLOCK_DELAY 15 // 15 msec. #define FIRMWARE_BLOCK_FAST 0x300 // 4096 bytes ////////////////////////////////////////////////////////////////////////////// QSize OSTC4Operations::nameSize() const { return QSize(12, 4); } ////////////////////////////////////////////////////////////////////////////// QString OSTC4Operations::model() const { return "OSTC4"; } HardwareOperations::CompanionFeatures OSTC4Operations::supported() const { // No ICON, no DUMPSCREEN return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE |HELIUM_DIVE|CCR_DIVE|BLUETOOTH|VPM_MODEL|SIGNAL_CHECK); } QString OSTC4Operations::firmwareTemplate() const { return "ostc4*.bin"; } QStringList OSTC4Operations::listPorts() const { return listBluetoothPorts(); } #if 0 QRegExp OSTC4Operations::portTemplate() const { #if defined(Q_OS_MAC) return QRegExp("tty[.]OSTC4-.*", Qt::CaseInsensitive); #elif defined(Q_OS_LINUX) // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) return QRegExp("ttyUSB.*", Qt::CaseSensitive); #elif defined(Q_OS_WIN) return QRegExp("COM.*", Qt::CaseSensitive); #endif } #endif QRegularExpression OSTC4Operations::portTemplate() const { #if defined(Q_OS_MAC) return QRegularExpression("tty[.]OSTC4-.*", QRegularExpression::CaseInsensitiveOption); #elif defined(Q_OS_LINUX) // Debian, Ubuntu, RedHat, CentOS, SUSE return QRegularExpression("ttyUSB.*"); // default: case-sensitive #elif defined(Q_OS_WIN) return QRegularExpression("COM.*"); // default: case-sensitive #endif } ////////////////////////////////////////////////////////////////////////////// void OSTC4Operations::getIdentity() { descriptionString.clear(); LOG_TRACE("Getting model..."); HardwareDescriptor hw = hardwareDescriptor(); if( hw != HW_OSTC4 ) LOG_THROW("Not an OSTC4."); LOG_TRACE("Getting identity..."); getCommonIdentity(); //---- Main firmware ----------------------------------------------------- QString mainFW; { unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. if( echo != 'k' ) LOG_THROW("Bad firmware details."); unsigned char reply[4+1]; _serial.writeByte(0xFF); // Main firmware _serial.readBlock(reply, sizeof reply); if( reply[4] != 0x4D ) LOG_THROW("Bad main firmware reply."); mainFW = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); } //---- RTE firmware ------------------------------------------------------ QString rteFW; { unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. if( echo != 'k' ) LOG_THROW("Bad firmware details."); unsigned char reply[4+1]; _serial.writeByte(0xFE); // RTE firmware _serial.readBlock(reply, sizeof reply); if( reply[4] != 0x4D ) LOG_THROW("Bad RTE firmware reply."); rteFW = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); } //---- Font Package ------------------------------------------------------ QString fontPKG; { unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. if( echo != 'k' ) LOG_THROW("Bad firmware details."); unsigned char reply[4+1]; _serial.writeByte(0x10); // Font Package _serial.readBlock(reply, sizeof reply); if( reply[4] != 0x4D ) LOG_THROW("Bad Font package reply."); fontPKG = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); } //---- OSTC4 specific description ---------------------------------------- descriptionString = QString("%1 #%2, fw %3 (main %4/RTE %5/Font %6), %7") .arg(model()) .arg(serialNumber(), 4, 10, QChar('0')) .arg( QString::asprintf("%d.%02d", firmware() / 100, firmware() % 100)) .arg( mainFW ) .arg( rteFW ) .arg( fontPKG ) .arg( QString::fromLatin1((char*)(computerText), 60) .replace(QChar('\0'), "") .trimmed() ); LOG_INFO("Found " << descriptionString); } ////////////////////////////////////////////////////////////////////////////// OSTC4Operations::OSTC4Operations() : fileChecksum(0,0) { emulatorName = "OSTC4"; } ////////////////////////////////////////////////////////////////////////////// void OSTC4Operations::upgradeFW(const QString &fileName) { LOG_INFO("Starting OSTC4 firmware upgrade..."); openFirmware(fileName, true); openFirmware(fileName, false); LOG_INFO("Please wait for OSTC4 to complete installation..."); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /// FIRMWARE UPGRADE /// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #define SWAP4BYTES(x) \ uint( (((x) & 0x000000FF) << 24) \ | (((x) & 0x0000FF00) << 8) \ | (((x) & 0x00FF0000) >> 8) \ | (((x) & 0xFF000000) >> 24)) ////////////////////////////////////////////////////////////////////////////// void OSTC4Operations::openFirmware(const QString &fileName, bool dryRun) { LOG_TRACE("Opening OSTC4 firmware '" << fileName << "'..."); bool forceFirmwareUpdate; bool forceRTEUpdate; bool forceFontlibUpdate; bool useFastMode; bool firmwareSupportFastMode = false; int size; int blockSize; int transferDelay; // Previous trial failed ? if( _file.isOpen() ) _file.close(); _file.setFileName(fileName); if( ! _file.open(QIODevice::ReadOnly) ) LOG_THROW( "Cannot open BIN file " << fileName ); // Get settings to overwrite version check forceFirmwareUpdate = settings->value("OSTC/forceFirmwareUpdate", false).toBool(); forceRTEUpdate = settings->value("OSTC/forceRTEUpdate", false).toBool(); forceFontlibUpdate = settings->value("OSTC/forceFontlibUpdate", false).toBool(); useFastMode = settings->value("OSTC/useFastMode", false).toBool(); //---- Check consistency ------------------------------------------------- fileChecksum.reset(0, 0); for(int part=1; part<4; ++part) { // Less than 3 parts ? if( (_file.size() - _file.pos()) < 16 ) break; if( dryRun ) LOG_TRACE("Checking part " << part <<"..."); FirmwareOSTC4 header; QByteArray bin; loadFirmwarePart(header, bin, part, dryRun); QString name = QString::asprintf("%s v%d.%02d.%02d%s", header.type==0x10 ? "Font" : header.type==0xFF ? "FW " : header.type==0xFE ? "RTE " : "??? ", header.version.x, header.version.y, header.version.z, header.version.beta ? " beta" : ""); //---- On first pass, just check file structure ---------------------- if( dryRun ) continue; //---- On second pass, do upload new stuff --------------------------- if( _connectMode != SERVICE_MODE ) connectServiceMode(); if( _connectMode != SERVICE_MODE ) LOG_THROW("Cannot connect OSTC4 service mode..."); //---- Check if needed ? --------------------------------------------- LOG_TRACE("Check part " << part << " is needed ?"); unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. if( echo != 'k' ) LOG_THROW("Bad firmware details."); unsigned char reply[4+1]; _serial.writeByte(header.type); // type of firmware part _serial.readBlock(reply, sizeof reply); if( reply[4] != 0x4C ) // SERVICE MODE only. LOG_THROW("Bad firmware reply."); if(((reply[0] *100 + reply[1] * 10 + reply[2]) > 151 ) && (header.type==0xFF)) /* suitable firmware version? First supported in 1.5.2 */ { firmwareSupportFastMode = true; } if(( header.version.x == reply[0] && header.version.y == reply[1] && header.version.z == reply[2] ) && (!(forceFirmwareUpdate && header.type==0xFF)) && (!(forceRTEUpdate && header.type==0xFE)) && (!(forceFontlibUpdate && header.type==0x10)) ) { LOG_INFO(" part " << part << ": " << name << " already up-to-date."); continue; } if((useFastMode) && (firmwareSupportFastMode)) /* is fast mode supported ? */ { blockSize = FIRMWARE_BLOCK_FAST; transferDelay = 1; } else { blockSize = FIRMWARE_BLOCK; transferDelay = FIRMWARE_BLOCK_DELAY; } //---- Upload firwmare ----------------------------------------------- const int steps = 3 + (bin.size()+blockSize-1) / blockSize; int step = 0; PROGRESS(step++, steps); LOG_INFO(" uploading part " << part << ": " << name << "..."); writeText( QString("Upload Part %1").arg(part) ); //---- Command PROGRESS(step++, steps); echo = retryCommand(_serial, 's'); // 0x73 OSTC4 FW Upgrade if( echo != 's' ) LOG_THROW("Bad OSTC4 FW upgrade commande."); //---- Header PROGRESS(step++, steps); _serial.writeBlock((uchar*)&header, sizeof header); _serial.sleep(500); //---- Data for(int len = 0x00000; len < bin.size(); len += blockSize) { if(transferDelay) { _serial.sleep(transferDelay); // 15 msec between 64B blocks... } PROGRESS(step++, steps); size = qMin(blockSize, bin.size() - len); LOG_TRACE("Fill " << size); _serial.writeBlock((uchar*)bin.data()+len, (unsigned int)size); } PROGRESS(steps, steps); // 100% //---- Wait acknowledge ---------------------------------------------- // RTE seems to miss the acknowledge byte ... if( header.type != 0xFE ) { PROGRESS_THROTTLE(); for(int wait=0; wait<2*60; ++wait) // Up to 2 min... { try { unsigned char reply = _serial.readByte(); if( reply == 0x4C ) break; } catch( const ReadTimeout&) {} } } LOG_INFO(" part " << part << ": " << name << " uploaded."); } //---- Done -------------------------------------------------------------- // Low-level close, to avoid trying to send a 0xFF byte... if( !dryRun ) { disconnect(true); } PROGRESS_RESET(); //---- Check FILE checksum on first pass --------------------------------- if( dryRun ) { uint expected; if( 4 != _file.read((char*)&expected, sizeof expected) ) LOG_THROW("Missing file checksum."); uint checksum = fileChecksum.a() | (fileChecksum.b() << 16); if( checksum != expected ) LOG_ERROR( QString::asprintf("File checksum ERROR: expected = %04X, actual = %04X", expected, checksum) ); else if( dryRun ) LOG_TRACE( QString::asprintf("File checksum = %04X (OK)", checksum) ); if( ! _file.atEnd() ) LOG_THROW("Extra data after last block."); } _file.close(); } ////////////////////////////////////////////////////////////////////////////// void OSTC4Operations::loadFirmwarePart(FirmwareOSTC4& header, QByteArray& bin, int part, bool dryRun) { memset(&header, 0, sizeof header); //---- Read part header and check consistency ---------------------------- if( sizeof header != _file.read((char*)&header, sizeof header) ) LOG_THROW("EOF in header."); fileChecksum.add(&header, sizeof header); uint checksum = SWAP4BYTES(header.length) + SWAP4BYTES(*(uint*)&header.type); if( checksum != header.checksum ) LOG_THROW( QString::asprintf("Inconsistent header (%04X != %04X).", checksum, header.checksum) ); if( SWAP4BYTES(header.length) > (1024 * 1024) || (SWAP4BYTES(header.length) % 4) ) LOG_THROW("Inconsistent header (part=" << part << ", size=" << SWAP4BYTES(header.length) << ")."); if( dryRun ) switch( header.type ) { case 0x10: LOG_TRACE("... font"); break; case 0xFF: LOG_TRACE("... firmware"); break; case 0xFE: LOG_TRACE("... RTE"); break; default: // LOG_THROW("Inconsistent header (part=" << part << ", type=0x" << QString::asprintf(int(header.type), 16) << ")."); LOG_THROW("Inconsistent header (part=" << part << ", type=0x" << QString::asprintf("Value = %d", header.type) << ")."); break; } if( dryRun ) LOG_TRACE("... version " << QString::asprintf("%d.%02d.%02d.%s", header.version.x, header.version.y, header.version.z, header.version.beta ? " beta" : "")); //---- Read Binary Data -------------------------------------------------- if( dryRun ) LOG_TRACE("... size " << SWAP4BYTES(header.length) << " bytes."); bin.resize(SWAP4BYTES(header.length) + 4); assert((uint)bin.size() == SWAP4BYTES(header.length) + 4); if( bin.size() != _file.read(bin.data(), bin.size()) ) LOG_THROW("EOF in data."); if( dryRun ) LOG_TRACE("... checksum " << QString::asprintf("%08x", *(uint*)(bin.data() + SWAP4BYTES(header.length)))); fileChecksum.add(bin); } void OSTC4Operations::getSignal() { char buffer[60]; memset(buffer, 0, sizeof buffer); unsigned char echo = retryCommand(_serial, 'l'); // 0x6c request bluetooth signal if( echo != 0x6C ) LOG_THROW("Bad text reply (1)" << echo); _serial.sleep(1000); unsigned char ok = _serial.readByte(); if( ok != 0x4D ) // DOWNLOAD mode only. LOG_THROW("Bad clock reply (2)"); } void OSTC4Operations::getAllHeader(unsigned char* pBuffer) { unsigned char index = 0; char buffer[60]; memset(buffer, 0, sizeof buffer); if( _connectMode != SERVICE_MODE ) connectServiceMode(); if( _connectMode != SERVICE_MODE ) LOG_THROW("Cannot connect OSTC4 service mode..."); unsigned char echo = retryCommand(_serial, 0x85); // 0x85 request all headers (dump) if( echo != 0x85 ) LOG_THROW("Bad text reply (1)" << echo); _serial.sleep(1000); for(index = 0; index < 8; index++) // Expect 8 blocks a 0x8000 { _serial.readBlock(pBuffer + (index * 0x8000), 0x8000); LOG_INFO((".")); } *(pBuffer + (index * 0x8000)) = _serial.readByte(); // get lastdiveID LOG_INFO(QString::asprintf("%d",*(pBuffer + (index * 0x8000)))); unsigned char ok = _serial.readByte(); if( ok != 0x4C ) //Service mode only. LOG_THROW("Bad clock reply (2)"); } void OSTC4Operations::writeAllHeader(unsigned char* pBuffer) { unsigned char index = 0; char buffer[60]; memset(buffer, 0, sizeof buffer); if( _connectMode != SERVICE_MODE ) connectServiceMode(); if( _connectMode != SERVICE_MODE ) LOG_THROW("Cannot connect OSTC4 service mode..."); unsigned char echo = retryCommand(_serial, 0x86); // 0x86 request all headers (dump) if( echo != 0x86 ) LOG_THROW("Bad text reply (1)" << echo); _serial.sleep(1000); for(index = 0; index < 8; index++) // Expect 8 blocks a 0x8000 { _serial.writeBlock(pBuffer + (index * 0x8000), 0x8000); LOG_INFO((".")); } _serial.writeByte(*(pBuffer + (index * 0x8000))); // write lastdiveID unsigned char ok = _serial.readByte(); if( ok != 0x4C ) //Service mode only. LOG_THROW("Bad clock reply (2)"); } void OSTC4Operations::getAllSamples(unsigned char* pBuffer) { unsigned short index = 0; char buffer[60]; memset(buffer, 0, sizeof buffer); if( _connectMode != SERVICE_MODE ) connectServiceMode(); if( _connectMode != SERVICE_MODE ) LOG_THROW("Cannot connect OSTC4 service mode..."); unsigned char echo = retryCommand(_serial, 0x88); // 0x88 request all samples (dump) if( echo != 0x88 ) LOG_THROW("Bad text reply (1)" << echo); _serial.sleep(500); for(index = 0; index < 384; index++) // Expect 8 blocks a 0x8000 { _serial.readBlock(pBuffer + (index * 0x8000), 0x8000); LOG_INFO(QString::asprintf("%d",index)); } _serial.readBlock(pBuffer + (index * 0x8000), 4); // get lastdiveID unsigned char ok = _serial.readByte(); if( ok != 0x4C ) //Service mode only. LOG_THROW("Bad clock reply (2)"); } void OSTC4Operations::writeAllSamples(unsigned char* pBuffer) { unsigned short index = 0; char buffer[60]; memset(buffer, 0, sizeof buffer); if( _connectMode != SERVICE_MODE ) connectServiceMode(); if( _connectMode != SERVICE_MODE ) LOG_THROW("Cannot connect OSTC4 service mode..."); unsigned char echo = retryCommand(_serial, 0x89); // 0x88 request all samples (dump) if( echo != 0x89 ) LOG_THROW("Bad text reply (1)" << echo); _serial.sleep(500); for(index = 0; index < 384; index++) // Expect 8 blocks a 0x8000 384 { _serial.writeBlock((const unsigned char*)(pBuffer + (index * 0x8000)), 0x8000); LOG_INFO(QString::asprintf("%d",index)); } _serial.writeBlock(pBuffer + (index * 0x8000), 4); // set lastdiveID unsigned char ok = _serial.readByte(); if( ok != 0x4C ) //Service mode only. LOG_THROW("Bad clock reply (2)"); } //////////////////////////////////////////////////////////////////////////////
