SerialLink.cc 32 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10 11 12
/*=====================================================================
======================================================================*/
/**
 * @file
 *   @brief Cross-platform support for serial ports
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QTimer>
#include <QDebug>
13
#include <QSettings>
pixhawk's avatar
pixhawk committed
14 15 16
#include <QMutexLocker>
#include "SerialLink.h"
#include "LinkManager.h"
17
#include "QGC.h"
pixhawk's avatar
pixhawk committed
18
#include <MG.h>
19
#include <iostream>
pixhawk's avatar
pixhawk committed
20 21 22 23
#ifdef _WIN32
#include "windows.h"
#endif

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
#ifdef _WIN32
#include <qextserialenumerator.h>
#endif
#if defined (__APPLE__) && defined (__MACH__)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <paths.h>
#include <termios.h>
#include <sysexits.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
#include <AvailabilityMacros.h>

#ifdef __MWERKS__
#define __CF_USE_FRAMEWORK_INCLUDES__
#endif


#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
#include <IOKit/serial/ioss.h>
#endif
#include <IOKit/IOBSD.h>

// Apple internal modems default to local echo being on. If your modem has local echo disabled,
// undefine the following macro.
#define LOCAL_ECHO

#define kATCommandString  "AT\r"

#ifdef LOCAL_ECHO
#define kOKResponseString  "AT\r\r\nOK\r\n"
#else
#define kOKResponseString  "\r\nOK\r\n"
#endif
#endif


// Some helper functions for serial port enumeration
#if defined (__APPLE__) && defined (__MACH__)

enum {
    kNumRetries = 3
};

// Function prototypes
static kern_return_t FindModems(io_iterator_t *matchingServices);
static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize);

// Returns an iterator across all known modems. Caller is responsible for
// releasing the iterator when iteration is complete.
static kern_return_t FindModems(io_iterator_t *matchingServices)
{
    kern_return_t      kernResult;
    CFMutableDictionaryRef  classesToMatch;

    /*! @function IOServiceMatching
    @abstract Create a matching dictionary that specifies an IOService class match.
    @discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name.
    @param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass.
    @result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */

    // Serial devices are instances of class IOSerialBSDClient
    classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
    if (classesToMatch == NULL) {
        printf("IOServiceMatching returned a NULL dictionary.\n");
    } else {
        /*!
          @function CFDictionarySetValue
          Sets the value of the key in the dictionary.
          @param theDict The dictionary to which the value is to be set. If this
            parameter is not a valid mutable CFDictionary, the behavior is
            undefined. If the dictionary is a fixed-capacity dictionary and
            it is full before this operation, and the key does not exist in
            the dictionary, the behavior is undefined.
          @param key The key of the value to set into the dictionary. If a key
            which matches this key is already present in the dictionary, only
            the value is changed ("add if absent, replace if present"). If
            no key matches the given key, the key-value pair is added to the
            dictionary. If added, the key is retained by the dictionary,
            using the retain callback provided
            when the dictionary was created. If the key is not of the sort
            expected by the key retain callback, the behavior is undefined.
          @param value The value to add to or replace into the dictionary. The value
            is retained by the dictionary using the retain callback provided
            when the dictionary was created, and the previous value if any is
            released. If the value is not of the sort expected by the
            retain or release callbacks, the behavior is undefined.
        */
        CFDictionarySetValue(classesToMatch,
                             CFSTR(kIOSerialBSDTypeKey),
                             CFSTR(kIOSerialBSDModemType));

        // Each serial device object has a property with key
        // kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes,
        // kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the
        // matching by changing the last parameter in the above call to CFDictionarySetValue.

        // As shipped, this sample is only interested in modems,
        // so add this property to the CFDictionary we're matching on.
        // This will find devices that advertise themselves as modems,
        // such as built-in and USB modems. However, this match won't find serial modems.
    }

    /*! @function IOServiceGetMatchingServices
        @abstract Look up registered IOService objects that match a matching dictionary.
        @discussion This is the preferred method of finding IOService objects currently registered by IOKit. IOServiceAddNotification can also supply this information and install a notification of new IOServices. The matching information used in the matching dictionary may vary depending on the class of service being looked up.
        @param masterPort The master port obtained from IOMasterPort().
        @param matching A CF dictionary containing matching information, of which one reference is consumed by this function. IOKitLib can contruct matching dictionaries for common criteria with helper functions such as IOServiceMatching, IOOpenFirmwarePathMatching.
        @param existing An iterator handle is returned on success, and should be released by the caller when the iteration is finished.
        @result A kern_return_t error code. */

    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices);
    if (KERN_SUCCESS != kernResult) {
        printf("IOServiceGetMatchingServices returned %d\n", kernResult);
        goto exit;
    }

exit:
    return kernResult;
}

/** Given an iterator across a set of modems, return the BSD path to the first one.
 *  If no modems are found the path name is set to an empty string.
 */
