termioshelper.cpp 20.7 KB
Newer Older
James Goppert's avatar
James Goppert committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * Unofficial Qt Serial Port Library
 *
 * Copyright (c) 2010 Inbiza Systems Inc. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 *
 * @file termioshelper.cpp
 */

#include <QDebug>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "termioshelper.h"

namespace TNX {

/*!
  Constructs a TermiosHelper object with the given \a file descriptor.
*/

TermiosHelper::TermiosHelper(int fileDescriptor)
36
    : fileDescriptor_(fileDescriptor), originalAttrs_(NULL), currentAttrs_(NULL)
James Goppert's avatar
James Goppert committed
37
{
38
    Q_ASSERT(fileDescriptor_ > 0);
James Goppert's avatar
James Goppert committed
39

40 41
    originalAttrs_ = new termios();
    currentAttrs_ = new termios();
James Goppert's avatar
James Goppert committed
42

43 44
    // save the current serial port attributes
    // see restoreTermios()
James Goppert's avatar
James Goppert committed
45

46
    saveTermios();
James Goppert's avatar
James Goppert committed
47

48
    // clone the original attributes
James Goppert's avatar
James Goppert committed
49

50
    *currentAttrs_ = *originalAttrs_;
James Goppert's avatar
James Goppert committed
51

52
    // initialize port attributes for serial port communication
James Goppert's avatar
James Goppert committed
53

54
    initTermios();
James Goppert's avatar
James Goppert committed
55 56 57 58 59
}


TermiosHelper::~TermiosHelper()
{
60 61 62 63
    // It is good practice to reset a serial port back to the state in
    // which you found it. This is why we saved the original termios struct
    // The constant TCSANOW (defined in termios.h) indicates that
    // the change should take effect immediately.
James Goppert's avatar
James Goppert committed
64

65
    restoreTermios();
James Goppert's avatar
James Goppert committed
66

67 68
    delete originalAttrs_;
    delete currentAttrs_;
James Goppert's avatar
James Goppert committed
69 70 71 72 73 74 75 76
}

/*!
   Sets the termios structure.
 */

bool TermiosHelper::applyChanges(ChangeApplyTypes /*apptype*/)
{
77 78 79 80 81 82 83 84 85 86 87 88
    // there is only termios structure to be applied for posix compatible platforms

    if ( tcsetattr(fileDescriptor_, TCSANOW, currentAttrs_) == -1 ) {
        qCritical() << QString("TermiosHelper::applyChanges(file: %1, applyType: %2) failed " \
                               "when setting new attributes: %3(%4)")
                       .arg(fileDescriptor_)
                       .arg(TCSANOW)
                       .arg(strerror(errno))
                       .arg(errno);
        return false;
    }
    return true;
James Goppert's avatar
James Goppert committed
89 90 91 92 93 94 95 96
}

/*!

 */

bool TermiosHelper::setCtrSignal(ControlSignals csig, bool value)
{
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
    int status;

    if ( ioctl(fileDescriptor_, TIOCMGET, &status) == -1 ) {
        qCritical() <<  QString("TermiosHelper::setCtrSignal(file: %1, csig: %2) failed" \
                                "when fetching control signal values : %3(%4)")
                        .arg(fileDescriptor_)
                        .arg(csig)
                        .arg(strerror(errno))
                        .arg(errno);
        return false;
    }

    if ( value )
        status |= (int)csig;
    else
        status &= ~((int)csig);

    if ( ioctl(fileDescriptor_, TIOCMSET, &status) == -1 ) {
        qCritical() <<  QString("TermiosHelper::setCtrSignal(file: %1, csig: %2) failed" \
                                "when setting control signal values : %3(%4)")
                        .arg(fileDescriptor_)
                        .arg(csig)
                        .arg(strerror(errno))
                        .arg(errno);
        return false;
    }
    return true;
James Goppert's avatar
James Goppert committed
124 125 126 127 128 129 130 131
}

/*!

 */

QSerialPort::CommSignalValues TermiosHelper::ctrSignal(ControlSignals csig) const
{
132 133 134 135 136 137 138 139 140 141 142 143 144
    int status;

    if ( ioctl(fileDescriptor_, TIOCMGET, &status) == -1 ) {
        qCritical() <<  QString("TermiosHelper::ctrSignal(file: %1, csig: %2) failed" \
                                "when fetching control signal values : %3(%4)")
                        .arg(fileDescriptor_)
                        .arg(csig)
                        .arg(strerror(errno))
                        .arg(errno);
        return QSerialPort::Signal_Unknown;
    }

    return (status & ((int)csig)) ? QSerialPort::Signal_On : QSerialPort::Signal_Off;
James Goppert's avatar
James Goppert committed
145 146 147 148 149 150 151 152
}

/*!

 */

void TermiosHelper::initTermios()
{
153 154 155
    // Set raw input (non-canonical) mode, with reads blocking until either a single character
    // has been received or a one second timeout expires.
    // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") for details.
James Goppert's avatar
James Goppert committed
156

157
    cfmakeraw(currentAttrs_);
James Goppert's avatar
James Goppert committed
158

159
    currentAttrs_->c_cflag |= (CLOCAL | CREAD);
James Goppert's avatar
James Goppert committed
160

161
    // set communication timeouts
James Goppert's avatar
James Goppert committed
162

163
    currentAttrs_->c_cc[VMIN] = kDefaultNumOfBytes;
James Goppert's avatar
James Goppert committed
164

165 166
    // converting ms to one-tenths-of-second (sec * 0.1)
    currentAttrs_->c_cc[VTIME] = (kDefaultReadTimeout / 100);
James Goppert's avatar
James Goppert committed
167

168
    // ensure the new attributes take effect immediately
James Goppert's avatar
James Goppert committed
169

170
    applyChanges();
James Goppert's avatar
James Goppert committed
171 172 173 174 175 176 177 178
}

/*!

*/

void TermiosHelper::saveTermios()
{
179 180 181 182 183 184 185 186 187
    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, originalAttrs_) == -1 ) {
        qWarning() << QString("TermiosHelper::saveTermios(file: %1) failed when" \
                              " getting original port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
    }
James Goppert's avatar
James Goppert committed
188 189 190 191 192 193 194 195
}

