Mercurial > public > ostc_companion
view 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 source
///////////////////////////////////////////////////////////////////////////// /// \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; //}