static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize)
{
    io_object_t    modemService;
    kern_return_t  kernResult = KERN_FAILURE;
    Boolean      modemFound = false;

    // Initialize the returned path
    *bsdPath = '\0';

    // Iterate across all modems found. In this example, we bail after finding the first modem.

    while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) {
        CFTypeRef  bsdPathAsCFString;

        // Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be
        // used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for
        // incoming calls, e.g. a fax listener.

        bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService,
                            CFSTR(kIOCalloutDeviceKey),
                            kCFAllocatorDefault,
                            0);
        if (bsdPathAsCFString) {
            Boolean result;

            // Convert the path from a CFString to a C (NUL-terminated) string for use
            // with the POSIX open() call.

            result = CFStringGetCString((CFStringRef)bsdPathAsCFString,
                                        bsdPath,
                                        maxPathSize,
                                        kCFStringEncodingUTF8);
            CFRelease(bsdPathAsCFString);

            if (result) {
                //printf("Modem found with BSD path: %s", bsdPath);
                modemFound = true;
                kernResult = KERN_SUCCESS;
            }
        }

        printf("\n");
200

201 202 203 204 205 206 207 208 209 210
        // Release the io_service_t now that we are done with it.

        (void) IOObjectRelease(modemService);
    }

    return kernResult;
}
#endif

using namespace TNX;
pixhawk's avatar
pixhawk committed
211

212 213
SerialLink::SerialLink(QString portname, int baudRate, bool hardwareFlowControl, bool parity,
                       int dataBits, int stopBits) :
214 215
    port(NULL),
    ports(new QVector<QString>())
pixhawk's avatar
pixhawk committed
216 217 218
{
    // Setup settings
    this->porthandle = portname.trimmed();
219 220 221 222 223 224

    if (this->porthandle == "" && getCurrentPorts()->size() > 0)
    {
        this->porthandle = getCurrentPorts()->first().trimmed();
    }

pixhawk's avatar
pixhawk committed
225 226 227
#ifdef _WIN32
    // Port names above 20 need the network path format - if the port name is not already in this format
    // catch this special case
228
    if (this->porthandle.size() > 0 && !this->porthandle.startsWith("\\")) {
pixhawk's avatar
pixhawk committed
229 230 231 232 233 234
        // Append \\.\ before the port handle. Additional backslashes are used for escaping.
        this->porthandle = "\\\\.\\" + this->porthandle;
    }
#endif
    // Set unique ID and add link to the list of links
    this->id = getNextLinkId();
235

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    setBaudRate(baudRate);
    if (hardwareFlowControl)
    {
        portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE);
    }
    else
    {
        portSettings.setFlowControl(QPortSettings::FLOW_OFF);
    }
    if (parity)
    {
        portSettings.setParity(QPortSettings::PAR_EVEN);
    }
    else
    {
        portSettings.setParity(QPortSettings::PAR_NONE);
    }
    setDataBits(dataBits);
    setStopBits(stopBits);
pixhawk's avatar
pixhawk committed
255 256

    // Set the port name
257 258
    if (porthandle == "")
    {
259
        name = tr("Serial Link ") + QString::number(getId());
pixhawk's avatar
pixhawk committed
260
    }
261 262 263
    else
    {
        name = portname.trimmed();
pixhawk's avatar
pixhawk committed
264
    }
lm's avatar
lm committed
265
    loadSettings();
pixhawk's avatar
pixhawk committed
266 267 268 269 270
}

SerialLink::~SerialLink()
{
    disconnect();
271
    if(port) delete port;
pixhawk's avatar
pixhawk committed
272
    port = NULL;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    if (ports) delete ports;
    ports = NULL;
}

QVector<QString>* SerialLink::getCurrentPorts()
{
    ports->clear();
#ifdef __linux

    // TODO Linux has no standard way of enumerating serial ports
    // However the device files are only present when the physical
    // device is connected, therefore listing the files should be
    // sufficient.

    QString devdir = "/dev";
    QDir dir(devdir);
    dir.setFilter(QDir::System);

    QFileInfoList list = dir.entryInfoList();
    for (int i = 0; i < list.size(); ++i) {
        QFileInfo fileInfo = list.at(i);
        if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS")) || fileInfo.fileName().contains(QString("ttyACM")))
        {
            ports->append(fileInfo.canonicalFilePath());
        }
    }
#endif

#if defined (__APPLE__) && defined (__MACH__)

    // Enumerate serial ports
    kern_return_t    kernResult; // on PowerPC this is an int (4 bytes)
    io_iterator_t    serialPortIterator;
    char        bsdPath[MAXPATHLEN];
    kernResult = FindModems(&serialPortIterator);
    kernResult = GetModemPath(serialPortIterator, bsdPath, sizeof(bsdPath));
    IOObjectRelease(serialPortIterator);    // Release the iterator.

    // Add found modems
    if (bsdPath[0])
    {
        ports->append(QString(bsdPath));
    }

    // Add USB serial port adapters
    // TODO Strangely usb serial port adapters are not enumerated, even when connected
    QString devdir = "/dev";
    QDir dir(devdir);
    dir.setFilter(QDir::System);

    QFileInfoList list = dir.entryInfoList();
    for (int i = list.size() - 1; i >= 0; i--) {
        QFileInfo fileInfo = list.at(i);
        if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS")) || fileInfo.fileName().contains(QString("tty.usbserial")) || fileInfo.fileName().contains(QString("ttyACM")))
        {
            ports->append(fileInfo.canonicalFilePath());
        }
    }
#endif

#ifdef _WIN32
    // Get the ports available on this system
    QList<QextPortInfo> ports = QextSerialEnumerator::getPorts();

    // Add the ports in reverse order, because we prepend them to the list
    for (int i = ports.size() - 1; i >= 0; i--)
    {
        QextPortInfo portInfo = ports.at(i);
        QString portString = QString(portInfo.portName.toLocal8Bit().constData()) + " - " + QString(ports.at(i).friendName.toLocal8Bit().constData()).split("(").first();
        // Prepend newly found port to the list
        ports.append(portString);
    }

    //printf("port name: %s\n", ports.at(i).portName.toLocal8Bit().constData());
    //printf("friendly name: %s\n", ports.at(i).friendName.toLocal8Bit().constData());
    //printf("physical name: %s\n", ports.at(i).physName.toLocal8Bit().constData());
    //printf("enumerator name: %s\n", ports.at(i).enumName.toLocal8Bit().constData());
    //printf("===================================\n\n");
#endif
    return ports;
pixhawk's avatar
pixhawk committed
353 354
}

