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 //}