/*!

*/

void TermiosHelper::restoreTermios()
{
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    if ( !originalAttrs_ || tcsetattr(fileDescriptor_, TCSANOW, originalAttrs_) == -1 ) {
        qWarning() << QString("TermiosHelper::restoreTermios(file: %1) failed when resetting " \
                              "serial port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
    }

    // Block until all written output has been sent from the device.
    // Note that this call is simply passed on to the serial device driver.
    // See tcsendbreak(3) ("man 3 tcsendbreak") for details.

    if ( tcdrain(fileDescriptor_) == -1 ) {
        qWarning() << QString("TermiosHelper::restoreTermios(file: %1) failed while waiting for drain: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
    }
James Goppert's avatar
James Goppert committed
214 215 216 217 218 219 220 221
}

/*!

*/

void TermiosHelper::setBaudRate(QPortSettings::BaudRate baudRate)
{
222
    speed_t baud = B9600;
James Goppert's avatar
James Goppert committed
223

224
    switch ( baudRate ) {
James Goppert's avatar
James Goppert committed
225
    case QPortSettings::BAUDR_50:
226 227
        baud = B50;
        break;
James Goppert's avatar
James Goppert committed
228
    case QPortSettings::BAUDR_75:
229 230
        baud = B75;
        break;
James Goppert's avatar
James Goppert committed
231
    case QPortSettings::BAUDR_110:
232 233
        baud = B110;
        break;
James Goppert's avatar
James Goppert committed
234
    case QPortSettings::BAUDR_134:
235 236
        baud = B134;
        break;
James Goppert's avatar
James Goppert committed
237
    case QPortSettings::BAUDR_150:
238 239
        baud = B150;
        break;
James Goppert's avatar
James Goppert committed
240
    case QPortSettings::BAUDR_200:
241 242
        baud = B200;
        break;
James Goppert's avatar
James Goppert committed
243
    case QPortSettings::BAUDR_300:
244 245
        baud = B300;
        break;
James Goppert's avatar
James Goppert committed
246
    case QPortSettings::BAUDR_600:
247 248
        baud = B600;
        break;
James Goppert's avatar
James Goppert committed
249
    case QPortSettings::BAUDR_1200:
250 251
        baud = B1200;
        break;
James Goppert's avatar
James Goppert committed
252
    case QPortSettings::BAUDR_1800:
253 254
        baud = B1800;
        break;
James Goppert's avatar
James Goppert committed
255
    case QPortSettings::BAUDR_2400:
256 257
        baud = B2400;
        break;
James Goppert's avatar
James Goppert committed
258
    case QPortSettings::BAUDR_4800:
259 260
        baud = B4800;
        break;
James Goppert's avatar
James Goppert committed
261
    case QPortSettings::BAUDR_9600:
262 263
        baud = B9600;
        break;
James Goppert's avatar
James Goppert committed
264
    case QPortSettings::BAUDR_19200:
265 266
        baud = B19200;
        break;
James Goppert's avatar
James Goppert committed
267
    case QPortSettings::BAUDR_38400:
268 269
        baud = B38400;
        break;
James Goppert's avatar
James Goppert committed
270
    case QPortSettings::BAUDR_57600:
271 272 273 274 275
        baud = B57600;
        break;
        //case QPortSettings::BAUDR_76800:
        //  baud = B76800;
        //  break;
James Goppert's avatar
James Goppert committed
276
    case QPortSettings::BAUDR_115200:
277 278 279
        baud = B115200;
        break;
    case QPortSettings::BAUDR_230400:
LM's avatar
LM committed
280
#ifdef B230400
281
        baud = B230400;
LM's avatar
LM committed
282
#else
283
        baud = (speed_t)230400;
LM's avatar
LM committed
284
#endif
285 286
        break;
    case QPortSettings::BAUDR_460800:
LM's avatar
LM committed
287
#ifdef B460800
288
        baud = B460800;
LM's avatar
LM committed
289
#else
290
        baud = (speed_t)460800;
LM's avatar
LM committed
291
#endif
292 293
        break;
    case QPortSettings::BAUDR_500000:
LM's avatar
LM committed
294
#ifdef B500000
295
        baud = B500000;
LM's avatar
LM committed
296
#else
297
        baud = (speed_t)500000;
LM's avatar
LM committed
298
#endif
299 300
        break;
    case QPortSettings::BAUDR_576000:
LM's avatar
LM committed
301
#ifdef B576000
302
        baud = B576000;
LM's avatar
LM committed
303
#else
304
        baud = (speed_t)576000;
LM's avatar
LM committed
305
#endif
306 307
        break;
    case QPortSettings::BAUDR_921600:
LM's avatar
LM committed
308
#ifdef B921600
309
        baud = B921600;
LM's avatar
LM committed
310
#else
311
        baud = (speed_t)921600;
James Goppert's avatar
James Goppert committed
312
#endif
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
        break;
    default:
        qWarning() << "TermiosHelper::setBaudRate(" << baudRate << "): " \
                      "Unsupported baud rate";
    }

    //#ifdef Q_OS_MAC
    //  if ( ioctl( fileDescriptor_, IOSSIOSPEED, &baud ) == -1 )
    //          {
    //      qCritical() <<  QString("TermiosHelper::setBaudRate(file: %1) failed: %2(%3)")
    //                   .arg(fileDescriptor_)
    //                   .arg(strerror(errno))
    //                   .arg(errno);
    //              return false;
    //          }
    //#else

330 331
    qCritical() << "Baud rate is now: " << baud;

332 333 334 335 336 337 338
    if ( cfsetspeed(currentAttrs_, baud) == -1 ) {
        qCritical() <<  QString("TermiosHelper::setBaudRate(file: %1) failed: %2(%3)")
                        .arg(fileDescriptor_)
                        .arg(strerror(errno))
                        .arg(errno);
    }
    //#endif
James Goppert's avatar
James Goppert committed
339 340 341 342 343 344 345 346
}

/*!

*/

QPortSettings::BaudRate TermiosHelper::baudRate() const
{
347 348
    speed_t ibaud = cfgetispeed(currentAttrs_);
    speed_t obaud = cfgetospeed(currentAttrs_);
James Goppert's avatar
James Goppert committed
349

350
    Q_ASSERT(ibaud == obaud);
James Goppert's avatar
James Goppert committed
351

352
    switch ( ibaud ) {
James Goppert's avatar
James Goppert committed
353
    case B50:
354
        return QPortSettings::BAUDR_50;
James Goppert's avatar
James Goppert committed
355
    case B75:
356
        return QPortSettings::BAUDR_75;
James Goppert's avatar
James Goppert committed
357
    case B110:
358
        return QPortSettings::BAUDR_110;
James Goppert's avatar
James Goppert committed
359
    case B134:
360
        return QPortSettings::BAUDR_134;
James Goppert's avatar
James Goppert committed
361
    case B150:
362
        return QPortSettings::BAUDR_150;
James Goppert's avatar
James Goppert committed
363
    case B200:
364
        return QPortSettings::BAUDR_200;
James Goppert's avatar
James Goppert committed
365
    case B300:
366
        return QPortSettings::BAUDR_300;
James Goppert's avatar
James Goppert committed
367
    case B600:
368
        return QPortSettings::BAUDR_600;
James Goppert's avatar
James Goppert committed
369
    case B1200:
370
        return QPortSettings::BAUDR_1200;
James Goppert's avatar
James Goppert committed
371
    case B1800:
372
        return QPortSettings::BAUDR_1800;
James Goppert's avatar
James Goppert committed
373
    case B2400:
374
        return QPortSettings::BAUDR_2400;
James Goppert's avatar
James Goppert committed
375
    case B4800:
376
        return QPortSettings::BAUDR_4800;
James Goppert's avatar
James Goppert committed
377
    case B9600:
378
        return QPortSettings::BAUDR_9600;
James Goppert's avatar
James Goppert committed
379
    case B19200:
380
        return QPortSettings::BAUDR_19200;
James Goppert's avatar
James Goppert committed
381
    case B38400:
382
        return QPortSettings::BAUDR_38400;
James Goppert's avatar
James Goppert committed
383
    case B57600:
384 385 386
        return QPortSettings::BAUDR_57600;
        //case B76800:
        //  return QPortSettings::BAUDR_76800;
James Goppert's avatar
James Goppert committed
387
    case B115200:
388 389
        return QPortSettings::BAUDR_115200;
#ifdef B230400
James Goppert's avatar
James Goppert committed
390
    case B230400:
391
        return QPortSettings::BAUDR_230400;
392 393 394
#else
    case 230400:
        return QPortSettings::BAUDR_230400;
395 396
#endif
#ifdef B460800
James Goppert's avatar
James Goppert committed
397
    case B460800:
398
        return QPortSettings::BAUDR_460800;
399 400 401
#else
    case 460800:
        return QPortSettings::BAUDR_460800;
402
#endif
403 404 405 406 407 408 409
#ifdef B500000
    case B500000:
        return QPortSettings::BAUDR_500000;
#else
    case 500000:
        return QPortSettings::BAUDR_500000;
#endif
Lorenz Meier's avatar
Lorenz Meier committed
410
#ifdef B576000
411 412 413 414 415 416
    case B576000:
        return QPortSettings::BAUDR_576000;
#else
    case 576000:
        return QPortSettings::BAUDR_576000;
#endif
417 418 419
#ifdef B921600
    case B921600:
        return QPortSettings::BAUDR_921600;
420 421 422
#else
    case 921600:
        return QPortSettings::BAUDR_921600;
James Goppert's avatar
James Goppert committed
423
#endif
424 425 426
    default:
        qWarning() << "TermiosHelper::baudRate(): Unknown baud rate";
    }
James Goppert's avatar
James Goppert committed
427

428
    return QPortSettings::BAUDR_UNKNOWN;
James Goppert's avatar
James Goppert committed
429 430 431 432 433 434 435 436
}

/*!

*/

QPortSettings::DataBits TermiosHelper::dataBits() const
{
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
    struct termios options;

    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, &options) == -1 ) {
        qWarning() << QString("TermiosHelper::dataBits(file: %1) failed when" \
                              " getting original port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
        return QPortSettings::DB_UNKNOWN;
    }

    if ( (options.c_cflag & CSIZE) == CS5 )
        return QPortSettings::DB_5;
    else if ( (options.c_cflag & CSIZE) == CS6 )
        return QPortSettings::DB_6;
    else if ( (options.c_cflag & CSIZE) == CS7 )
        return QPortSettings::DB_7;
    else if ( (options.c_cflag & CSIZE) == CS8 )
        return QPortSettings::DB_8;
    else
        return QPortSettings::DB_UNKNOWN;
James Goppert's avatar
James Goppert committed
460 461 462 463 464 465 466 467
}


/*!

*/
void TermiosHelper::setDataBits(QPortSettings::DataBits dataBits)
{
468
    switch( dataBits ) {
James Goppert's avatar
James Goppert committed
469 470
    /*5 data bits*/
    case QPortSettings::DB_5:
471 472 473 474
        currentAttrs_->c_cflag &= (~CSIZE);
        currentAttrs_->c_cflag |= CS5;
        break;
        /*6 data bits*/
James Goppert's avatar
James Goppert committed
475
    case QPortSettings::DB_6:
476 477 478 479
        currentAttrs_->c_cflag &= (~CSIZE);
        currentAttrs_->c_cflag |= CS6;
        break;
        /*7 data bits*/
James Goppert's avatar
James Goppert committed
480
    case QPortSettings::DB_7:
481 482 483 484
        currentAttrs_->c_cflag &= (~CSIZE);
        currentAttrs_->c_cflag |= CS7;
        break;
        /*8 data bits*/
James Goppert's avatar
James Goppert committed
485
    case QPortSettings::DB_8:
486 487 488
        currentAttrs_->c_cflag &= (~CSIZE);
        currentAttrs_->c_cflag |= CS8;
        break;
James Goppert's avatar
James Goppert committed
489
    default:
490 491 492 493
        currentAttrs_->c_cflag &= (~CSIZE);
        currentAttrs_->c_cflag |= CS8;
        qWarning() << "TermiosHelper::setDataBits(" << dataBits << "): Unsupported data bits";
    }
James Goppert's avatar
James Goppert committed
494 495 496 497 498 499 500 501
}

/*!

 */

QPortSettings::Parity TermiosHelper::parity() const
{
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
    struct termios options;

    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, &options) == -1 ) {
        qWarning() << QString("TermiosHelper::parity(file: %1) failed when getting original " \
                              "port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
        return QPortSettings::PAR_UNKNOWN;
    }

    if ( options.c_cflag & PARENB ) {
        if ( options.c_cflag & PARODD )
            return QPortSettings::PAR_ODD;
        else
            return QPortSettings::PAR_EVEN;
    }
James Goppert's avatar
James Goppert committed
521
    else
522
        return QPortSettings::PAR_NONE;
James Goppert's avatar
James Goppert committed
523 524 525 526 527 528 529
}