355 356 357 358 359
void SerialLink::loadSettings()
{
    // Load defaults from settings
    QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
    settings.sync();
360 361
    if (settings.contains("SERIALLINK_COMM_PORT"))
    {
362
        if (porthandle == "") setPortName(settings.value("SERIALLINK_COMM_PORT").toString());
363 364
        setBaudRateType(settings.value("SERIALLINK_COMM_BAUD").toInt());
        setParityType(settings.value("SERIALLINK_COMM_PARITY").toInt());
365 366 367
        setStopBits(settings.value("SERIALLINK_COMM_STOPBITS").toInt());
        setDataBits(settings.value("SERIALLINK_COMM_DATABITS").toInt());
        setFlowType(settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt());
368 369 370 371 372 373 374 375 376 377
    }
}

void SerialLink::writeSettings()
{
    // Store settings
    QSettings settings(QGC::COMPANYNAME, QGC::APPNAME);
    settings.setValue("SERIALLINK_COMM_PORT", this->porthandle);
    settings.setValue("SERIALLINK_COMM_BAUD", getBaudRateType());
    settings.setValue("SERIALLINK_COMM_PARITY", getParityType());
378 379 380
    settings.setValue("SERIALLINK_COMM_STOPBITS", getStopBits());
    settings.setValue("SERIALLINK_COMM_DATABITS", getDataBits());
    settings.setValue("SERIALLINK_COMM_FLOW_CONTROL", getFlowType());
381 382 383
    settings.sync();
}

pixhawk's avatar
pixhawk committed
384 385 386 387 388

/**
 * @brief Runs the thread
 *
 **/
389 390
void SerialLink::run()
{
pixhawk's avatar
pixhawk committed
391 392 393 394
    // Initialize the connection
    hardwareConnect();

    // Qt way to make clear what a while(1) loop does
395 396
    forever
    {
pixhawk's avatar
pixhawk committed
397 398 399 400 401 402 403 404 405 406
        // Check if new bytes have arrived, if yes, emit the notification signal
        checkForBytes();
        /* Serial data isn't arriving that fast normally, this saves the thread
                 * from consuming too much processing time
                 */
        MG::SLEEP::msleep(SerialLink::poll_interval);
    }
}


407 408
void SerialLink::checkForBytes()
{
pixhawk's avatar
pixhawk committed
409
    /* Check if bytes are available */
410 411
    if(port && port->isOpen() && port->isWritable())
    {
pixhawk's avatar
pixhawk committed
412 413 414 415
        dataMutex.lock();
        qint64 available = port->bytesAvailable();
        dataMutex.unlock();

416 417
        if(available > 0)
        {
418
            readBytes();
pixhawk's avatar
pixhawk committed
419
        }
420 421 422
    }
    else
    {
pixhawk's avatar
pixhawk committed
423
        emit disconnected();
424
        emit connected(false);
pixhawk's avatar
pixhawk committed
425 426 427 428
    }

}

429 430
void SerialLink::writeBytes(const char* data, qint64 size)
{
431
    if(port && port->isOpen()) {
pixhawk's avatar
pixhawk committed
432 433
        int b = port->write(data, size);

434
        if (b > 0) {
435

436
//            qDebug() << "Serial link " << this->getName() << "transmitted" << b << "bytes:";
pixhawk's avatar
pixhawk committed
437

438 439 440
            // Increase write counter
            bitsSentTotal += size * 8;

441 442 443 444 445 446 447
//            int i;
//            for (i=0; i<size; i++)
//            {
//                unsigned char v =data[i];
//                qDebug("%02x ", v);
//            }
//            qDebug("\n");
448
        } else {
449 450 451
            disconnect();
            // Error occured
            emit communicationError(this->getName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName()));
452
        }
pixhawk's avatar
pixhawk committed
453 454 455 456 457 458 459 460 461
    }
}

/**
 * @brief Read a number of bytes from the interface.
 *
 * @param data Pointer to the data byte array to write the bytes to
 * @param maxLength The maximum number of bytes to write
 **/
