Mercurial > public > ostc_companion
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:76ccd6ce50c0 | 1:0b3630a29ad8 |
|---|---|
| 1 ///////////////////////////////////////////////////////////////////////////// | |
| 2 /// \file HardwareOperations.cpp | |
| 3 /// \brief Abstract operations for HW dive computers. | |
| 4 /// \author JD Gascuel. | |
| 5 /// \sa OSTC3Operations.cpp | |
| 6 /// | |
| 7 /// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. | |
| 8 /// $Id$ | |
| 9 /////////////////////////////////////////////////////////////////////////////// | |
| 10 // | |
| 11 // BSD 2-Clause License: | |
| 12 // | |
| 13 // Redistribution and use in source and binary forms, with or without | |
| 14 // modification, are permitted provided that the following conditions | |
| 15 // are met: | |
| 16 // | |
| 17 // 1. Redistributions of source code must retain the above copyright notice, | |
| 18 // this list of conditions and the following disclaimer. | |
| 19 // | |
| 20 // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 21 // this list of conditions and the following disclaimer in the documentation | |
| 22 // and/or other materials provided with the distribution. | |
| 23 // | |
| 24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 25 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 26 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 27 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 28 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 29 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 30 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 31 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 32 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 33 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| 34 // THE POSSIBILITY OF SUCH DAMAGE. | |
| 35 // | |
| 36 ////////////////////////////////////////////////////////////////////////////// | |
| 37 | |
| 38 #include "HardwareOperations.h" | |
| 39 #include "MainWindow.h" | |
| 40 #include "Utils/Log.h" | |
| 41 #include <QSerialPortInfo> | |
| 42 | |
| 43 #ifdef WIN32 | |
| 44 # include <winreg.h> | |
| 45 #endif | |
| 46 | |
| 47 #include <QDir> | |
| 48 | |
| 49 ////////////////////////////////////////////////////////////////////////////// | |
| 50 | |
| 51 unsigned char HardwareOperations::retryCommand(Serial& serial, | |
| 52 unsigned char cmd, | |
| 53 int retries) | |
| 54 { | |
| 55 for(int retry=0; retry<retries; ++retry) | |
| 56 { | |
| 57 serial.writeByte( cmd ); // Send command | |
| 58 serial.sleep(25); // Allow 25msec lag. | |
| 59 | |
| 60 try { | |
| 61 unsigned char echo = serial.readByte(); | |
| 62 if( echo == cmd || echo == 'M' || echo == 'L' ) | |
| 63 return echo; // Got it, or unknown command... | |
| 64 } | |
| 65 catch(const ReadTimeout&) { | |
| 66 continue; | |
| 67 } | |
| 68 | |
| 69 serial.sleep(100); // Cleanup any pending stuff, | |
| 70 serial.purge(); // and retry... | |
| 71 } | |
| 72 | |
| 73 return 0xFF; | |
| 74 } | |
| 75 | |
| 76 ////////////////////////////////////////////////////////////////////////////// | |
| 77 | |
| 78 HardwareOperations::HardwareDescriptor | |
| 79 HardwareOperations::hardwareDescriptor() | |
| 80 { | |
| 81 unsigned char echo = 0; | |
| 82 unsigned int hardFeatures = 0xFF; // timeout response... | |
| 83 unsigned int softFeatures = 0; | |
| 84 unsigned int model = 0; | |
| 85 unsigned char ok = 0; | |
| 86 | |
| 87 try { | |
| 88 //---- First: try the new extended hardware query -------------------- | |
| 89 echo = retryCommand(_serial, 0x60, 1); // BACKQUOTE char | |
| 90 if( echo == 0x60 ) { | |
| 91 uchar extendedDescriptor[6]; | |
| 92 _serial.readBlock(extendedDescriptor, sizeof extendedDescriptor); | |
| 93 | |
| 94 | |
| 95 hardFeatures = extendedDescriptor[0] * 256 + extendedDescriptor[1]; | |
| 96 softFeatures = extendedDescriptor[2] * 256 + extendedDescriptor[3]; | |
| 97 model = extendedDescriptor[4]; | |
| 98 ok = extendedDescriptor[5]; | |
| 99 } | |
| 100 else | |
| 101 { | |
| 102 // Did we have a timeout ? | |
| 103 // In that case, some hwOS versions fails and need a reset of | |
| 104 // the connection mode... | |
| 105 if( echo == 0xFF ) | |
| 106 { | |
| 107 echo = retryCommand(_serial, 0xBB); // Try to reconnect | |
| 108 if( echo == 0xBB ) | |
| 109 echo = _serial.readByte(); // Eat 4d prompt | |
| 110 } | |
| 111 | |
| 112 // Then try the OLD hardware descriptor command... | |
| 113 echo = retryCommand(_serial, 'j'); // 0x6A | |
| 114 if( echo == 'j' ) { | |
| 115 hardFeatures = _serial.readByte(); | |
| 116 ok = _serial.readByte(); | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 catch(const ReadTimeout&) {} | |
| 121 | |
| 122 if( (echo != 0x60 && echo != 'j') | |
| 123 || (ok != 'M' && ok != 'L') | |
| 124 ) { | |
| 125 LOG_TRACE("Old OSTC not responding..."); | |
| 126 return HW_UNKNOWN_OSTC; | |
| 127 } | |
| 128 | |
| 129 switch(hardFeatures) { | |
| 130 case HW_Frog : LOG_TRACE("Frog found"); break; | |
| 131 case HW_OSTCSport_a : LOG_TRACE("OSTC Sport found"); break; | |
| 132 case HW_OSTC2c : LOG_TRACE("OSTC 2c found"); break; | |
| 133 case HW_OSTC2_a: | |
| 134 case HW_OSTC2_c : LOG_TRACE("OSTC 2 found"); break; | |
| 135 case HW_OSTCcR_a: | |
| 136 case HW_OSTCcR_b : LOG_TRACE("OSTC cR found"); break; | |
| 137 case HW_OSTC3 : LOG_TRACE("OSTC 3 found"); break; | |
| 138 case HW_OSTC3p_a : LOG_TRACE("OSTC 3+ found"); break; | |
| 139 case HW_OSTC4 : LOG_TRACE("OSTC 4 found"); break; | |
| 140 | |
| 141 case HW_OSTCSport_b : LOG_TRACE("OSTC Sport, OSTC 2 or OSTC 3 found."); break; | |
| 142 | |
| 143 case 0xFF: case 0x4C: case 0x4D: | |
| 144 LOG_TRACE("old OSTC not responding..."); | |
| 145 return HW_UNKNOWN_OSTC; | |
| 146 | |
| 147 default: | |
| 148 // LOG_TRACE("Unknown hardware feature =" << QString().sprintf("0x%04x", hardFeatures)); | |
| 149 LOG_TRACE("Unknown hardware feature =" << QString::asprintf("0x%04x", hardFeatures)); | |
| 150 break; | |
| 151 } | |
| 152 | |
| 153 if( echo == 0x60 ) { | |
| 154 LOG_TRACE(" software feature = " << QString::asprintf("0x%04x", softFeatures)); | |
| 155 LOG_TRACE(" model = " << QString::asprintf("0x%02x", model)); | |
| 156 } | |
| 157 | |
| 158 return (HardwareDescriptor)hardFeatures; | |
| 159 } | |
| 160 | |
| 161 ////////////////////////////////////////////////////////////////////////////// | |
| 162 | |
| 163 QStringList HardwareOperations::listBluetoothPorts() const | |
| 164 { | |
| 165 assert(supported() & BLUETOOTH); | |
| 166 QStringList list; | |
| 167 QString PortDesc; | |
| 168 const auto serialPortInfos = QSerialPortInfo::availablePorts(); | |
| 169 | |
| 170 #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) | |
| 171 // TODO: Linux USB search... | |
| 172 QRegExp pTemplate = portTemplate(); | |
| 173 QDir dev("/dev"); | |
| 174 QStringList all = dev.entryList(QStringList() << "tty.*", | |
| 175 QDir::NoDotAndDotDot|QDir::System|QDir::Writable, | |
| 176 QDir::Name); | |
| 177 | |
| 178 for(int i=0; i<(int)all.count(); ++i) { | |
| 179 if( pTemplate.indexIn(all[i]) >= 0 ) { | |
| 180 LOG_TRACE("Port " << all[i]); | |
| 181 list += all[i]; | |
| 182 } | |
| 183 else | |
| 184 LOG_DEBUG("... " << all[i]); | |
| 185 } | |
| 186 #else | |
| 187 /* Check the descriptors of the available COMs for Bluetooth tag */ | |
| 188 for (const QSerialPortInfo &serialPortInfo : serialPortInfos) { | |
| 189 PortDesc = serialPortInfo.description(); | |
| 190 if( PortDesc.contains("Bluetooth")) | |
| 191 list += serialPortInfo.portName(); | |
| 192 } | |
| 193 | |
| 194 if( list.isEmpty() ) /* no port identified => fallback to old detection function */ | |
| 195 { | |
| 196 for(int i=1; i<300; ++i) | |
| 197 { | |
| 198 QString port = QString("COM%1").arg(i); | |
| 199 | |
| 200 // First: try to read default configuration... | |
| 201 COMMCONFIG config = {0}; | |
| 202 config.dwSize = sizeof config; | |
| 203 config.wVersion = 1; | |
| 204 DWORD len = sizeof config; | |
| 205 | |
| 206 QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); | |
| 207 if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { | |
| 208 if( config.dwProviderSubType == PST_RS232 ) | |
| 209 list += port; | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 //---- Second chance | |
| 214 // overide usual MS bug, by looking into the registry for more | |
| 215 // BLUETOOTH ports... | |
| 216 { | |
| 217 HKEY key; | |
| 218 const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; | |
| 219 if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD | |
| 220 registryPath, // "SOFTWARE\Intel\PSIB" | |
| 221 0, // Options | |
| 222 KEY_READ, // Desired SAM: See 32bits view. | |
| 223 &key) == ERROR_SUCCESS | |
| 224 ) { | |
| 225 for(DWORD i = 0; ++i;) { | |
| 226 char nameBuffer[128] = {0}; | |
| 227 DWORD nameLen = sizeof nameBuffer; | |
| 228 unsigned char dataBuffer[128] = {0}; | |
| 229 DWORD dataLen = sizeof dataBuffer; | |
| 230 long rc = RegEnumValueA(key, i, | |
| 231 nameBuffer, &nameLen, | |
| 232 nullptr, | |
| 233 nullptr, | |
| 234 dataBuffer, &dataLen); | |
| 235 if( rc != ERROR_SUCCESS ) | |
| 236 break; | |
| 237 | |
| 238 QString name = QString(nameBuffer); | |
| 239 QString port = QString((char*)dataBuffer); | |
| 240 LOG_TRACE("Resource " << i << ": " << name << ", " << port); | |
| 241 if( name.contains("\\BtModem") || name.contains("\\BthModem") || name.contains("\\BtPort") ) { | |
| 242 list += port + " (Bluetooth)"; | |
| 243 LOG_TRACE("Port " << name); | |
| 244 } | |
| 245 else | |
| 246 LOG_DEBUG("... " << name); | |
| 247 } | |
| 248 RegCloseKey(key); | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 #endif | |
| 253 | |
| 254 return list; | |
| 255 } | |
| 256 | |
| 257 ////////////////////////////////////////////////////////////////////////////// | |
| 258 | |
| 259 QStringList HardwareOperations::listUSBPorts() const | |
| 260 { | |
| 261 assert( !(supported() & BLUETOOTH) ); | |
| 262 QStringList list; | |
| 263 | |
| 264 #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) | |
| 265 // TODO: Linux USB search... | |
| 266 QDir dev("/dev"); | |
| 267 QRegExp pTemplate = portTemplate(); | |
| 268 QStringList all = dev.entryList(QStringList() << "tty.*", | |
| 269 QDir::System|QDir::Writable|QDir::NoDotAndDotDot, | |
| 270 QDir::Name); | |
| 271 for(int i=0; i<(int)all.count(); ++i) { | |
| 272 if( pTemplate.indexIn(all[i]) >= 0 ) { | |
| 273 LOG_TRACE("Port " << all[i]); | |
| 274 list += all[i]; | |
| 275 } | |
| 276 else | |
| 277 LOG_TRACE("... " << all[i]); | |
| 278 } | |
| 279 #else | |
| 280 //---- First chance: Try the normal port list: | |
| 281 for(int i=1; i<300; ++i) | |
| 282 { | |
| 283 QString port = QString("COM%1").arg(i); | |
| 284 | |
| 285 // First: try to read default configuration... | |
| 286 COMMCONFIG config; | |
| 287 memset(&config, 0, sizeof config); | |
| 288 config.dwSize = sizeof config; | |
| 289 config.wVersion = 1; | |
| 290 DWORD len = sizeof config; | |
| 291 | |
| 292 QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); | |
| 293 if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { | |
| 294 LOG_TRACE("Port " << port << " subtype=" << int(config.dwProviderSubType) ); | |
| 295 if( config.dwProviderSubType == PST_RS232 ) | |
| 296 list += port; | |
| 297 } else if( len != sizeof config ) | |
| 298 LOG_THROW("Required " << len << " bytes."); | |
| 299 else if( HRESULT rc = GetLastError() ) | |
| 300 if( rc != 87 ) | |
| 301 LOG_TRACE("Port " << port << " error=" << rc ); | |
| 302 | |
| 303 } | |
| 304 //---- Second chance | |
| 305 // overide usual MS bug, by looking into the registry for more | |
| 306 // USB serial ports... | |
| 307 { | |
| 308 HKEY key; | |
| 309 const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; | |
| 310 if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD | |
| 311 registryPath, // path | |
| 312 0, // Options | |
| 313 KEY_READ, // Desired SAM: See 32bits view. | |
| 314 &key) == ERROR_SUCCESS | |
| 315 ) { | |
| 316 for(DWORD i = 0;; ++i) { | |
| 317 char nameBuffer[128] = {0}; | |
| 318 DWORD nameLen = sizeof nameBuffer; | |
| 319 unsigned char dataBuffer[128] = {0}; | |
| 320 DWORD dataLen = sizeof dataBuffer; | |
| 321 long rc = RegEnumValueA(key, i, | |
| 322 nameBuffer, &nameLen, | |
| 323 nullptr, | |
| 324 nullptr, | |
| 325 dataBuffer, &dataLen); | |
| 326 | |
| 327 if( rc == ERROR_NO_MORE_ITEMS ) | |
| 328 break; | |
| 329 | |
| 330 if( rc != ERROR_SUCCESS ) | |
| 331 LOG_THROW( "Enumeration error" ); | |
| 332 | |
| 333 QString name = QString(nameBuffer); | |
| 334 QString port = QString((char*)dataBuffer); | |
| 335 LOG_TRACE("Resource " << i << ": " << name << ", " << port); | |
| 336 | |
| 337 if( name.contains("\\VCP") ) | |
| 338 list += port + " (USB)"; | |
| 339 } | |
| 340 RegCloseKey(key); | |
| 341 } | |
| 342 } | |
| 343 #endif | |
| 344 | |
| 345 return list; | |
| 346 } | |
| 347 | |
| 348 ////////////////////////////////////////////////////////////////////////////// | |
| 349 | |
| 350 //QString HardwareOperations::scanNewPort() | |
| 351 //{ | |
| 352 // static QStringList oldPorts; | |
| 353 | |
| 354 // //---- Get current ports, type-less (ie. strip (USB) or (Bluetooth) ------ | |
| 355 // QStringList newPorts; | |
| 356 // foreach(QString port, listPorts()) | |
| 357 // newPorts += port.section(" ", 0, 0); | |
| 358 | |
| 359 // //---- Simplify all ports not in the list anymore ----------------------- | |
| 360 // for(int p=0; p<oldPorts.count(); ++p) { | |
| 361 // QString port = oldPorts[p]; | |
| 362 // if( ! newPorts.contains(port) ) | |
| 363 // oldPorts.removeAll(port); | |
| 364 // } | |
| 365 | |
| 366 // //---- Check for new port in the list ----------------------------------- | |
| 367 // for(int p=0; p<newPorts.count(); ++p) { | |
| 368 // QString port = newPorts[p]; | |
| 369 // // Did we find a new port ? | |
| 370 // if( ! oldPorts.contains(port) ) { | |
| 371 // oldPorts += port; | |
| 372 // return port; | |
| 373 // } | |
| 374 // } | |
| 375 | |
| 376 // return QString::null; | |
| 377 //} |