/*!

*/
void TermiosHelper::setParity(QPortSettings::Parity parity)
{
530
    switch ( parity ) {
James Goppert's avatar
James Goppert committed
531 532
    /*no parity*/
    case QPortSettings::PAR_NONE:
533 534 535
        currentAttrs_->c_cflag &= (~PARENB);
        break;
        /*odd parity*/
James Goppert's avatar
James Goppert committed
536
    case QPortSettings::PAR_ODD:
537 538 539
        currentAttrs_->c_cflag |= (PARENB|PARODD);
        break;
        /*even parity*/
James Goppert's avatar
James Goppert committed
540
    case QPortSettings::PAR_EVEN:
541 542 543
        currentAttrs_->c_cflag &= (~PARODD);
        currentAttrs_->c_cflag |= PARENB;
        break;
James Goppert's avatar
James Goppert committed
544
    default:
545 546 547
        currentAttrs_->c_cflag &= (~PARENB);
        qWarning() << "TermiosHelper::setParity(" << parity << "): Unsupported parity";
    }
James Goppert's avatar
James Goppert committed
548 549 550 551 552 553 554 555
}

/*!

 */

QPortSettings::StopBits TermiosHelper::stopBits() const
{
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
    struct termios options;
    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, &options) == -1 ) {
        qWarning() << QString("TermiosHelper::stopBits(file: %1) failed when getting original " \
                              "port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
        return QPortSettings::STOP_UNKNOWN;
    }

    if ( options.c_cflag & CSTOPB )
        return QPortSettings::STOP_2;
    else
        return QPortSettings::STOP_1;
James Goppert's avatar
James Goppert committed
572 573 574 575 576 577 578
}