462 463
void SerialLink::readBytes()
{
pixhawk's avatar
pixhawk committed
464
    dataMutex.lock();
465
    if(port && port->isOpen()) {
466 467
        const qint64 maxLength = 2048;
        char data[maxLength];
pixhawk's avatar
pixhawk committed
468
        qint64 numBytes = port->bytesAvailable();
James Goppert's avatar
James Goppert committed
469
        //qDebug() << "numBytes: " << numBytes;
470

471
        if(numBytes > 0) {
pixhawk's avatar
pixhawk committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
            /* Read as much data in buffer as possible without overflow */
            if(maxLength < numBytes) numBytes = maxLength;

            port->read(data, numBytes);
            QByteArray b(data, numBytes);
            emit bytesReceived(this, b);

            //qDebug() << "SerialLink::readBytes()" << std::hex << data;
            //            int i;
            //            for (i=0; i<numBytes; i++){
            //                unsigned int v=data[i];
            //
            //                fprintf(stderr,"%02x ", v);
            //            }
            //            fprintf(stderr,"\n");
            bitsReceivedTotal += numBytes * 8;
        }
    }
    dataMutex.unlock();
}


/**
 * @brief Get the number of bytes to read.
 *
 * @return The number of bytes to read
 **/
499 500
qint64 SerialLink::bytesAvailable()
{
501
    if (port) {
502
        return port->bytesAvailable();
503
    } else {
504 505
        return 0;
    }
pixhawk's avatar
pixhawk committed
506 507 508 509 510 511 512
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
513 514
bool SerialLink::disconnect()
{
515
    if (port) {
516 517 518
        //#if !defined _WIN32 || !defined _WIN64
        /* Block the thread until it returns from run() */
        //#endif
519 520
        port->flushInBuffer();
        port->flushOutBuffer();
521 522 523
        port->close();
        delete port;
        port = NULL;
pixhawk's avatar
pixhawk committed
524

525
        if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect
526

527 528
        bool closed = true;
        //port->isOpen();
pixhawk's avatar
pixhawk committed
529

530 531
        emit disconnected();
        emit connected(false);
532
        return closed;
533
    } else {
534 535 536
        // No port, so we're disconnected
        return true;
    }
pixhawk's avatar
pixhawk committed
537 538 539 540 541 542 543 544 545 546

}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
bool SerialLink::connect()
{
547 548
    if (this->isRunning()) this->disconnect();
    this->start(LowPriority);
549
    return true;
pixhawk's avatar
pixhawk committed
550 551 552 553 554 555 556 557 558 559
}

/**
 * @brief This function is called indirectly by the connect() call.
 *
 * The connect() function starts the thread and indirectly calls this method.
 *
 * @return True if the connection could be established, false otherwise
 * @see connect() For the right function to establish the connection.
 **/
560 561
bool SerialLink::hardwareConnect()
{
562
    if(port) {
563 564 565
        port->close();
        delete port;
    }
566 567 568
    port = new QSerialPort(porthandle, portSettings);
    QObject::connect(port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected()));
    port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead);
pixhawk's avatar
pixhawk committed
569 570
    connectionStartTime = MG::TIME::getGroundTimeNow();

571 572
    port->open();

pixhawk's avatar
pixhawk committed
573
    bool connectionUp = isConnected();
574
    if(connectionUp) {
pixhawk's avatar
pixhawk committed
575 576 577 578
        emit connected();
        emit connected(true);
    }

579
    //qDebug() << "CONNECTING LINK: " << __FILE__ << __LINE__ << "with settings" << port->portName() << getBaudRate() << getDataBits() << getParityType() << getStopBits();
580 581


582 583
    writeSettings();

pixhawk's avatar
pixhawk committed
584 585
    return connectionUp;
}
586 587


pixhawk's avatar
pixhawk committed
588 589 590 591 592
/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
593 594
bool SerialLink::isConnected()
{
595
    if (port) {
lm's avatar
lm committed
596
        return port->isOpen();
597
    } else {
lm's avatar
lm committed
598 599
        return false;
    }
pixhawk's avatar
pixhawk committed
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
}

int SerialLink::getId()
{
    return id;
}

QString SerialLink::getName()
{
    return name;
}

void SerialLink::setName(QString name)
{
    this->name = name;
    emit nameChanged(this->name);
}


