/*
* 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
*
*
* @file qserialportnative_posix.cpp
* @brief
*/
#include
#include
#include
#include
#include
#include "qserialportnative.h"
#include "termioshelper.h"
namespace {
const int kMinReadTimeout = 100;
}
namespace TNX {
/*!
Constructs a QSerialPortNative object with the given \a port name and \a parent.
*/
QSerialPortNative::QSerialPortNative(const QString &portName, QObject *parent)
: QIODevice(parent), portName_(portName), fileDescriptor_(0), portHelper_(NULL),
readNotifier_(NULL)
{
}
/*!
Constructs a QSerialPortNative object with the given \a port name, \a settings and \a parent.
*/
QSerialPortNative::QSerialPortNative(const QString &portName, const QPortSettings &settings, QObject *parent)
: QIODevice(parent), portName_(portName), portSettings_(settings), fileDescriptor_(0), portHelper_(NULL),
readNotifier_(NULL)
{
}
/*!
*/
bool QSerialPortNative::open_impl()
{
// Open the serial port read/write, with no controlling terminal,
// and don't wait for a connection.
// The O_NONBLOCK flag also causes subsequent I/O on the device to be non-blocking.
// See open(2) ("man 2 open") for details.
fileDescriptor_ = ::open(qPrintable(portName_), O_RDWR | O_NOCTTY | O_NONBLOCK);
if ( fileDescriptor_ == -1 )
return false;
// Note that open() follows POSIX semantics: multiple open() calls to
// the same file will succeed unless the TIOCEXCL ioctl is issued.
// This will prevent additional opens except by root-owned processes.
// See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
if ( ioctl(fileDescriptor_, TIOCEXCL) == -1 )
return false;
// Now that the device is open, clear the O_NONBLOCK flag so
// subsequent I/O will block.
// See fcntl(2) ("man 2 fcntl") for details.
if ( fcntl(fileDescriptor_, F_SETFL, 0) == -1)
return false;
readNotifier_ = new QSocketNotifier(fileDescriptor_, QSocketNotifier::Read, this);
Q_CHECK_PTR(readNotifier_);
if ( !readNotifier_ || !connect(readNotifier_, SIGNAL(activated(int)), this, SLOT(onDataReceived())) )
qWarning() << QString("QSerialPortNative::open(%1) failed when connecting to read notifier")
.arg(portName_);
// create a termios helper object
portHelper_ = new TermiosHelper(fileDescriptor_);
Q_CHECK_PTR(portHelper_);
return true;
}
/*!
*/
void QSerialPortNative::close_impl()
{
::close(fileDescriptor_);
fileDescriptor_ = 0;
}
/*!
*/
QString QSerialPortNative::lastErrorText_impl() const
{
return strerror(errno);
}
/*!
*/
int QSerialPortNative::lastError_impl() const
{
return errno;
}
/*!
*/
qint64 QSerialPortNative::bytesAvailable_impl() const
{
int nbytes;
if ( ioctl(fileDescriptor_, FIONREAD, &nbytes) == -1 )
return -1LL;
return QIODevice::bytesAvailable() + (qint64)nbytes;
}
/*!
* Blocks until new data is available for reading, or until msecs milliseconds have passed.
* If msecs is -1, this function will not time out.
* Returns 1 if new data is available for reading; 0 if the operation timed out or;
* -1 if an error occurred.
*/
int QSerialPortNative::waitForReadyRead_impl(int timeout)
{
fd_set input;
struct timeval wait, *waitTimeout = NULL;
FD_ZERO (&input);
FD_SET (fileDescriptor_, &input);
if ( timeout != -1 ) {
wait.tv_sec = timeout/1000;
wait.tv_usec = (timeout%1000) * 1000;
waitTimeout = &wait;
}
// waitTimeout is NULL if timeout is -1, meaning no timeout
int max_fd = fileDescriptor_ + 1;
int num = select(max_fd, &input, NULL, NULL, waitTimeout);
if ( num > 0 ) {
if ( FD_ISSET(fileDescriptor_, &input) )
return 1; // data pending.
else {
qWarning() << QString("QSerialPortNative::waitForReadyRead(%1): unexpected value returned from select().")
.arg(portName_);
return 0; // this path is not expected
}
}
return num; // error or timeout occurred.
}
/*!
*/
bool QSerialPortNative::flushInBuffer_impl()
{
if ( tcflush(fileDescriptor_, TCIFLUSH) == -1 )
return false;
return true;
}
/*!
*/
bool QSerialPortNative::flushOutBuffer_impl()
{
if ( tcflush(fileDescriptor_, TCOFLUSH) == -1 )
return false;
return true;
}
/*!
*/
bool QSerialPortNative::sendBreak_impl(int timeout)
{
// Mac OSX ignores the duration parameter. Not sure what Linux does.
if ( tcsendbreak(fileDescriptor_, (timeout*1000)) == -1 )
return false;
return true;
}
/*!
*/
qint64 QSerialPortNative::readData_impl(char *data, qint64 maxlen)
{
qint64 numBytes = ::read(fileDescriptor_, data, maxlen);
if ( numBytes == -1LL && errno != EAGAIN )
return -1LL;
return numBytes;
}
/*!
*/
qint64 QSerialPortNative::writeData_impl(const char *data, qint64 len)
{
qint64 numBytes = ::write(fileDescriptor_, data, len);
if ( numBytes == -1LL && errno != EAGAIN )
return -1LL;
return numBytes;
}
} // namespace