view HardwareOperations.cpp @ 4:e30f00f760d3 default tip

Cleanup OSTC label and removed url The computer type will now show OSTC 4/5 instead of only 4. The url has been removed because it is no longer maintained. The ui header have been deleted because they are generated files shich should not be under version controll. Delete locally if you want to force an update of the dialog layout.
author Ideenmodellierer
date Sun, 30 Nov 2025 18:37:32 +0100
parents 0b3630a29ad8
children
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/5 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;
//}