619 620
qint64 SerialLink::getNominalDataRate()
{
pixhawk's avatar
pixhawk committed
621
    qint64 dataRate = 0;
622
    switch (portSettings.baudRate()) {
623
#ifndef Q_OS_WIN
624
    case QPortSettings::BAUDR_50:
pixhawk's avatar
pixhawk committed
625 626
        dataRate = 50;
        break;
627
    case QPortSettings::BAUDR_75:
pixhawk's avatar
pixhawk committed
628 629
        dataRate = 75;
        break;
630
    case QPortSettings::BAUDR_110:
pixhawk's avatar
pixhawk committed
631 632
        dataRate = 110;
        break;
633
    case QPortSettings::BAUDR_134:
pixhawk's avatar
pixhawk committed
634 635
        dataRate = 134;
        break;
636
    case QPortSettings::BAUDR_150:
pixhawk's avatar
pixhawk committed
637 638
        dataRate = 150;
        break;
639
    case QPortSettings::BAUDR_200:
pixhawk's avatar
pixhawk committed
640 641
        dataRate = 200;
        break;
642
#endif
643
    case QPortSettings::BAUDR_300:
pixhawk's avatar
pixhawk committed
644 645
        dataRate = 300;
        break;
646
    case QPortSettings::BAUDR_600:
pixhawk's avatar
pixhawk committed
647 648
        dataRate = 600;
        break;
649
    case QPortSettings::BAUDR_1200:
pixhawk's avatar
pixhawk committed
650 651
        dataRate = 1200;
        break;
652
#ifndef Q_OS_WIN
653
    case QPortSettings::BAUDR_1800:
pixhawk's avatar
pixhawk committed
654 655
        dataRate = 1800;
        break;
656
#endif
657
    case QPortSettings::BAUDR_2400:
pixhawk's avatar
pixhawk committed
658 659
        dataRate = 2400;
        break;
660
    case QPortSettings::BAUDR_4800:
pixhawk's avatar
pixhawk committed
661 662
        dataRate = 4800;
        break;
663
    case QPortSettings::BAUDR_9600:
pixhawk's avatar
pixhawk committed
664 665
        dataRate = 9600;
        break;
666 667
#ifdef Q_OS_WIN
    case QPortSettings::BAUDR_14400:
pixhawk's avatar
pixhawk committed
668 669
        dataRate = 14400;
        break;
670 671
#endif
    case QPortSettings::BAUDR_19200:
pixhawk's avatar
pixhawk committed
672 673
        dataRate = 19200;
        break;
674
    case QPortSettings::BAUDR_38400:
pixhawk's avatar
pixhawk committed
675 676
        dataRate = 38400;
        break;
677 678
#ifdef Q_OS_WIN
    case QPortSettings::BAUDR_56000:
pixhawk's avatar
pixhawk committed
679 680
        dataRate = 56000;
        break;
681 682
#endif
    case QPortSettings::BAUDR_57600:
pixhawk's avatar
pixhawk committed
683 684
        dataRate = 57600;
        break;
685
#ifdef Q_OS_WIN_XXXX // FIXME
686
    case QPortSettings::BAUDR_76800:
pixhawk's avatar
pixhawk committed
687 688
        dataRate = 76800;
        break;
689 690
#endif
    case QPortSettings::BAUDR_115200:
pixhawk's avatar
pixhawk committed
691 692
        dataRate = 115200;
        break;
693 694 695
#ifdef Q_OS_WIN
        // Windows-specific high-end baudrates
    case QPortSettings::BAUDR_128000:
pixhawk's avatar
pixhawk committed
696 697
        dataRate = 128000;
        break;
698
    case QPortSettings::BAUDR_256000:
pixhawk's avatar
pixhawk committed
699
        dataRate = 256000;
700
    case QPortSettings::BAUDR_230400:
701
        dataRate = 230400;
702
    case QPortSettings::BAUDR_460800:
703
        dataRate = 460800;
704 705 706
#endif
        // All-OS high-speed
    case QPortSettings::BAUDR_921600:
707
        dataRate = 921600;
pixhawk's avatar
pixhawk committed
708
        break;
709 710 711 712
    case QPortSettings::BAUDR_UNKNOWN:
        default:
        // Do nothing
        break;
pixhawk's avatar
pixhawk committed
713 714 715 716
    }
    return dataRate;
}

717 718
qint64 SerialLink::getTotalUpstream()
{
pixhawk's avatar
pixhawk committed
719 720 721 722 723
    statisticsMutex.lock();
    return bitsSentTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000);
    statisticsMutex.unlock();
}

724 725
qint64 SerialLink::getCurrentUpstream()
{
pixhawk's avatar
pixhawk committed
726 727 728
    return 0; // TODO
}

729 730
qint64 SerialLink::getMaxUpstream()
{
pixhawk's avatar
pixhawk committed
731 732 733
    return 0; // TODO
}

734 735
qint64 SerialLink::getBitsSent()
{
pixhawk's avatar
pixhawk committed
736 737 738
    return bitsSentTotal;
}

739 740
qint64 SerialLink::getBitsReceived()
{
pixhawk's avatar
pixhawk committed
741 742 743
    return bitsReceivedTotal;
}

744 745
qint64 SerialLink::getTotalDownstream()
{
pixhawk's avatar
pixhawk committed
746 747 748 749 750
    statisticsMutex.lock();
    return bitsReceivedTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000);
    statisticsMutex.unlock();
}

751 752
qint64 SerialLink::getCurrentDownstream()
{
pixhawk's avatar
pixhawk committed
753 754 755
    return 0; // TODO
}

756 757
qint64 SerialLink::getMaxDownstream()
{
pixhawk's avatar
pixhawk committed
758 759 760
    return 0; // TODO
}

761 762
bool SerialLink::isFullDuplex()
{
pixhawk's avatar
pixhawk committed
763 764 765 766
    /* Serial connections are always half duplex */
    return false;
}

767 768
int SerialLink::getLinkQuality()
{
pixhawk's avatar
pixhawk committed
769 770 771 772
    /* This feature is not supported with this interface */
    return -1;
}

773 774
QString SerialLink::getPortName()
{
pixhawk's avatar
pixhawk committed
775 776 777
    return porthandle;
}