/*!

*/
void TermiosHelper::setStopBits(QPortSettings::StopBits stopBits)
{
579
    switch( stopBits ) {
James Goppert's avatar
James Goppert committed
580 581
    /*one stop bit*/
    case QPortSettings::STOP_1:
582 583 584
        currentAttrs_->c_cflag &= (~CSTOPB);
        break;
        /*two stop bits*/
James Goppert's avatar
James Goppert committed
585
    case QPortSettings::STOP_2:
586 587
        currentAttrs_->c_cflag |= CSTOPB;
        break;
James Goppert's avatar
James Goppert committed
588
    default:
589 590 591
        currentAttrs_->c_cflag &= (~CSTOPB);
        qWarning() << "TermiosHelper::setStopBits(" << stopBits << "): Unsupported stop bits";
    }
James Goppert's avatar
James Goppert committed
592 593 594 595 596 597 598 599
}

/*!
  @return FLOW_UNKNOWN in error case
 */

QPortSettings::FlowControl TermiosHelper::flowControl() const
{
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
    struct termios options;
    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, &options) == -1 ) {
        qWarning() << QString("TermiosHelper::flowControl(file: %1) failed when getting original " \
                              "port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
        return QPortSettings::FLOW_UNKNOWN;
    }

    if ( options.c_cflag & CRTSCTS ) {
        return QPortSettings::FLOW_HARDWARE;
    }
    else {
        if ( options.c_iflag & IXON )
            return QPortSettings::FLOW_XONXOFF;
        else
            return QPortSettings::FLOW_OFF;
    }
James Goppert's avatar
James Goppert committed
621 622 623 624 625 626 627
}

