Mercurial > public > ostc_companion
diff HardwareOperations.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 | e30f00f760d3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HardwareOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,377 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file HardwareOperations.cpp +/// \brief Abstract operations for HW dive computers. +/// \author JD Gascuel. +/// \sa OSTC3Operations.cpp +/// +/// \copyright (c) 2011-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 "HardwareOperations.h" +#include "MainWindow.h" +#include "Utils/Log.h" +#include <QSerialPortInfo> + +#ifdef WIN32 +# include <winreg.h> +#endif + +#include <QDir> + +////////////////////////////////////////////////////////////////////////////// + +unsigned char HardwareOperations::retryCommand(Serial& serial, + unsigned char cmd, + int retries) +{ + for(int retry=0; retry<retries; ++retry) + { + serial.writeByte( cmd ); // Send command + serial.sleep(25); // Allow 25msec lag. + + try { + unsigned char echo = serial.readByte(); + if( echo == cmd || echo == 'M' || echo == 'L' ) + return echo; // Got it, or unknown command... + } + catch(const ReadTimeout&) { + continue; + } + + serial.sleep(100); // Cleanup any pending stuff, + serial.purge(); // and retry... + } + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +HardwareOperations::HardwareDescriptor +HardwareOperations::hardwareDescriptor() +{ + unsigned char echo = 0; + unsigned int hardFeatures = 0xFF; // timeout response... + unsigned int softFeatures = 0; + unsigned int model = 0; + unsigned char ok = 0; + + try { + //---- First: try the new extended hardware query -------------------- + echo = retryCommand(_serial, 0x60, 1); // BACKQUOTE char + if( echo == 0x60 ) { + uchar extendedDescriptor[6]; + _serial.readBlock(extendedDescriptor, sizeof extendedDescriptor); + + + hardFeatures = extendedDescriptor[0] * 256 + extendedDescriptor[1]; + softFeatures = extendedDescriptor[2] * 256 + extendedDescriptor[3]; + model = extendedDescriptor[4]; + ok = extendedDescriptor[5]; + } + else + { + // Did we have a timeout ? + // In that case, some hwOS versions fails and need a reset of + // the connection mode... + if( echo == 0xFF ) + { + echo = retryCommand(_serial, 0xBB); // Try to reconnect + if( echo == 0xBB ) + echo = _serial.readByte(); // Eat 4d prompt + } + + // Then try the OLD hardware descriptor command... + echo = retryCommand(_serial, 'j'); // 0x6A + if( echo == 'j' ) { + hardFeatures = _serial.readByte(); + ok = _serial.readByte(); + } + } + } + catch(const ReadTimeout&) {} + + if( (echo != 0x60 && echo != 'j') + || (ok != 'M' && ok != 'L') + ) { + LOG_TRACE("Old OSTC not responding..."); + return HW_UNKNOWN_OSTC; + } + + switch(hardFeatures) { + case HW_Frog : LOG_TRACE("Frog found"); break; + case HW_OSTCSport_a : LOG_TRACE("OSTC Sport found"); break; + case HW_OSTC2c : LOG_TRACE("OSTC 2c found"); break; + case HW_OSTC2_a: + case HW_OSTC2_c : LOG_TRACE("OSTC 2 found"); break; + case HW_OSTCcR_a: + case HW_OSTCcR_b : LOG_TRACE("OSTC cR found"); break; + case HW_OSTC3 : LOG_TRACE("OSTC 3 found"); break; + case HW_OSTC3p_a : LOG_TRACE("OSTC 3+ found"); break; + case HW_OSTC4 : LOG_TRACE("OSTC 4 found"); break; + + case HW_OSTCSport_b : LOG_TRACE("OSTC Sport, OSTC 2 or OSTC 3 found."); break; + + case 0xFF: case 0x4C: case 0x4D: + LOG_TRACE("old OSTC not responding..."); + return HW_UNKNOWN_OSTC; + + default: + // LOG_TRACE("Unknown hardware feature =" << QString().sprintf("0x%04x", hardFeatures)); + LOG_TRACE("Unknown hardware feature =" << QString::asprintf("0x%04x", hardFeatures)); + break; + } + + if( echo == 0x60 ) { + LOG_TRACE(" software feature = " << QString::asprintf("0x%04x", softFeatures)); + LOG_TRACE(" model = " << QString::asprintf("0x%02x", model)); + } + + return (HardwareDescriptor)hardFeatures; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList HardwareOperations::listBluetoothPorts() const +{ + assert(supported() & BLUETOOTH); + QStringList list; + QString PortDesc; + const auto serialPortInfos = QSerialPortInfo::availablePorts(); + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + // TODO: Linux USB search... + QRegExp pTemplate = portTemplate(); + QDir dev("/dev"); + QStringList all = dev.entryList(QStringList() << "tty.*", + QDir::NoDotAndDotDot|QDir::System|QDir::Writable, + QDir::Name); + + for(int i=0; i<(int)all.count(); ++i) { + if( pTemplate.indexIn(all[i]) >= 0 ) { + LOG_TRACE("Port " << all[i]); + list += all[i]; + } + else + LOG_DEBUG("... " << all[i]); + } +#else + /* Check the descriptors of the available COMs for Bluetooth tag */ + for (const QSerialPortInfo &serialPortInfo : serialPortInfos) { + PortDesc = serialPortInfo.description(); + if( PortDesc.contains("Bluetooth")) + list += serialPortInfo.portName(); + } + + if( list.isEmpty() ) /* no port identified => fallback to old detection function */ + { + for(int i=1; i<300; ++i) + { + QString port = QString("COM%1").arg(i); + + // First: try to read default configuration... + COMMCONFIG config = {0}; + config.dwSize = sizeof config; + config.wVersion = 1; + DWORD len = sizeof config; + + QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); + if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { + if( config.dwProviderSubType == PST_RS232 ) + list += port; + } + } + + //---- Second chance + // overide usual MS bug, by looking into the registry for more + // BLUETOOTH ports... + { + HKEY key; + const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; + if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD + registryPath, // "SOFTWARE\Intel\PSIB" + 0, // Options + KEY_READ, // Desired SAM: See 32bits view. + &key) == ERROR_SUCCESS + ) { + for(DWORD i = 0; ++i;) { + char nameBuffer[128] = {0}; + DWORD nameLen = sizeof nameBuffer; + unsigned char dataBuffer[128] = {0}; + DWORD dataLen = sizeof dataBuffer; + long rc = RegEnumValueA(key, i, + nameBuffer, &nameLen, + nullptr, + nullptr, + dataBuffer, &dataLen); + if( rc != ERROR_SUCCESS ) + break; + + QString name = QString(nameBuffer); + QString port = QString((char*)dataBuffer); + LOG_TRACE("Resource " << i << ": " << name << ", " << port); + if( name.contains("\\BtModem") || name.contains("\\BthModem") || name.contains("\\BtPort") ) { + list += port + " (Bluetooth)"; + LOG_TRACE("Port " << name); + } + else + LOG_DEBUG("... " << name); + } + RegCloseKey(key); + } + } + } +#endif + + return list; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList HardwareOperations::listUSBPorts() const +{ + assert( !(supported() & BLUETOOTH) ); + QStringList list; + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + // TODO: Linux USB search... + QDir dev("/dev"); + QRegExp pTemplate = portTemplate(); + QStringList all = dev.entryList(QStringList() << "tty.*", + QDir::System|QDir::Writable|QDir::NoDotAndDotDot, + QDir::Name); + for(int i=0; i<(int)all.count(); ++i) { + if( pTemplate.indexIn(all[i]) >= 0 ) { + LOG_TRACE("Port " << all[i]); + list += all[i]; + } + else + LOG_TRACE("... " << all[i]); + } +#else + //---- First chance: Try the normal port list: + for(int i=1; i<300; ++i) + { + QString port = QString("COM%1").arg(i); + + // First: try to read default configuration... + COMMCONFIG config; + memset(&config, 0, sizeof config); + config.dwSize = sizeof config; + config.wVersion = 1; + DWORD len = sizeof config; + + QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); + if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { + LOG_TRACE("Port " << port << " subtype=" << int(config.dwProviderSubType) ); + if( config.dwProviderSubType == PST_RS232 ) + list += port; + } else if( len != sizeof config ) + LOG_THROW("Required " << len << " bytes."); + else if( HRESULT rc = GetLastError() ) + if( rc != 87 ) + LOG_TRACE("Port " << port << " error=" << rc ); + + } + //---- Second chance + // overide usual MS bug, by looking into the registry for more + // USB serial ports... + { + HKEY key; + const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; + if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD + registryPath, // path + 0, // Options + KEY_READ, // Desired SAM: See 32bits view. + &key) == ERROR_SUCCESS + ) { + for(DWORD i = 0;; ++i) { + char nameBuffer[128] = {0}; + DWORD nameLen = sizeof nameBuffer; + unsigned char dataBuffer[128] = {0}; + DWORD dataLen = sizeof dataBuffer; + long rc = RegEnumValueA(key, i, + nameBuffer, &nameLen, + nullptr, + nullptr, + dataBuffer, &dataLen); + + if( rc == ERROR_NO_MORE_ITEMS ) + break; + + if( rc != ERROR_SUCCESS ) + LOG_THROW( "Enumeration error" ); + + QString name = QString(nameBuffer); + QString port = QString((char*)dataBuffer); + LOG_TRACE("Resource " << i << ": " << name << ", " << port); + + if( name.contains("\\VCP") ) + list += port + " (USB)"; + } + RegCloseKey(key); + } + } +#endif + + return list; +} + +////////////////////////////////////////////////////////////////////////////// + +//QString HardwareOperations::scanNewPort() +//{ +// static QStringList oldPorts; + +// //---- Get current ports, type-less (ie. strip (USB) or (Bluetooth) ------ +// QStringList newPorts; +// foreach(QString port, listPorts()) +// newPorts += port.section(" ", 0, 0); + +// //---- Simplify all ports not in the list anymore ----------------------- +// for(int p=0; p<oldPorts.count(); ++p) { +// QString port = oldPorts[p]; +// if( ! newPorts.contains(port) ) +// oldPorts.removeAll(port); +// } + +// //---- Check for new port in the list ----------------------------------- +// for(int p=0; p<newPorts.count(); ++p) { +// QString port = newPorts[p]; +// // Did we find a new port ? +// if( ! oldPorts.contains(port) ) { +// oldPorts += port; +// return port; +// } +// } + +// return QString::null; +//}