778 779
int SerialLink::getBaudRate()
{
pixhawk's avatar
pixhawk committed
780 781 782
    return getNominalDataRate();
}

783 784
int SerialLink::getBaudRateType()
{
785
    return portSettings.baudRate();
pixhawk's avatar
pixhawk committed
786 787
}

788 789
int SerialLink::getFlowType()
{
790
    return portSettings.flowControl();
pixhawk's avatar
pixhawk committed
791 792
}

793 794
int SerialLink::getParityType()
{
795
    return portSettings.parity();
pixhawk's avatar
pixhawk committed
796 797
}

798 799
int SerialLink::getDataBitsType()
{
800
    return portSettings.dataBits();
pixhawk's avatar
pixhawk committed
801 802
}

803 804
int SerialLink::getStopBitsType()
{
805
    return portSettings.stopBits();
pixhawk's avatar
pixhawk committed
806 807
}

808 809
int SerialLink::getDataBits()
{
810 811 812
    int ret = -1;
    switch (portSettings.dataBits()) {
    case QPortSettings::DB_5:
813 814
        ret = 5;
        break;
815
    case QPortSettings::DB_6:
816 817
        ret = 6;
        break;
818
    case QPortSettings::DB_7:
819 820
        ret = 7;
        break;
821
    case QPortSettings::DB_8:
822 823 824
        ret = 8;
        break;
    default:
825
        ret = -1;
826 827 828 829 830 831 832
        break;
    }
    return ret;
}

int SerialLink::getStopBits()
{
833 834 835 836 837 838 839 840 841 842 843 844
    int ret = -1;
        switch (portSettings.stopBits()) {
        case QPortSettings::STOP_1:
            ret = 1;
            break;
        case QPortSettings::STOP_2:
            ret = 2;
            break;
        default:
            ret = -1;
            break;
        }
845 846 847
    return ret;
}

pixhawk's avatar
pixhawk committed
848 849
bool SerialLink::setPortName(QString portName)
{
850
    if(portName.trimmed().length() > 0) {
pixhawk's avatar
pixhawk committed
851
        bool reconnect = false;
852 853 854
        if (isConnected()) reconnect = true;
        disconnect();

pixhawk's avatar
pixhawk committed
855 856 857 858 859
        this->porthandle = portName.trimmed();
        setName(tr("serial port ") + portName.trimmed());
#ifdef _WIN32
        // Port names above 20 need the network path format - if the port name is not already in this format
        // catch this special case
860
        if (!this->porthandle.startsWith("\\")) {
pixhawk's avatar
pixhawk committed
861 862 863 864
            // Append \\.\ before the port handle. Additional backslashes are used for escaping.
            this->porthandle = "\\\\.\\" + this->porthandle;
        }
#endif
865

pixhawk's avatar
pixhawk committed
866 867
        if(reconnect) connect();
        return true;
868
    } else {
pixhawk's avatar
pixhawk committed
869 870 871 872 873 874 875 876 877
        return false;
    }
}


bool SerialLink::setBaudRateType(int rateIndex)
{
    bool reconnect = false;
    bool accepted = true; // This is changed if none of the data rates matches
878 879 880
    if(isConnected()) reconnect = true;
    disconnect();

881 882 883 884 885 886 887
#ifdef Q_OS_WIN
	const int minBaud = (int)QPortSettings::BAUDR_14400;
#else
	const int minBaud = (int)QPortSettings::BAUDR_50;
#endif

    if (rateIndex >= minBaud && rateIndex <= (int)QPortSettings::BAUDR_921600)
888 889
    {
        portSettings.setBaudRate((QPortSettings::BaudRate)rateIndex);
pixhawk's avatar
pixhawk committed
890 891 892 893 894 895
    }

    if(reconnect) connect();
    return accepted;
}

896 897 898 899 900 901 902
bool SerialLink::setBaudRateString(const QString& rate)
{
    bool ok;
    int intrate = rate.toInt(&ok);
    if (!ok) return false;
    return setBaudRate(intrate);
}
pixhawk's avatar
pixhawk committed
903 904 905

