Mercurial > public > ostc_companion
comparison OSTC3Operations.cpp @ 1:0b3630a29ad8
Initial version based on previous repository.
Project was ported to QT6 and in now cmake based.
| author | Ideenmodellierer <tiefenrauscher@web.de> |
|---|---|
| date | Thu, 27 Nov 2025 18:40:28 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:76ccd6ce50c0 | 1:0b3630a29ad8 |
|---|---|
| 1 ////////////////////////////////////////////////////////////////////////////// | |
| 2 /// \file OSTC3Operations.cpp | |
| 3 /// \brief Implementing various operations for OSTC3 dive computer | |
| 4 /// \author JD Gascuel. | |
| 5 /// | |
| 6 /// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. | |
| 7 /// $Id$ | |
| 8 ////////////////////////////////////////////////////////////////////////////// | |
| 9 // | |
| 10 // BSD 2-Clause License: | |
| 11 // | |
| 12 // Redistribution and use in source and binary forms, with or without | |
| 13 // modification, are permitted provided that the following conditions | |
| 14 // are met: | |
| 15 // | |
| 16 // 1. Redistributions of source code must retain the above copyright notice, | |
| 17 // this list of conditions and the following disclaimer. | |
| 18 // | |
| 19 // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 20 // this list of conditions and the following disclaimer in the documentation | |
| 21 // and/or other materials provided with the distribution. | |
| 22 // | |
| 23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 24 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 25 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 26 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 27 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 28 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 29 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 30 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 31 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 32 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| 33 // THE POSSIBILITY OF SUCH DAMAGE. | |
| 34 // | |
| 35 ////////////////////////////////////////////////////////////////////////////// | |
| 36 | |
| 37 #include "OSTC3Operations.h" | |
| 38 | |
| 39 #include "Utils/Exception.h" | |
| 40 #include "Utils/Log.h" | |
| 41 #include "Utils/ProgressEvent.h" | |
| 42 | |
| 43 #include "SettingsDialog.h" | |
| 44 #include "HexFile.h" | |
| 45 | |
| 46 #include <QApplication> | |
| 47 #include <QDateTime> | |
| 48 #include <QDir> | |
| 49 #include <QRegularExpression> | |
| 50 | |
| 51 // Byte extration, compatible littleendian or bigendian. | |
| 52 #define LOW(x) ((unsigned char)((x) % 256)) | |
| 53 #define HIGH(x) ((unsigned char)((x / (1<<8)) % 256)) | |
| 54 #define UPPER(x) ((unsigned char)((x / (1<<16)) % 256)) | |
| 55 #define UP32(x) ((unsigned char)((x / (1<<24)) % 256)) | |
| 56 | |
| 57 #define FIRMWARE_AREA 0x3E0000 | |
| 58 #define FIRMWARE_SIZE 0x01E000 // 120KB | |
| 59 #define FIRMWARE_BLOCK 0x1000 // 4KB | |
| 60 | |
| 61 ////////////////////////////////////////////////////////////////////////////// | |
| 62 | |
| 63 OSTC3Operations::OSTC3Operations() | |
| 64 : descriptionString(""), | |
| 65 emulatorName("OSTC3"), | |
| 66 _computerFirmware(0), | |
| 67 _computerSerial(0), | |
| 68 _connectMode(CLOSED_MODE) | |
| 69 { | |
| 70 memset(computerText, 0, sizeof computerText); | |
| 71 } | |
| 72 | |
| 73 OSTC3Operations::~OSTC3Operations() | |
| 74 { | |
| 75 if( _connectMode != CLOSED_MODE ) | |
| 76 disconnect(true); | |
| 77 } | |
| 78 | |
| 79 ////////////////////////////////////////////////////////////////////////////// | |
| 80 | |
| 81 QStringList OSTC3Operations::listPorts() const | |
| 82 { | |
| 83 return listUSBPorts(); | |
| 84 } | |
| 85 | |
| 86 ////////////////////////////////////////////////////////////////////////////// | |
| 87 | |
| 88 bool OSTC3Operations::connect() | |
| 89 { | |
| 90 LOG_TRACE( "Enter download mode..." ); | |
| 91 | |
| 92 try { | |
| 93 _connectMode = CLOSED_MODE; | |
| 94 _serial.open( Settings::port, emulatorName); | |
| 95 _serial.sleep(333); // Initial 1/3 sec. delay to first comm. | |
| 96 | |
| 97 for(int retry=0; retry < 10; ++retry) | |
| 98 { | |
| 99 // Allow for 0.1sec extra delay | |
| 100 try { | |
| 101 _serial.writeByte(0xBB); | |
| 102 } catch(const WriteTimeout& ) { | |
| 103 // Bluetooth not present: one can open the pseudo COM port, | |
| 104 // but we will have a timeout on first write byte... | |
| 105 if( retry < 9 ) { | |
| 106 LOG_INFO("Cannot connect to " << Settings::port <<" (" << (retry+1) << "/10)..."); | |
| 107 _serial.sleep(1000); | |
| 108 continue; | |
| 109 } | |
| 110 LOG_THROW("Cannot connect to " << model() <<"."); | |
| 111 return false; | |
| 112 } | |
| 113 | |
| 114 _serial.sleep(100); | |
| 115 | |
| 116 //---- Check acknowledge, w/o fatal timeouts. | |
| 117 unsigned char ok = 0; | |
| 118 unsigned char echo = 0; | |
| 119 try { | |
| 120 echo = _serial.readByte(); | |
| 121 | |
| 122 // Already in connect() mode ??? | |
| 123 if( echo == 'M' ) | |
| 124 break; | |
| 125 | |
| 126 ok = _serial.readByte(); | |
| 127 } | |
| 128 catch(const ReadTimeout&) { | |
| 129 LOG_INFO("Retry " << (retry+1) << "/10..."); | |
| 130 } | |
| 131 | |
| 132 if( echo != 0xBB || ok != 0x4D ) { // DOWNLOAD modes only. | |
| 133 if( retry < 9 ) | |
| 134 { | |
| 135 _serial.purge(); | |
| 136 _serial.sleep(400); | |
| 137 continue; | |
| 138 } | |
| 139 LOG_THROW("Unable to enter hwOS service mode"); | |
| 140 return false; | |
| 141 } | |
| 142 break; | |
| 143 } | |
| 144 getIdentity(); | |
| 145 | |
| 146 QString banner = Log::applicationName(); | |
| 147 writeText(banner); | |
| 148 LOG_TRACE("Connected."); | |
| 149 _connectMode = DOWNLOAD_MODE; | |
| 150 return true; | |
| 151 } | |
| 152 catch(const Exception& e) { | |
| 153 disconnect(); | |
| 154 LOG_THROW("Port " << Settings::port << ": " << e.what()); | |
| 155 } | |
| 156 return false; | |
| 157 } | |
| 158 | |
| 159 ////////////////////////////////////////////////////////////////////////////// | |
| 160 | |
| 161 bool OSTC3Operations::disconnect(bool /*closing*/) | |
| 162 { | |
| 163 if( _connectMode == CLOSED_MODE ) return false; | |
| 164 | |
| 165 descriptionString.clear(); // cleanup for interface updateStatus() | |
| 166 _connectMode = CLOSED_MODE; | |
| 167 | |
| 168 _serial.purge(); | |
| 169 _serial.writeByte(0xFF); // Exit communications, just in case... | |
| 170 _serial.sleep(100); | |
| 171 | |
| 172 _serial.purge(); | |
| 173 _serial.close(); | |
| 174 | |
| 175 return true; | |
| 176 } | |
| 177 | |
| 178 ////////////////////////////////////////////////////////////////////////////// | |
| 179 | |
| 180 void OSTC3Operations::getIdentity() | |
| 181 { | |
| 182 descriptionString.clear(); | |
| 183 | |
| 184 LOG_TRACE("Getting model..."); | |
| 185 HardwareDescriptor hw = hardwareDescriptor(); | |
| 186 // if( hw != HW_OSTC3 ) | |
| 187 // LOG_THROW("Not an OSTC3."); | |
| 188 | |
| 189 LOG_TRACE("Getting identity..."); | |
| 190 getCommonIdentity(); | |
| 191 | |
| 192 LOG_INFO("Found " << descriptionString.trimmed()); | |
| 193 } | |
| 194 | |
| 195 ////////////////////////////////////////////////////////////////////////////// | |
| 196 | |
| 197 void OSTC3Operations::getCommonIdentity() | |
| 198 { | |
| 199 unsigned char echo = retryCommand(_serial, 'i'); // 0x69 | |
| 200 if( echo != 'i' ) | |
| 201 LOG_THROW("Bad identity reply (1)"); | |
| 202 | |
| 203 unsigned char header[4 + 60 + 1] = {0}; | |
| 204 _serial.readBlock(header, sizeof header); | |
| 205 if( header[64] != 0x4D ) // DOWNLOAD modes only. | |
| 206 LOG_THROW("Bad identity reply (2)"); | |
| 207 | |
| 208 _computerSerial = header[0] + header[1]*256; | |
| 209 _computerFirmware = header[2] * 100 + header[3]; | |
| 210 memcpy(computerText, header+4, sizeof computerText); | |
| 211 | |
| 212 descriptionString = QString("%1 #%2, fw %3.%4, %5") | |
| 213 .arg(model()) | |
| 214 .arg(serialNumber(), 4, 10, QChar('0')) | |
| 215 .arg(firmware() / 100).arg(firmware() % 100, 2, 10, QChar('0')) | |
| 216 .arg( QString::fromLatin1((char*)(computerText), 60) | |
| 217 .replace(QChar('\0'), " ") ); | |
| 218 } | |
| 219 | |
| 220 ////////////////////////////////////////////////////////////////////////////// | |
| 221 | |
| 222 int OSTC3Operations::firmware() const | |
| 223 { | |
| 224 return _computerFirmware; | |
| 225 } | |
| 226 | |
| 227 int OSTC3Operations::serialNumber() const | |
| 228 { | |
| 229 return _computerSerial; | |
| 230 } | |
| 231 | |
| 232 QString OSTC3Operations::customText() const | |
| 233 { | |
| 234 return QString::fromLatin1(computerText, sizeof computerText); | |
| 235 } | |
| 236 | |
| 237 ////////////////////////////////////////////////////////////////////////////// | |
| 238 | |
| 239 QSize OSTC3Operations::nameSize() const | |
| 240 { | |
| 241 return QSize(12, 5); | |
| 242 } | |
| 243 | |
| 244 ////////////////////////////////////////////////////////////////////////////// | |
| 245 | |
| 246 void OSTC3Operations::writeText(const QString& msg) | |
| 247 { | |
| 248 QByteArray buffer = msg.leftJustified(16, ' ', true).toLatin1(); | |
| 249 LOG_TRACE("Echoing string '" << buffer << "'"); | |
| 250 | |
| 251 // 2014-10-27 jDG: On OSTC3 v1.60, after an ERASE AREA, we do get | |
| 252 // a spurious L here (instead of n)... | |
| 253 unsigned char echo = retryCommand(_serial, 'n'); // 0x6E Echo string. | |
| 254 if( echo != 'n' ) | |
| 255 LOG_THROW("Bad message reply (1)"); | |
| 256 | |
| 257 _serial.writeBlock((const unsigned char*)buffer.data(), 16); | |
| 258 _serial.sleep(25); // Allow 25msec to display the message... | |
| 259 | |
| 260 unsigned char ok = _serial.readByte(); | |
| 261 if( ok != 0x4C && ok != 0x4D ) // DOWNLOAD or SERVICE modes. | |
| 262 LOG_THROW("Bad message reply (2)"); | |
| 263 } | |
| 264 | |
| 265 ////////////////////////////////////////////////////////////////////////////// | |
| 266 | |
| 267 void OSTC3Operations::setDate(const QDateTime &date) | |
| 268 { | |
| 269 LOG_TRACE("Set Date " << date.toString("MM/dd/yyyy hh:mm")); | |
| 270 | |
| 271 unsigned char buffer[6]; | |
| 272 buffer[0] = date.time().hour(); | |
| 273 buffer[1] = date.time().minute(); | |
| 274 buffer[2] = date.time().second(); | |
| 275 buffer[3] = date.date().month(); | |
| 276 buffer[4] = date.date().day(); | |
| 277 buffer[5] = date.date().year() % 100; | |
| 278 | |
| 279 unsigned char echo = retryCommand(_serial, 'b'); // 0x62 Sync date | |
| 280 if( echo != 'b' ) | |
| 281 LOG_THROW("Bad clock reply (1)"); | |
| 282 | |
| 283 _serial.writeBlock(buffer, sizeof buffer); | |
| 284 _serial.sleep(5); | |
| 285 | |
| 286 unsigned char ok = _serial.readByte(); | |
| 287 if( ok != 0x4D ) // DOWNLOAD mode only. | |
| 288 LOG_THROW("Bad clock reply (2)"); | |
| 289 | |
| 290 writeText( "Set " + date.toString("MM/dd hh:mm") ); | |
| 291 } | |
| 292 | |
| 293 ////////////////////////////////////////////////////////////////////////////// | |
| 294 | |
| 295 void OSTC3Operations::setName(const QString &newName) | |
| 296 { | |
| 297 LOG_TRACE("Set Name '" << newName << "'"); | |
| 298 | |
| 299 char buffer[60]; | |
| 300 memset(buffer, 0, sizeof buffer); | |
| 301 strncpy(buffer, newName.toLatin1().constData(), sizeof buffer); | |
| 302 | |
| 303 unsigned char echo = retryCommand(_serial, 'c'); // 0x63 Send custom text | |
| 304 if( echo != 'c' ) | |
| 305 LOG_THROW("Bad text reply (1)"); | |
| 306 | |
| 307 _serial.writeBlock((unsigned char*)buffer, sizeof buffer); | |
| 308 _serial.sleep(5); | |
| 309 | |
| 310 unsigned char ok = _serial.readByte(); | |
| 311 if( ok != 0x4D ) // DOWNLOAD modes only. | |
| 312 LOG_THROW("Bad text reply (2)"); | |
| 313 | |
| 314 getIdentity(); | |
| 315 // Echo the first line of customtext: | |
| 316 writeText(newName.left(12).trimmed()); | |
| 317 } | |
| 318 | |
| 319 ////////////////////////////////////////////////////////////////////////////// | |
| 320 | |
| 321 void OSTC3Operations::setIcons(const QString &) | |
| 322 { | |
| 323 LOG_THROW("Set icons: Not yet implemented"); | |
| 324 } | |
| 325 | |
| 326 QImage OSTC3Operations::dumpScreen() const | |
| 327 { | |
| 328 LOG_THROW("Dump screen: Not yet implemented"); | |
| 329 return QImage(); | |
| 330 } | |
| 331 | |
| 332 ////////////////////////////////////////////////////////////////////////////// | |
| 333 | |
| 334 static unsigned char ostc3SecretKey[16] = { | |
| 335 241,233, 176, 48, | |
| 336 69,111, 190, 85, | |
| 337 255,231, 248, 49, | |
| 338 19,108, 242,254 | |
| 339 }; | |
| 340 | |
| 341 ////////////////////////////////////////////////////////////////////////////// | |
| 342 | |
| 343 void OSTC3Operations::eraseRange(unsigned int addr, unsigned int size) | |
| 344 { | |
| 345 // Convert size to number of pages, rounded up. | |
| 346 size = ((size + 4095) / 4096); | |
| 347 | |
| 348 // Erase just the needed pages. | |
| 349 unsigned char buffer[4]; | |
| 350 buffer[0] = UPPER(addr); | |
| 351 buffer[1] = HIGH(addr); | |
| 352 buffer[2] = LOW(addr); | |
| 353 buffer[3] = LOW(size); | |
| 354 | |
| 355 unsigned char reply = retryCommand(_serial, 'B'); // Command 'B' | |
| 356 if( reply != 0x42 ) | |
| 357 LOG_THROW("eraseRange (1)"); | |
| 358 | |
| 359 _serial.writeBlock(buffer, 4); | |
| 360 | |
| 361 // Wait (120/4)ms by block of 4K, plus 3% VAT to be sure. | |
| 362 _serial.sleep(40 + size * 31); | |
| 363 | |
| 364 unsigned char ok = _serial.readByte(); | |
| 365 if( ok != 0x4c ) // SERVICE MODE acknowledge. | |
| 366 LOG_THROW("eraseRange (2)"); | |
| 367 } | |
| 368 | |
| 369 ////////////////////////////////////////////////////////////////////////////// | |
| 370 | |
| 371 void OSTC3Operations::writeBlock(unsigned int addr, | |
| 372 const unsigned char *data, | |
| 373 unsigned int size) | |
| 374 { | |
| 375 unsigned char buffer[3]; | |
| 376 buffer[0] = UPPER(addr); | |
| 377 buffer[1] = HIGH(addr); | |
| 378 buffer[2] = LOW(addr); | |
| 379 | |
| 380 unsigned char reply = retryCommand(_serial, '0'); // 0x30 | |
| 381 if( reply != '0' ) | |
| 382 LOG_THROW("startWrite"); | |
| 383 | |
| 384 _serial.writeBlock(buffer, sizeof buffer); | |
| 385 _serial.sleep(2); | |
| 386 | |
| 387 _serial.writeBlock(data, size); | |
| 388 // Approximated EEPROM write time some 1sec timeout ?? | |
| 389 // 1KB = 240 + 1000 = 1240 : Ok. | |
| 390 // 4KB = 1.1sec : Ok. | |
| 391 _serial.sleep(1100); | |
| 392 | |
| 393 unsigned char ok = _serial.readByte(); | |
| 394 if( ok != 0x4c && ok != 0x4d ) // DOWNLOAD or SERVICE modes. | |
| 395 LOG_THROW("stopWrite"); | |
| 396 } | |
| 397 | |
| 398 | |
| 399 ////////////////////////////////////////////////////////////////////////////// | |
| 400 | |
| 401 void OSTC3Operations::readBlock(unsigned int addr, unsigned char* ptr, unsigned int size) | |
| 402 { | |
| 403 unsigned char buffer[6]; | |
| 404 buffer[0] = UPPER(addr); | |
| 405 buffer[1] = HIGH(addr); | |
| 406 buffer[2] = LOW(addr); | |
| 407 buffer[3] = UPPER(size); | |
| 408 buffer[4] = HIGH(size); | |
| 409 buffer[5] = LOW(size); | |
| 410 | |
| 411 unsigned char reply = retryCommand(_serial, 0x20); // Command ' ' | |
| 412 if( reply != 0x20 ) | |
| 413 LOG_THROW("readBytes"); | |
| 414 | |
| 415 _serial.writeBlock(buffer, sizeof buffer); | |
| 416 _serial.sleep(500); // Allow some time to start emitting... | |
| 417 _serial.readBlock(ptr, size); | |
| 418 | |
| 419 // Note: in that case, the OK byte is send AFTER the data block. | |
| 420 unsigned char ok = _serial.readByte(); | |
| 421 if( ok != 0x4C ) // SERVICE modes only. | |
| 422 LOG_THROW("readBytes (2)"); | |
| 423 } | |
| 424 | |
| 425 ////////////////////////////////////////////////////////////////////////////// | |
| 426 | |
| 427 void OSTC3Operations::upgradeFirmware(unsigned int checksum) | |
| 428 { | |
| 429 unsigned char buffer[5]; | |
| 430 buffer[0] = LOW(checksum); | |
| 431 buffer[1] = HIGH(checksum); | |
| 432 buffer[2] = UPPER(checksum); | |
| 433 buffer[3] = UP32(checksum); | |
| 434 | |
| 435 // Compute magic checksum's checksum. | |
| 436 buffer[4] = 0x55; | |
| 437 buffer[4] ^= buffer[0]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); | |
| 438 buffer[4] ^= buffer[1]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); | |
| 439 buffer[4] ^= buffer[2]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); | |
| 440 buffer[4] ^= buffer[3]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); | |
| 441 | |
| 442 unsigned char reply = retryCommand(_serial, 0x50); // 'P' : send FW to bootloader | |
| 443 if( reply != 0x50 ) | |
| 444 LOG_THROW("Flashing start (1)"); | |
| 445 | |
| 446 _serial.writeBlock(buffer, sizeof buffer); | |
| 447 unsigned char ok = _serial.readByte(); | |
| 448 if( ok != 0x4C ) // SERVICE modes only. | |
| 449 LOG_THROW("Flashing start (2)"); | |
| 450 | |
| 451 // NOTE: the device never return, because it always do a reset, | |
| 452 // with ot without reprogramming... | |
| 453 _serial.sleep(500); | |
| 454 _serial.close(); | |
| 455 descriptionString.clear(); | |
| 456 } | |
| 457 | |
| 458 QString OSTC3Operations::firmwareTemplate() const | |
| 459 { | |
| 460 return "*_firmware.hex"; | |
| 461 } | |
| 462 | |
| 463 #if 0 | |
| 464 QRegExp OSTC3Operations::portTemplate() const | |
| 465 { | |
| 466 #if defined(Q_OS_MAC) | |
| 467 return QRegExp("tty.usbserial-.*", Qt::CaseInsensitive); | |
| 468 #elif defined(Q_OS_LINUX) | |
| 469 // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) | |
| 470 return QRegExp("ttyUSB.*", Qt::CaseSensitive); | |
| 471 #elif defined(Q_OS_WIN) | |
| 472 return QRegExp("COM.*", Qt::CaseSensitive); | |
| 473 #endif | |
| 474 } | |
| 475 #endif | |
| 476 | |
| 477 QRegularExpression OSTC3Operations::portTemplate() const | |
| 478 { | |
| 479 #if defined(Q_OS_MAC) | |
| 480 return QRegularExpression("tty.usbserial-.*", | |
| 481 QRegularExpression::CaseInsensitiveOption); | |
| 482 #elif defined(Q_OS_LINUX) | |
| 483 // Debian, Ubuntu, RedHat, CentOS, SUSE | |
| 484 return QRegularExpression("ttyUSB.*"); // default: case-sensitive | |
| 485 #elif defined(Q_OS_WIN) | |
| 486 return QRegularExpression("COM.*"); // default: case-sensitive | |
| 487 #endif | |
| 488 } | |
| 489 ////////////////////////////////////////////////////////////////////////////// | |
| 490 | |
| 491 void OSTC3Operations::connectServiceMode() | |
| 492 { | |
| 493 LOG_TRACE( "Enter service mode..." ); | |
| 494 | |
| 495 // NOTE: Service mode requires a special starting sequence, different from | |
| 496 // the usual download mode state. | |
| 497 // Also, the general acknowledge byte is changed to 0x4c 'L'. | |
| 498 | |
| 499 assert( _connectMode != SERVICE_MODE ); | |
| 500 _serial.open( Settings::port, emulatorName); | |
| 501 _serial.sleep(333); // Initial 1/3 sec before trying service mode... | |
| 502 | |
| 503 for(int retry=0; retry < 10; ++retry) | |
| 504 { | |
| 505 unsigned char serviceMode[] = { 0xAA, 0xAB, 0xCD, 0xEF }; | |
| 506 | |
| 507 try { | |
| 508 _serial.writeBlock(serviceMode, sizeof serviceMode); | |
| 509 } | |
| 510 catch(const WriteTimeout&) { | |
| 511 // Bluetooth not present: one can open the pseudo COM port, | |
| 512 // but we will have a timeout on first write byte... | |
| 513 if( retry < 9 ) { | |
| 514 LOG_INFO("Cannot connect to " << Settings::port <<" (" << (retry+1) << "/10)..."); | |
| 515 _serial.sleep(1000); | |
| 516 continue; | |
| 517 } | |
| 518 LOG_THROW("Cannot connect to " << model() << "."); | |
| 519 return; | |
| 520 } | |
| 521 | |
| 522 // Allow for 0.1sec extra delay | |
| 523 _serial.sleep(100); | |
| 524 | |
| 525 //---- Check acknowledge: | |
| 526 unsigned char echo = 0; | |
| 527 try { | |
| 528 echo = _serial.readByte(); | |
| 529 } | |
| 530 catch(...) {} | |
| 531 | |
| 532 if( echo != 0x4b ) { | |
| 533 serviceModeFailed: | |
| 534 if( retry < 9 ) | |
| 535 { | |
| 536 _serial.purge(); | |
| 537 _serial.sleep(400); | |
| 538 continue; | |
| 539 } | |
| 540 LOG_THROW("Unable to enter " << model() <<" service mode"); | |
| 541 } | |
| 542 | |
| 543 echo = _serial.readByte(); | |
| 544 if( echo != 0xAB ) | |
| 545 goto serviceModeFailed; | |
| 546 echo = _serial.readByte(); | |
| 547 if( echo != 0xCD ) | |
| 548 goto serviceModeFailed; | |
| 549 echo = _serial.readByte(); | |
| 550 if( echo != 0xEF ) | |
| 551 goto serviceModeFailed; | |
| 552 echo = _serial.readByte(); | |
| 553 if( echo != 0x4c ) // SERVICE modes only. | |
| 554 goto serviceModeFailed; | |
| 555 break; | |
| 556 } | |
| 557 _connectMode = SERVICE_MODE; | |
| 558 } | |
| 559 | |
| 560 ////////////////////////////////////////////////////////////////////////////// | |
| 561 | |
| 562 void OSTC3Operations::upgradeFW(const QString &fileName) | |
| 563 { | |
| 564 //---- Load and check firmware ------------------------------------------- | |
| 565 LOG_INFO("Loading firmware."); | |
| 566 HexFile hex; | |
| 567 loadFirmware(hex, fileName); | |
| 568 | |
| 569 //---- Enter Service Mode ------------------------------------------------ | |
| 570 connectServiceMode(); | |
| 571 | |
| 572 //---- Erase old Firmware ------------------------------------------------ | |
| 573 PROGRESS(0, FIRMWARE_SIZE); | |
| 574 LOG_INFO("Erasing Firmware."); | |
| 575 | |
| 576 writeText(" Erasing FW..."); | |
| 577 eraseRange(FIRMWARE_AREA, FIRMWARE_SIZE); | |
| 578 | |
| 579 //---- Upload Firmware --------------------------------------------------- | |
| 580 LOG_INFO("Uploading firmware."); | |
| 581 writeText(" Uploading..."); | |
| 582 | |
| 583 for(int len = 0x00000; len < FIRMWARE_SIZE; len += FIRMWARE_BLOCK) | |
| 584 { | |
| 585 unsigned char percent = int(len * 100.0f / FIRMWARE_SIZE + 0.5f); | |
| 586 writeText( QString(" Uploading %1%") | |
| 587 .arg(percent, 2) ); | |
| 588 PROGRESS(percent, 100); | |
| 589 | |
| 590 writeBlock(FIRMWARE_AREA+len, hex.data()+len, FIRMWARE_BLOCK); | |
| 591 } | |
| 592 PROGRESS(100, 100); | |
| 593 | |
| 594 //---- Verify firmware --------------------------------------------------- | |
| 595 LOG_INFO("Verify firmware."); | |
| 596 writeText(" Verifying..."); | |
| 597 { | |
| 598 unsigned char* buffer = new unsigned char[FIRMWARE_SIZE]; | |
| 599 Q_CHECK_PTR(buffer); | |
| 600 | |
| 601 for(int len = 0x00000; len < FIRMWARE_SIZE; len += FIRMWARE_BLOCK) | |
| 602 { | |
| 603 unsigned char percent = int(len * 100.0f / FIRMWARE_SIZE + 0.5f); | |
| 604 writeText( QString(" Verifying %1%") | |
| 605 .arg(percent, 2) ); | |
| 606 PROGRESS(percent, 100); | |
| 607 | |
| 608 readBlock(FIRMWARE_AREA+len, buffer+len, FIRMWARE_BLOCK); | |
| 609 } | |
| 610 PROGRESS(100, 100); | |
| 611 qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 10); | |
| 612 | |
| 613 for(int i=0; i<FIRMWARE_SIZE; ++i) | |
| 614 if( buffer[i] != hex.data()[i] ) | |
| 615 { | |
| 616 writeText(" Verify FAILED"); | |
| 617 LOG_THROW("readback is different"); | |
| 618 } | |
| 619 | |
| 620 delete[] buffer; | |
| 621 } | |
| 622 PROGRESS_THROTTLE(); | |
| 623 | |
| 624 //---- Flashing firmware ------------------------------------------------- | |
| 625 LOG_INFO("Programming."); | |
| 626 writeText(" Programming..."); | |
| 627 upgradeFirmware( hex.checksum() ); | |
| 628 | |
| 629 //---- Done -------------------------------------------------------------- | |
| 630 // Low-level close, to avoid trying to send a 0xFF byte... | |
| 631 _serial.close(); | |
| 632 _connectMode = CLOSED_MODE; | |
| 633 LOG_INFO("Upgrade done."); | |
| 634 PROGRESS_RESET(); | |
| 635 } | |
| 636 | |
| 637 void OSTC3Operations::loadFirmware(HexFile& hex, const QString& fileName) const | |
| 638 { | |
| 639 hex.allocate(FIRMWARE_SIZE); | |
| 640 hex.loadEncrypted(fileName, ostc3SecretKey); | |
| 641 } | |
| 642 | |
| 643 ////////////////////////////////////////////////////////////////////////////// | |
| 644 | |
| 645 QString OSTC3Operations::model() const | |
| 646 { | |
| 647 return "OSTC hwOS (USB)"; | |
| 648 } | |
| 649 | |
| 650 QString OSTC3Operations::description() | |
| 651 { | |
| 652 return descriptionString; | |
| 653 } | |
| 654 | |
| 655 ////////////////////////////////////////////////////////////////////////////// | |
| 656 | |
| 657 HardwareOperations::CompanionFeatures OSTC3Operations::supported() const | |
| 658 { | |
| 659 // No ICON, no DUMPSCREEN, no VPM | |
| 660 return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE | |
| 661 |HELIUM_DIVE|CCR_DIVE); | |
| 662 } | |
| 663 | |
| 664 ////////////////////////////////////////////////////////////////////////////// | |
| 665 void OSTC3Operations::getSignal() | |
| 666 { | |
| 667 return; | |
| 668 } | |
| 669 void OSTC3Operations::getAllHeader(unsigned char* pBuffer) | |
| 670 { | |
| 671 return; | |
| 672 } | |
| 673 void OSTC3Operations::writeAllHeader(unsigned char* pBuffer) | |
| 674 { | |
| 675 return; | |
| 676 } | |
| 677 | |
| 678 void OSTC3Operations::getAllSamples(unsigned char* pBuffer) | |
| 679 { | |
| 680 return; | |
| 681 } | |
| 682 void OSTC3Operations::writeAllSamples(unsigned char* pBuffer) | |
| 683 { | |
| 684 return; | |
| 685 } |
