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
    (obaud == ibaud);
James Goppert's avatar
James Goppert committed
351

352
    Q_ASSERT(ibaud == obaud);
James Goppert's avatar
James Goppert committed
353

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

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

/*!

*/

QPortSettings::DataBits TermiosHelper::dataBits() const
{
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
    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
462 463 464 465 466 467 468 469
}


/*!

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

/*!

 */

QPortSettings::Parity TermiosHelper::parity() const
{
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    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
523
    else
524
        return QPortSettings::PAR_NONE;
James Goppert's avatar
James Goppert committed
525 526 527 528 529 530 531
}

/*!

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

/*!

 */

QPortSettings::StopBits TermiosHelper::stopBits() const
{
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    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
574 575 576 577 578 579 580
}

/*!

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

/*!
  @return FLOW_UNKNOWN in error case
 */

QPortSettings::FlowControl TermiosHelper::flowControl() const
{
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
    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
623 624 625 626 627 628 629
}

/*!

*/
void TermiosHelper::setFlowControl(QPortSettings::FlowControl flow)
{
630
    switch( flow ) {
James Goppert's avatar
James Goppert committed
631 632
    /*no flow control*/
    case QPortSettings::FLOW_OFF:
633 634 635 636
        currentAttrs_->c_cflag &= (~CRTSCTS);
        currentAttrs_->c_iflag &= (~(IXON|IXOFF|IXANY));
        break;
        /*software (XON/XOFF) flow control*/
James Goppert's avatar
James Goppert committed
637
    case QPortSettings::FLOW_XONXOFF:
638 639 640
        currentAttrs_->c_cflag &= (~CRTSCTS);
        currentAttrs_->c_iflag |= (IXON|IXOFF|IXANY);
        break;
James Goppert's avatar
James Goppert committed
641
    case QPortSettings::FLOW_HARDWARE:
642 643 644
        currentAttrs_->c_cflag |= CRTSCTS;
        currentAttrs_->c_iflag &= (~(IXON|IXOFF|IXANY));
        break;
James Goppert's avatar
James Goppert committed
645
    default:
646 647 648 649
        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
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 697 698
}

//
// 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
{
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    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
715 716 717 718
}

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

} // namespace