bool SerialLink::setBaudRate(int rate)
{
906
    //qDebug() << "BAUD RATE:" << rate;
907

pixhawk's avatar
pixhawk committed
908 909
    bool reconnect = false;
    bool accepted = true; // This is changed if none of the data rates matches
910
    if(isConnected()) {
pixhawk's avatar
pixhawk committed
911 912
        reconnect = true;
    }
913
    disconnect();
pixhawk's avatar
pixhawk committed
914

915
    switch (rate) {
916 917

#ifndef Q_OS_WIN
pixhawk's avatar
pixhawk committed
918
    case 50:
919
        portSettings.setBaudRate(QPortSettings::BAUDR_50);
pixhawk's avatar
pixhawk committed
920 921
        break;
    case 75:
922
        portSettings.setBaudRate(QPortSettings::BAUDR_75);
pixhawk's avatar
pixhawk committed
923 924
        break;
    case 110:
925
        portSettings.setBaudRate(QPortSettings::BAUDR_110);
pixhawk's avatar
pixhawk committed
926 927
        break;
    case 134:
928
        portSettings.setBaudRate(QPortSettings::BAUDR_134);
pixhawk's avatar
pixhawk committed
929 930
        break;
    case 150:
931
        portSettings.setBaudRate(QPortSettings::BAUDR_150);
pixhawk's avatar
pixhawk committed
932 933
        break;
    case 200:
934
        portSettings.setBaudRate(QPortSettings::BAUDR_200);
pixhawk's avatar
pixhawk committed
935
        break;
936
#endif
pixhawk's avatar
pixhawk committed
937
    case 300:
938
        portSettings.setBaudRate(QPortSettings::BAUDR_300);
pixhawk's avatar
pixhawk committed
939 940
        break;
    case 600:
941
        portSettings.setBaudRate(QPortSettings::BAUDR_600);
pixhawk's avatar
pixhawk committed
942 943
        break;
    case 1200:
944
        portSettings.setBaudRate(QPortSettings::BAUDR_1200);
pixhawk's avatar
pixhawk committed
945
        break;
946
#ifndef Q_OS_WIN
pixhawk's avatar
pixhawk committed
947
    case 1800:
948
        portSettings.setBaudRate(QPortSettings::BAUDR_1800);
pixhawk's avatar
pixhawk committed
949
        break;
950
#endif
pixhawk's avatar
pixhawk committed
951
    case 2400:
952
        portSettings.setBaudRate(QPortSettings::BAUDR_2400);
pixhawk's avatar
pixhawk committed
953 954
        break;
    case 4800:
955
        portSettings.setBaudRate(QPortSettings::BAUDR_4800);
pixhawk's avatar
pixhawk committed
956 957
        break;
    case 9600:
958
        portSettings.setBaudRate(QPortSettings::BAUDR_9600);
pixhawk's avatar
pixhawk committed
959
        break;
960
#ifdef Q_OS_WIN
pixhawk's avatar
pixhawk committed
961
    case 14400:
962
        portSettings.setBaudRate(QPortSettings::BAUDR_14400);
pixhawk's avatar
pixhawk committed
963
        break;
964
#endif
pixhawk's avatar
pixhawk committed
965
    case 19200:
966
        portSettings.setBaudRate(QPortSettings::BAUDR_19200);
pixhawk's avatar
pixhawk committed
967 968
        break;
    case 38400:
969
        portSettings.setBaudRate(QPortSettings::BAUDR_38400);
pixhawk's avatar
pixhawk committed
970
        break;
971
#ifdef Q_OS_WIN
pixhawk's avatar
pixhawk committed
972
    case 56000:
973
        portSettings.setBaudRate(QPortSettings::BAUDR_56000);
pixhawk's avatar
pixhawk committed
974
        break;
975
#endif
pixhawk's avatar
pixhawk committed
976
    case 57600:
977
        portSettings.setBaudRate(QPortSettings::BAUDR_57600);
pixhawk's avatar
pixhawk committed
978
        break;
979
#ifdef Q_OS_WIN_XXXX // FIXME CHECK THIS
pixhawk's avatar
pixhawk committed
980
    case 76800:
981
        portSettings.setBaudRate(QPortSettings::BAUDR_76800);
pixhawk's avatar
pixhawk committed
982
        break;
983
#endif
pixhawk's avatar
pixhawk committed
984
    case 115200:
985
        portSettings.setBaudRate(QPortSettings::BAUDR_115200);
pixhawk's avatar
pixhawk committed
986
        break;
987
#ifdef Q_OS_WIN
pixhawk's avatar
pixhawk committed
988
    case 128000:
989
        portSettings.setBaudRate(QPortSettings::BAUDR_128000);
pixhawk's avatar
pixhawk committed
990
        break;
991
    case 230400:
992
        portSettings.setBaudRate(QPortSettings::BAUDR_230400);
993
        break;
pixhawk's avatar
pixhawk committed
994
    case 256000:
995
        portSettings.setBaudRate(QPortSettings::BAUDR_256000);
pixhawk's avatar
pixhawk committed
996
        break;
997
    case 460800:
998
        portSettings.setBaudRate(QPortSettings::BAUDR_460800);
999
        break;
1000
#endif
1001
    case 921600:
1002
        portSettings.setBaudRate(QPortSettings::BAUDR_921600);
1003
        break;
pixhawk's avatar
pixhawk committed
1004 1005 1006 1007 1008 1009
    default:
        // If none of the above cases matches, there must be an error
        accepted = false;
        break;
    }

1010 1011 1012
    if(reconnect) connect();
    return accepted;

pixhawk's avatar
pixhawk committed
1013 1014
}

1015 1016
bool SerialLink::setFlowType(int flow)
{
pixhawk's avatar
pixhawk committed
1017 1018
    bool reconnect = false;
    bool accepted = true;
1019 1020
    if(isConnected()) reconnect = true;
    disconnect();
pixhawk's avatar
pixhawk committed
1021

1022
    switch (flow) {
1023 1024
    case (int)QPortSettings::FLOW_OFF:
        portSettings.setFlowControl(QPortSettings::FLOW_OFF);
pixhawk's avatar
pixhawk committed
1025
        break;
1026 1027
    case (int)QPortSettings::FLOW_HARDWARE:
        portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE);
pixhawk's avatar
pixhawk committed
1028
        break;
1029 1030
    case (int)QPortSettings::FLOW_XONXOFF:
        portSettings.setFlowControl(QPortSettings::FLOW_XONXOFF);
pixhawk's avatar
pixhawk committed
1031 1032 1033 1034 1035 1036
        break;
    default:
        // If none of the above cases matches, there must be an error
        accepted = false;
        break;
    }