/*!

*/
void TermiosHelper::setFlowControl(QPortSettings::FlowControl flow)
{
628
    switch( flow ) {
James Goppert's avatar
James Goppert committed
629 630
    /*no flow control*/
    case QPortSettings::FLOW_OFF:
631 632 633 634
        currentAttrs_->c_cflag &= (~CRTSCTS);
        currentAttrs_->c_iflag &= (~(IXON|IXOFF|IXANY));
        break;
        /*software (XON/XOFF) flow control*/
James Goppert's avatar
James Goppert committed
635
    case QPortSettings::FLOW_XONXOFF:
636 637 638
        currentAttrs_->c_cflag &= (~CRTSCTS);
        currentAttrs_->c_iflag |= (IXON|IXOFF|IXANY);
        break;
James Goppert's avatar
James Goppert committed
639
    case QPortSettings::FLOW_HARDWARE:
640 641 642
        currentAttrs_->c_cflag |= CRTSCTS;
        currentAttrs_->c_iflag &= (~(IXON|IXOFF|IXANY));
        break;
James Goppert's avatar
James Goppert committed
643
    default:
644 645 646 647
        currentAttrs_->c_cflag &= (~CRTSCTS);
        currentAttrs_->c_iflag &= (~(IXON|IXOFF|IXANY));
        qWarning() << "TermiosHelper::setFlowControl(" << flow << "): Unsupported flow control type";
    }
James Goppert's avatar
James Goppert committed
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

//
// information about VMIN and VTIME
// @see www.unixwiz.net/techtips/termios-vmin-vtime.html
//
// VMIN is a character count ranging from 0 to 255 characters, and VTIME is time measured in 0.1
// second intervals, (0 to 25.5 seconds). The value of "zero" is special to both of these parameters,
// and this suggests four combinations that we'll discuss below. In every case, the question is when
// a read() system call is satisfied, and this is our prototype call:
//  int n = read(fd, buffer, nbytes);
//
// Keep in mind that the tty driver maintains an input queue of bytes already read from the
// serial line and not passed to the user, so not every read() call waits for actual I/O - the read
// may very well be satisfied directly from the input queue.
//
// VMIN = 0 and VTIME = 0
// This is a completely non-blocking read - the call is satisfied immediately directly from the
// driver's input queue. If data are available, it's transferred to the caller's buffer up to
// nbytes and returned. Otherwise zero is immediately returned to indicate "no data". We'll note
// that this is "polling" of the serial port, and it's almost always a bad idea. If done repeatedly,
// it can consume enormous amounts of processor time and is highly inefficient. Don't use this mode
// unless you really, really know what you're doing.
//
// VMIN = 0 and VTIME > 0
// This is a pure timed read. If data are available in the input queue, it's transferred to the caller's
// buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks
// until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires
// without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more
// is available in the input queue, it's returned to the caller. Note that this is an overall timer,
// not an intercharacter one.
//
// VMIN > 0 and VTIME > 0
// A read() is satisfied when either VMIN characters have been transferred to the caller's buffer,
// or when VTIME tenths expire between characters. Since this timer is not started until the first
// character arrives, this call can block indefinitely if the serial line is idle. This is the most
// common mode of operation, and we consider VTIME to be an intercharacter timeout, not an overall one.
// This call should never return zero bytes read.
//
// VMIN > 0 and VTIME = 0
// This is a counted read that is satisfied only when at least VMIN characters have been transferred
// to the caller's buffer - there is no timing component involved. This read can be satisfied from the
// driver's input queue (where the call could return immediately), or by waiting for new data to arrive:
// in this respect the call could block indefinitely. We believe that it's undefined behavior if nbytes
// is less then VMIN.
//

bool TermiosHelper::commTimeouts(CommTimeouts &commtimeouts) const
{
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    struct termios options;

    // get the current serial port attributes

    if ( tcgetattr(fileDescriptor_, &options) == -1 ) {
        qWarning() << QString("TermiosHelper::commTimeouts(file: %1) failed when getting original " \
                              "port attributes: %2(%3)")
                      .arg(fileDescriptor_)
                      .arg(strerror(errno))
                      .arg(errno);
        return false;
    }
    commtimeouts.PosixVMIN = options.c_cc[VMIN];
    commtimeouts.PosixVTIME = options.c_cc[VTIME];

    return true;
James Goppert's avatar
James Goppert committed
713 714 715 716
}

void TermiosHelper::setCommTimeouts(const CommTimeouts commtimeouts)
{
717 718
    currentAttrs_->c_cc[VMIN] = (cc_t)commtimeouts.PosixVMIN;
    currentAttrs_->c_cc[VTIME] = (cc_t)commtimeouts.PosixVTIME;
James Goppert's avatar
James Goppert committed
719 720 721 722 723
}

} // namespace