1037

pixhawk's avatar
pixhawk committed
1038 1039 1040 1041
    if(reconnect) connect();
    return accepted;
}

1042 1043
bool SerialLink::setParityType(int parity)
{
pixhawk's avatar
pixhawk committed
1044 1045
    bool reconnect = false;
    bool accepted = true;
1046 1047
    if (isConnected()) reconnect = true;
    disconnect();
pixhawk's avatar
pixhawk committed
1048

1049
    switch (parity) {
1050 1051
    case (int)QPortSettings::PAR_NONE:
        portSettings.setParity(QPortSettings::PAR_NONE);
pixhawk's avatar
pixhawk committed
1052
        break;
1053 1054
    case (int)QPortSettings::PAR_ODD:
        portSettings.setParity(QPortSettings::PAR_ODD);
pixhawk's avatar
pixhawk committed
1055
        break;
1056 1057
    case (int)QPortSettings::PAR_EVEN:
        portSettings.setParity(QPortSettings::PAR_EVEN);
pixhawk's avatar
pixhawk committed
1058
        break;
1059 1060
    case (int)QPortSettings::PAR_SPACE:
        portSettings.setParity(QPortSettings::PAR_SPACE);
pixhawk's avatar
pixhawk committed
1061 1062 1063 1064 1065 1066 1067
        break;
    default:
        // If none of the above cases matches, there must be an error
        accepted = false;
        break;
    }

1068
    if (reconnect) connect();
pixhawk's avatar
pixhawk committed
1069 1070 1071
    return accepted;
}

1072

1073
bool SerialLink::setDataBits(int dataBits)
1074
{
1075
    //qDebug() << "Setting" << dataBits << "data bits";
1076 1077
    bool reconnect = false;
    if (isConnected()) reconnect = true;
pixhawk's avatar
pixhawk committed
1078
    bool accepted = true;
1079
    disconnect();
pixhawk's avatar
pixhawk committed
1080

1081
    switch (dataBits) {
pixhawk's avatar
pixhawk committed
1082
    case 5:
1083
        portSettings.setDataBits(QPortSettings::DB_5);
pixhawk's avatar
pixhawk committed
1084 1085
        break;
    case 6:
1086
        portSettings.setDataBits(QPortSettings::DB_6);
pixhawk's avatar
pixhawk committed
1087 1088
        break;
    case 7:
1089
        portSettings.setDataBits(QPortSettings::DB_7);
pixhawk's avatar
pixhawk committed
1090 1091
        break;
    case 8:
1092
        portSettings.setDataBits(QPortSettings::DB_8);
pixhawk's avatar
pixhawk committed
1093 1094 1095 1096 1097 1098 1099
        break;
    default:
        // If none of the above cases matches, there must be an error
        accepted = false;
        break;
    }

1100
    if(reconnect) connect();
pixhawk's avatar
pixhawk committed
1101 1102 1103 1104

    return accepted;
}

1105
bool SerialLink::setStopBits(int stopBits)
1106
{
pixhawk's avatar
pixhawk committed
1107 1108
    bool reconnect = false;
    bool accepted = true;
1109 1110
    if(isConnected()) reconnect = true;
    disconnect();
pixhawk's avatar
pixhawk committed
1111

1112
    switch (stopBits) {
pixhawk's avatar
pixhawk committed
1113
    case 1:
1114
        portSettings.setStopBits(QPortSettings::STOP_1);
pixhawk's avatar
pixhawk committed
1115 1116
        break;
    case 2:
1117
        portSettings.setStopBits(QPortSettings::STOP_2);
pixhawk's avatar
pixhawk committed
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
        break;
    default:
        // If none of the above cases matches, there must be an error
        accepted = false;
        break;
    }

    if(reconnect) connect();
    return accepted;
}
1128 1129 1130 1131 1132 1133

bool SerialLink::setDataBitsType(int dataBits)
{
    bool reconnect = false;
    bool accepted = false;

1134 1135
    if (isConnected()) reconnect = true;
    disconnect();
1136

1137 1138
    if (dataBits >= (int)QPortSettings::DB_5 && dataBits <= (int)QPortSettings::DB_8) {
        portSettings.setDataBits((QPortSettings::DataBits) dataBits);
1139

1140
        if(reconnect) connect();
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
        accepted = true;
    }

    return accepted;
}

bool SerialLink::setStopBitsType(int stopBits)
{
    bool reconnect = false;
    bool accepted = false;
1151 1152
    if(isConnected()) reconnect = true;
    disconnect();
1153

1154 1155
    if (stopBits >= (int)QPortSettings::STOP_1 && stopBits <= (int)QPortSettings::STOP_2) {
        portSettings.setStopBits((QPortSettings::StopBits) stopBits);
1156

1157
        if(reconnect) connect();
1158 1159 1160 1161 1162 1163
        accepted = true;
    }

    if(reconnect) connect();
    return accepted;
}