mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-11-29 07:24:13 +01:00
[OP-835] Qt 5.1.0 - removed libs/qextserialport as it is now part of Qt
This commit is contained in:
parent
7bcb82478d
commit
a9f2cfdf3e
@ -1,2 +0,0 @@
|
||||
LIBS *= -l$$qtLibraryName(QExtSerialPort)
|
||||
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = src \
|
||||
|
@ -1,958 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include "qextserialport.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
|
||||
void QextSerialPort::platformSpecificInit()
|
||||
{
|
||||
fd = 0;
|
||||
readNotifier = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Standard destructor.
|
||||
*/
|
||||
void QextSerialPort::platformSpecificDestruct()
|
||||
{}
|
||||
|
||||
/*!
|
||||
Sets the baud rate of the serial port. Note that not all rates are applicable on
|
||||
all platforms. The following table shows translations of the various baud rate
|
||||
constants on Windows(including NT/2000) and POSIX platforms. Speeds marked with an *
|
||||
are speeds that are usable on both Windows and POSIX.
|
||||
|
||||
\note
|
||||
BAUD76800 may not be supported on all POSIX systems. SGI/IRIX systems do not support
|
||||
BAUD1800.
|
||||
|
||||
\verbatim
|
||||
|
||||
RATE Windows Speed POSIX Speed
|
||||
----------- ------------- -----------
|
||||
BAUD50 110 50
|
||||
BAUD75 110 75
|
||||
* BAUD110 110 110
|
||||
BAUD134 110 134.5
|
||||
BAUD150 110 150
|
||||
BAUD200 110 200
|
||||
* BAUD300 300 300
|
||||
* BAUD600 600 600
|
||||
* BAUD1200 1200 1200
|
||||
BAUD1800 1200 1800
|
||||
* BAUD2400 2400 2400
|
||||
* BAUD4800 4800 4800
|
||||
* BAUD9600 9600 9600
|
||||
BAUD14400 14400 9600
|
||||
* BAUD19200 19200 19200
|
||||
* BAUD38400 38400 38400
|
||||
BAUD56000 56000 38400
|
||||
* BAUD57600 57600 57600
|
||||
BAUD76800 57600 76800
|
||||
* BAUD115200 115200 115200
|
||||
BAUD128000 128000 115200
|
||||
BAUD256000 256000 115200
|
||||
\endverbatim
|
||||
*/
|
||||
void QextSerialPort::setBaudRate(BaudRateType baudRate)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.BaudRate != baudRate) {
|
||||
switch (baudRate) {
|
||||
case BAUD14400:
|
||||
Settings.BaudRate = BAUD9600;
|
||||
break;
|
||||
|
||||
case BAUD56000:
|
||||
Settings.BaudRate = BAUD38400;
|
||||
break;
|
||||
|
||||
case BAUD76800:
|
||||
|
||||
#ifndef B76800
|
||||
Settings.BaudRate = BAUD57600;
|
||||
#else
|
||||
Settings.BaudRate = baudRate;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case BAUD128000:
|
||||
case BAUD256000:
|
||||
Settings.BaudRate = BAUD115200;
|
||||
break;
|
||||
|
||||
default:
|
||||
Settings.BaudRate = baudRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (baudRate) {
|
||||
/*50 baud*/
|
||||
case BAUD50:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows does not support 50 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B50;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B50);
|
||||
cfsetospeed(&Posix_CommConfig, B50);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*75 baud*/
|
||||
case BAUD75:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows does not support 75 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B75;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B75);
|
||||
cfsetospeed(&Posix_CommConfig, B75);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*110 baud*/
|
||||
case BAUD110:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B110;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B110);
|
||||
cfsetospeed(&Posix_CommConfig, B110);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*134.5 baud*/
|
||||
case BAUD134:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows does not support 134.5 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B134;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B134);
|
||||
cfsetospeed(&Posix_CommConfig, B134);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*150 baud*/
|
||||
case BAUD150:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows does not support 150 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B150;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B150);
|
||||
cfsetospeed(&Posix_CommConfig, B150);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*200 baud*/
|
||||
case BAUD200:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows does not support 200 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B200);
|
||||
cfsetospeed(&Posix_CommConfig, B200);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*300 baud*/
|
||||
case BAUD300:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B300;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B300);
|
||||
cfsetospeed(&Posix_CommConfig, B300);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*600 baud*/
|
||||
case BAUD600:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B600;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B600);
|
||||
cfsetospeed(&Posix_CommConfig, B600);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*1200 baud*/
|
||||
case BAUD1200:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B1200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B1200);
|
||||
cfsetospeed(&Posix_CommConfig, B1200);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*1800 baud*/
|
||||
case BAUD1800:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows and IRIX do not support 1800 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B1800;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B1800);
|
||||
cfsetospeed(&Posix_CommConfig, B1800);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*2400 baud*/
|
||||
case BAUD2400:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B2400;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B2400);
|
||||
cfsetospeed(&Posix_CommConfig, B2400);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*4800 baud*/
|
||||
case BAUD4800:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B4800;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B4800);
|
||||
cfsetospeed(&Posix_CommConfig, B4800);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*9600 baud*/
|
||||
case BAUD9600:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B9600;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B9600);
|
||||
cfsetospeed(&Posix_CommConfig, B9600);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*14400 baud*/
|
||||
case BAUD14400:
|
||||
TTY_WARNING("QextSerialPort: POSIX does not support 14400 baud operation. Switching to 9600 baud.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B9600;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B9600);
|
||||
cfsetospeed(&Posix_CommConfig, B9600);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*19200 baud*/
|
||||
case BAUD19200:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B19200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B19200);
|
||||
cfsetospeed(&Posix_CommConfig, B19200);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*38400 baud*/
|
||||
case BAUD38400:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B38400;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B38400);
|
||||
cfsetospeed(&Posix_CommConfig, B38400);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*56000 baud*/
|
||||
case BAUD56000:
|
||||
TTY_WARNING("QextSerialPort: POSIX does not support 56000 baud operation. Switching to 38400 baud.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B38400;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B38400);
|
||||
cfsetospeed(&Posix_CommConfig, B38400);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*57600 baud*/
|
||||
case BAUD57600:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B57600;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B57600);
|
||||
cfsetospeed(&Posix_CommConfig, B57600);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*76800 baud*/
|
||||
case BAUD76800:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Windows and some POSIX systems do not support 76800 baud operation.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
|
||||
#ifdef B76800
|
||||
Posix_CommConfig.c_cflag |= B76800;
|
||||
#else
|
||||
TTY_WARNING("QextSerialPort: QextSerialPort was compiled without 76800 baud support. Switching to 57600 baud.");
|
||||
Posix_CommConfig.c_cflag |= B57600;
|
||||
#endif // B76800
|
||||
#else // CBAUD
|
||||
#ifdef B76800
|
||||
cfsetispeed(&Posix_CommConfig, B76800);
|
||||
cfsetospeed(&Posix_CommConfig, B76800);
|
||||
#else
|
||||
TTY_WARNING("QextSerialPort: QextSerialPort was compiled without 76800 baud support. Switching to 57600 baud.");
|
||||
cfsetispeed(&Posix_CommConfig, B57600);
|
||||
cfsetospeed(&Posix_CommConfig, B57600);
|
||||
#endif // B76800
|
||||
#endif // CBAUD
|
||||
break;
|
||||
|
||||
/*115200 baud*/
|
||||
case BAUD115200:
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B115200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B115200);
|
||||
cfsetospeed(&Posix_CommConfig, B115200);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*128000 baud*/
|
||||
case BAUD128000:
|
||||
TTY_WARNING("QextSerialPort: POSIX does not support 128000 baud operation. Switching to 115200 baud.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B115200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B115200);
|
||||
cfsetospeed(&Posix_CommConfig, B115200);
|
||||
#endif
|
||||
break;
|
||||
|
||||
/*256000 baud*/
|
||||
case BAUD256000:
|
||||
TTY_WARNING("QextSerialPort: POSIX does not support 256000 baud operation. Switching to 115200 baud.");
|
||||
#ifdef CBAUD
|
||||
Posix_CommConfig.c_cflag &= (~CBAUD);
|
||||
Posix_CommConfig.c_cflag |= B115200;
|
||||
#else
|
||||
cfsetispeed(&Posix_CommConfig, B115200);
|
||||
cfsetospeed(&Posix_CommConfig, B115200);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the number of data bits used by the serial port. Possible values of dataBits are:
|
||||
\verbatim
|
||||
DATA_5 5 data bits
|
||||
DATA_6 6 data bits
|
||||
DATA_7 7 data bits
|
||||
DATA_8 8 data bits
|
||||
\endverbatim
|
||||
|
||||
\note
|
||||
This function is subject to the following restrictions:
|
||||
\par
|
||||
5 data bits cannot be used with 2 stop bits.
|
||||
\par
|
||||
8 data bits cannot be used with space parity on POSIX systems.
|
||||
*/
|
||||
void QextSerialPort::setDataBits(DataBitsType dataBits)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.DataBits != dataBits) {
|
||||
if ((Settings.StopBits == STOP_2 && dataBits == DATA_5) ||
|
||||
(Settings.StopBits == STOP_1_5 && dataBits != DATA_5) ||
|
||||
(Settings.Parity == PAR_SPACE && dataBits == DATA_8)) {} else {
|
||||
Settings.DataBits = dataBits;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (dataBits) {
|
||||
/*5 data bits*/
|
||||
case DATA_5:
|
||||
if (Settings.StopBits == STOP_2) {
|
||||
TTY_WARNING("QextSerialPort: 5 Data bits cannot be used with 2 stop bits.");
|
||||
} else {
|
||||
Settings.DataBits = dataBits;
|
||||
Posix_CommConfig.c_cflag &= (~CSIZE);
|
||||
Posix_CommConfig.c_cflag |= CS5;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
|
||||
/*6 data bits*/
|
||||
case DATA_6:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 6 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Settings.DataBits = dataBits;
|
||||
Posix_CommConfig.c_cflag &= (~CSIZE);
|
||||
Posix_CommConfig.c_cflag |= CS6;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
|
||||
/*7 data bits*/
|
||||
case DATA_7:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 7 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Settings.DataBits = dataBits;
|
||||
Posix_CommConfig.c_cflag &= (~CSIZE);
|
||||
Posix_CommConfig.c_cflag |= CS7;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
|
||||
/*8 data bits*/
|
||||
case DATA_8:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 8 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Settings.DataBits = dataBits;
|
||||
Posix_CommConfig.c_cflag &= (~CSIZE);
|
||||
Posix_CommConfig.c_cflag |= CS8;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the parity associated with the serial port. The possible values of parity are:
|
||||
\verbatim
|
||||
PAR_SPACE Space Parity
|
||||
PAR_MARK Mark Parity
|
||||
PAR_NONE No Parity
|
||||
PAR_EVEN Even Parity
|
||||
PAR_ODD Odd Parity
|
||||
\endverbatim
|
||||
|
||||
\note
|
||||
This function is subject to the following limitations:
|
||||
\par
|
||||
POSIX systems do not support mark parity.
|
||||
\par
|
||||
POSIX systems support space parity only if tricked into doing so, and only with
|
||||
fewer than 8 data bits. Use space parity very carefully with POSIX systems.
|
||||
*/
|
||||
void QextSerialPort::setParity(ParityType parity)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.Parity != parity) {
|
||||
if (parity == PAR_MARK || (parity == PAR_SPACE && Settings.DataBits == DATA_8)) {} else {
|
||||
Settings.Parity = parity;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (parity) {
|
||||
/*space parity*/
|
||||
case PAR_SPACE:
|
||||
if (Settings.DataBits == DATA_8) {
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort: Space parity is only supported in POSIX with 7 or fewer data bits");
|
||||
} else {
|
||||
/*space parity not directly supported - add an extra data bit to simulate it*/
|
||||
Posix_CommConfig.c_cflag &= ~(PARENB | CSIZE);
|
||||
switch (Settings.DataBits) {
|
||||
case DATA_5:
|
||||
Settings.DataBits = DATA_6;
|
||||
Posix_CommConfig.c_cflag |= CS6;
|
||||
break;
|
||||
|
||||
case DATA_6:
|
||||
Settings.DataBits = DATA_7;
|
||||
Posix_CommConfig.c_cflag |= CS7;
|
||||
break;
|
||||
|
||||
case DATA_7:
|
||||
Settings.DataBits = DATA_8;
|
||||
Posix_CommConfig.c_cflag |= CS8;
|
||||
break;
|
||||
|
||||
case DATA_8:
|
||||
break;
|
||||
}
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
|
||||
/*mark parity - WINDOWS ONLY*/
|
||||
case PAR_MARK:
|
||||
TTY_WARNING("QextSerialPort: Mark parity is not supported by POSIX.");
|
||||
break;
|
||||
|
||||
/*no parity*/
|
||||
case PAR_NONE:
|
||||
Posix_CommConfig.c_cflag &= (~PARENB);
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
|
||||
/*even parity*/
|
||||
case PAR_EVEN:
|
||||
Posix_CommConfig.c_cflag &= (~PARODD);
|
||||
Posix_CommConfig.c_cflag |= PARENB;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
|
||||
/*odd parity*/
|
||||
case PAR_ODD:
|
||||
Posix_CommConfig.c_cflag |= (PARENB | PARODD);
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the number of stop bits used by the serial port. Possible values of stopBits are:
|
||||
\verbatim
|
||||
STOP_1 1 stop bit
|
||||
STOP_1_5 1.5 stop bits
|
||||
STOP_2 2 stop bits
|
||||
\endverbatim
|
||||
\note
|
||||
This function is subject to the following restrictions:
|
||||
\par
|
||||
2 stop bits cannot be used with 5 data bits.
|
||||
\par
|
||||
POSIX does not support 1.5 stop bits.
|
||||
|
||||
*/
|
||||
void QextSerialPort::setStopBits(StopBitsType stopBits)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.StopBits != stopBits) {
|
||||
if ((Settings.DataBits == DATA_5 && stopBits == STOP_2) || stopBits == STOP_1_5) {} else {
|
||||
Settings.StopBits = stopBits;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (stopBits) {
|
||||
/*one stop bit*/
|
||||
case STOP_1:
|
||||
Settings.StopBits = stopBits;
|
||||
Posix_CommConfig.c_cflag &= (~CSTOPB);
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
|
||||
/*1.5 stop bits*/
|
||||
case STOP_1_5:
|
||||
TTY_WARNING("QextSerialPort: 1.5 stop bit operation is not supported by POSIX.");
|
||||
break;
|
||||
|
||||
/*two stop bits*/
|
||||
case STOP_2:
|
||||
if (Settings.DataBits == DATA_5) {
|
||||
TTY_WARNING("QextSerialPort: 2 stop bits cannot be used with 5 data bits");
|
||||
} else {
|
||||
Settings.StopBits = stopBits;
|
||||
Posix_CommConfig.c_cflag |= CSTOPB;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the flow control used by the port. Possible values of flow are:
|
||||
\verbatim
|
||||
FLOW_OFF No flow control
|
||||
FLOW_HARDWARE Hardware (RTS/CTS) flow control
|
||||
FLOW_XONXOFF Software (XON/XOFF) flow control
|
||||
\endverbatim
|
||||
\note
|
||||
FLOW_HARDWARE may not be supported on all versions of UNIX. In cases where it is
|
||||
unsupported, FLOW_HARDWARE is the same as FLOW_OFF.
|
||||
|
||||
*/
|
||||
void QextSerialPort::setFlowControl(FlowType flow)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.FlowControl != flow) {
|
||||
Settings.FlowControl = flow;
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (flow) {
|
||||
/*no flow control*/
|
||||
case FLOW_OFF:
|
||||
Posix_CommConfig.c_cflag &= (~CRTSCTS);
|
||||
Posix_CommConfig.c_iflag &= (~(IXON | IXOFF | IXANY));
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
|
||||
/*software (XON/XOFF) flow control*/
|
||||
case FLOW_XONXOFF:
|
||||
Posix_CommConfig.c_cflag &= (~CRTSCTS);
|
||||
Posix_CommConfig.c_iflag |= (IXON | IXOFF | IXANY);
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
|
||||
case FLOW_HARDWARE:
|
||||
Posix_CommConfig.c_cflag |= CRTSCTS;
|
||||
Posix_CommConfig.c_iflag &= (~(IXON | IXOFF | IXANY));
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the read and write timeouts for the port to millisec milliseconds.
|
||||
Note that this is a per-character timeout, i.e. the port will wait this long for each
|
||||
individual character, not for the whole read operation. This timeout also applies to the
|
||||
bytesWaiting() function.
|
||||
|
||||
\note
|
||||
POSIX does not support millisecond-level control for I/O timeout values. Any
|
||||
timeout set using this function will be set to the next lowest tenth of a second for
|
||||
the purposes of detecting read or write timeouts. For example a timeout of 550 milliseconds
|
||||
will be seen by the class as a timeout of 500 milliseconds for the purposes of reading and
|
||||
writing the port. However millisecond-level control is allowed by the select() system call,
|
||||
so for example a 550-millisecond timeout will be seen as 550 milliseconds on POSIX systems for
|
||||
the purpose of detecting available bytes in the read buffer.
|
||||
|
||||
*/
|
||||
void QextSerialPort::setTimeout(long millisec)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
Settings.Timeout_Millisec = millisec;
|
||||
Posix_Copy_Timeout.tv_sec = millisec / 1000;
|
||||
Posix_Copy_Timeout.tv_usec = millisec % 1000;
|
||||
if (isOpen()) {
|
||||
if (millisec == -1) {
|
||||
fcntl(fd, F_SETFL, O_NDELAY);
|
||||
} else {
|
||||
// O_SYNC should enable blocking ::write()
|
||||
// however this seems not working on Linux 2.6.21 (works on OpenBSD 4.2)
|
||||
fcntl(fd, F_SETFL, O_SYNC);
|
||||
}
|
||||
tcgetattr(fd, &Posix_CommConfig);
|
||||
Posix_CommConfig.c_cc[VTIME] = millisec / 100;
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Opens the serial port associated to this class.
|
||||
This function has no effect if the port associated with the class is already open.
|
||||
The port is also configured to the current settings, as stored in the Settings structure.
|
||||
*/
|
||||
bool QextSerialPort::open(OpenMode mode)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (mode == QIODevice::NotOpen) {
|
||||
return isOpen();
|
||||
}
|
||||
if (!isOpen()) {
|
||||
qDebug() << "trying to open file" << port.toAscii();
|
||||
// note: linux 2.6.21 seems to ignore O_NDELAY flag
|
||||
if ((fd = ::open(port.toAscii(), O_RDWR | O_NOCTTY | O_NDELAY)) != -1) {
|
||||
qDebug("file opened succesfully");
|
||||
|
||||
setOpenMode(mode); // Flag the port as opened
|
||||
tcgetattr(fd, &old_termios); // Save the old termios
|
||||
Posix_CommConfig = old_termios; // Make a working copy
|
||||
cfmakeraw(&Posix_CommConfig); // Enable raw access
|
||||
|
||||
/*set up other port settings*/
|
||||
Posix_CommConfig.c_cflag |= CREAD | CLOCAL;
|
||||
Posix_CommConfig.c_lflag &= (~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG));
|
||||
Posix_CommConfig.c_iflag &= (~(INPCK | IGNPAR | PARMRK | ISTRIP | ICRNL | IXANY));
|
||||
Posix_CommConfig.c_oflag &= (~OPOST);
|
||||
Posix_CommConfig.c_cc[VMIN] = 0;
|
||||
#ifdef _POSIX_VDISABLE // Is a disable character available on this system?
|
||||
// Some systems allow for per-device disable-characters, so get the
|
||||
// proper value for the configured device
|
||||
const long vdisable = fpathconf(fd, _PC_VDISABLE);
|
||||
Posix_CommConfig.c_cc[VINTR] = vdisable;
|
||||
Posix_CommConfig.c_cc[VQUIT] = vdisable;
|
||||
Posix_CommConfig.c_cc[VSTART] = vdisable;
|
||||
Posix_CommConfig.c_cc[VSTOP] = vdisable;
|
||||
Posix_CommConfig.c_cc[VSUSP] = vdisable;
|
||||
#endif // _POSIX_VDISABLE
|
||||
setBaudRate(Settings.BaudRate);
|
||||
setDataBits(Settings.DataBits);
|
||||
setParity(Settings.Parity);
|
||||
setStopBits(Settings.StopBits);
|
||||
setFlowControl(Settings.FlowControl);
|
||||
setTimeout(Settings.Timeout_Millisec);
|
||||
tcsetattr(fd, TCSAFLUSH, &Posix_CommConfig);
|
||||
|
||||
if (queryMode() == QextSerialPort::EventDriven) {
|
||||
readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
connect(readNotifier, SIGNAL(activated(int)), this, SIGNAL(readyRead()));
|
||||
}
|
||||
} else {
|
||||
qDebug() << "could not open file:" << strerror(errno);
|
||||
lastErr = E_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
/*!
|
||||
Closes a serial port. This function has no effect if the serial port associated with the class
|
||||
is not currently open.
|
||||
*/
|
||||
void QextSerialPort::close()
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
// Force a flush and then restore the original termios
|
||||
flush();
|
||||
// Using both TCSAFLUSH and TCSANOW here discards any pending input
|
||||
tcsetattr(fd, TCSAFLUSH | TCSANOW, &old_termios); // Restore termios
|
||||
// Be a good QIODevice and call QIODevice::close() before POSIX close()
|
||||
// so the aboutToClose() signal is emitted at the proper time
|
||||
QIODevice::close(); // Flag the device as closed
|
||||
// QIODevice::close() doesn't actually close the port, so do that here
|
||||
::close(fd);
|
||||
if (readNotifier) {
|
||||
delete readNotifier;
|
||||
readNotifier = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Flushes all pending I/O to the serial port. This function has no effect if the serial port
|
||||
associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::flush()
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
This function will return the number of bytes waiting in the receive queue of the serial port.
|
||||
It is included primarily to provide a complete QIODevice interface, and will not record errors
|
||||
in the lastErr member (because it is const). This function is also not thread-safe - in
|
||||
multithreading situations, use QextSerialPort::bytesWaiting() instead.
|
||||
*/
|
||||
qint64 QextSerialPort::size() const
|
||||
{
|
||||
int numBytes;
|
||||
|
||||
if (ioctl(fd, FIONREAD, &numBytes) < 0) {
|
||||
numBytes = 0;
|
||||
}
|
||||
return (qint64)numBytes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of bytes waiting in the port's receive queue. This function will return 0 if
|
||||
the port is not currently open, or -1 on error.
|
||||
*/
|
||||
qint64 QextSerialPort::bytesAvailable() const
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
int bytesQueued;
|
||||
if (ioctl(fd, FIONREAD, &bytesQueued) == -1) {
|
||||
return (qint64) - 1;
|
||||
}
|
||||
return bytesQueued + QIODevice::bytesAvailable();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is included to implement the full QIODevice interface, and currently has no
|
||||
purpose within this class. This function is meaningless on an unbuffered device and currently
|
||||
only prints a warning message to that effect.
|
||||
*/
|
||||
void QextSerialPort::ungetChar(char)
|
||||
{
|
||||
/*meaningless on unbuffered sequential device - return error and print a warning*/
|
||||
TTY_WARNING("QextSerialPort: ungetChar() called on an unbuffered sequential device - operation is meaningless");
|
||||
}
|
||||
|
||||
/*!
|
||||
Translates a system-specific error code to a QextSerialPort error code. Used internally.
|
||||
*/
|
||||
void QextSerialPort::translateError(ulong error)
|
||||
{
|
||||
switch (error) {
|
||||
case EBADF:
|
||||
case ENOTTY:
|
||||
lastErr = E_INVALID_FD;
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
lastErr = E_CAUGHT_NON_BLOCKED_SIGNAL;
|
||||
break;
|
||||
|
||||
case ENOMEM:
|
||||
lastErr = E_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets DTR line to the requested state (high by default). This function will have no effect if
|
||||
the port associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::setDtr(bool set)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
if (set) {
|
||||
status |= TIOCM_DTR;
|
||||
} else {
|
||||
status &= ~TIOCM_DTR;
|
||||
}
|
||||
ioctl(fd, TIOCMSET, &status);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets RTS line to the requested state (high by default). This function will have no effect if
|
||||
the port associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::setRts(bool set)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
if (set) {
|
||||
status |= TIOCM_RTS;
|
||||
} else {
|
||||
status &= ~TIOCM_RTS;
|
||||
}
|
||||
ioctl(fd, TIOCMSET, &status);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the line status as stored by the port function. This function will retrieve the states
|
||||
of the following lines: DCD, CTS, DSR, and RI. On POSIX systems, the following additional lines
|
||||
can be monitored: DTR, RTS, Secondary TXD, and Secondary RXD. The value returned is an unsigned
|
||||
long with specific bits indicating which lines are high. The following constants should be used
|
||||
to examine the states of individual lines:
|
||||
|
||||
\verbatim
|
||||
Mask Line
|
||||
------ ----
|
||||
LS_CTS CTS
|
||||
LS_DSR DSR
|
||||
LS_DCD DCD
|
||||
LS_RI RI
|
||||
LS_RTS RTS (POSIX only)
|
||||
LS_DTR DTR (POSIX only)
|
||||
LS_ST Secondary TXD (POSIX only)
|
||||
LS_SR Secondary RXD (POSIX only)
|
||||
\endverbatim
|
||||
|
||||
This function will return 0 if the port associated with the class is not currently open.
|
||||
*/
|
||||
unsigned long QextSerialPort::lineStatus()
|
||||
{
|
||||
unsigned long Status = 0, Temp = 0;
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
ioctl(fd, TIOCMGET, &Temp);
|
||||
if (Temp & TIOCM_CTS) {
|
||||
Status |= LS_CTS;
|
||||
}
|
||||
if (Temp & TIOCM_DSR) {
|
||||
Status |= LS_DSR;
|
||||
}
|
||||
if (Temp & TIOCM_RI) {
|
||||
Status |= LS_RI;
|
||||
}
|
||||
if (Temp & TIOCM_CD) {
|
||||
Status |= LS_DCD;
|
||||
}
|
||||
if (Temp & TIOCM_DTR) {
|
||||
Status |= LS_DTR;
|
||||
}
|
||||
if (Temp & TIOCM_RTS) {
|
||||
Status |= LS_RTS;
|
||||
}
|
||||
if (Temp & TIOCM_ST) {
|
||||
Status |= LS_ST;
|
||||
}
|
||||
if (Temp & TIOCM_SR) {
|
||||
Status |= LS_SR;
|
||||
}
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
||||
/*!
|
||||
Reads a block of data from the serial port. This function will read at most maxSize bytes from
|
||||
the serial port and place them in the buffer pointed to by data. Return value is the number of
|
||||
bytes actually read, or -1 on error.
|
||||
|
||||
\warning before calling this function ensure that serial port associated with this class
|
||||
is currently open (use isOpen() function to check if port is open).
|
||||
*/
|
||||
qint64 QextSerialPort::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
int retVal = ::read(fd, data, maxSize);
|
||||
|
||||
if (retVal == -1) {
|
||||
lastErr = E_READ_FAILED;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes a block of data to the serial port. This function will write maxSize bytes
|
||||
from the buffer pointed to by data to the serial port. Return value is the number
|
||||
of bytes actually written, or -1 on error.
|
||||
|
||||
\warning before calling this function ensure that serial port associated with this class
|
||||
is currently open (use isOpen() function to check if port is open).
|
||||
*/
|
||||
qint64 QextSerialPort::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
int retVal = ::write(fd, data, maxSize);
|
||||
|
||||
if (retVal == -1) {
|
||||
lastErr = E_WRITE_FAILED;
|
||||
}
|
||||
|
||||
return (qint64)retVal;
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
/*!
|
||||
* \file qextserialenumerator.h
|
||||
* \author Michal Policht
|
||||
* \see QextSerialEnumerator
|
||||
*/
|
||||
|
||||
#ifndef _QEXTSERIALENUMERATOR_H_
|
||||
#define _QEXTSERIALENUMERATOR_H_
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include "qextserialport_global.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <dbt.h>
|
||||
#endif /*Q_OS_WIN*/
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Structure containing port information.
|
||||
*/
|
||||
struct QextPortInfo {
|
||||
QString portName; ///< Port name.
|
||||
QString physName; ///< Physical name.
|
||||
QString friendName; ///< Friendly name.
|
||||
QString enumName; ///< Enumerator name.
|
||||
int vendorID; ///< Vendor ID.
|
||||
int productID; ///< Product ID
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef QT_GUI_LIB
|
||||
#include <QWidget>
|
||||
class QextSerialEnumerator;
|
||||
|
||||
class QextSerialRegistrationWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QextSerialRegistrationWidget(QextSerialEnumerator *qese)
|
||||
{
|
||||
this->qese = qese;
|
||||
}
|
||||
~QextSerialRegistrationWidget() {}
|
||||
|
||||
protected:
|
||||
QextSerialEnumerator *qese;
|
||||
bool winEvent(MSG *message, long *result);
|
||||
};
|
||||
#endif // QT_GUI_LIB
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
/*!
|
||||
Provides list of ports available in the system.
|
||||
|
||||
\section Usage
|
||||
To poll the system for a list of connected devices, simply use getPorts(). Each
|
||||
QextPortInfo structure will populated with information about the corresponding device.
|
||||
|
||||
\b Example
|
||||
\code
|
||||
QList<QextPortInfo> ports = QextSerialEnumerator::getPorts();
|
||||
foreach( QextPortInfo port, ports ) {
|
||||
// inspect port...
|
||||
}
|
||||
\endcode
|
||||
|
||||
To enable event-driven notification of device connection events, first call
|
||||
setUpNotifications() and then connect to the deviceDiscovered() and deviceRemoved()
|
||||
signals. Event-driven behavior is currently available only on Windows and OS X.
|
||||
|
||||
\b Example
|
||||
\code
|
||||
QextSerialEnumerator* enumerator = new QextSerialEnumerator();
|
||||
connect(enumerator, SIGNAL(deviceDiscovered(const QextPortInfo &)),
|
||||
myClass, SLOT(onDeviceDiscovered(const QextPortInfo &)));
|
||||
connect(enumerator, SIGNAL(deviceRemoved(const QextPortInfo &)),
|
||||
myClass, SLOT(onDeviceRemoved(const QextPortInfo &)));
|
||||
\endcode
|
||||
|
||||
\section Credits
|
||||
Windows implementation is based on Zach Gorman's work from
|
||||
<a href="http://www.codeproject.com">The Code Project</a> (http://www.codeproject.com/system/setupdi.asp).
|
||||
|
||||
OS X implementation, see
|
||||
http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html
|
||||
|
||||
\author Michal Policht, Liam Staskawicz
|
||||
*/
|
||||
class QEXTSERIALPORT_EXPORT QextSerialEnumerator : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QextSerialEnumerator();
|
||||
~QextSerialEnumerator();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
LRESULT onDeviceChangeWin(WPARAM wParam, LPARAM lParam);
|
||||
private:
|
||||
/*!
|
||||
* Get value of specified property from the registry.
|
||||
* \param key handle to an open key.
|
||||
* \param property property name.
|
||||
* \return property value.
|
||||
*/
|
||||
static QString getRegKeyValue(HKEY key, LPCTSTR property);
|
||||
|
||||
/*!
|
||||
* Get specific property from registry.
|
||||
* \param devInfo pointer to the device information set that contains the interface
|
||||
* and its underlying device. Returned by SetupDiGetClassDevs() function.
|
||||
* \param devData pointer to an SP_DEVINFO_DATA structure that defines the device instance.
|
||||
* this is returned by SetupDiGetDeviceInterfaceDetail() function.
|
||||
* \param property registry property. One of defined SPDRP_* constants.
|
||||
* \return property string.
|
||||
*/
|
||||
static QString getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property);
|
||||
|
||||
/*!
|
||||
* Search for serial ports using setupapi.
|
||||
* \param infoList list with result.
|
||||
*/
|
||||
static void setupAPIScan(QList<QextPortInfo> & infoList);
|
||||
void setUpNotificationWin();
|
||||
static bool getDeviceDetailsWin(QextPortInfo *portInfo, HDEVINFO devInfo,
|
||||
PSP_DEVINFO_DATA devData, WPARAM wParam = DBT_DEVICEARRIVAL);
|
||||
static void enumerateDevicesWin(const GUID & guidDev, QList<QextPortInfo> *infoList);
|
||||
bool matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam);
|
||||
#ifdef QT_GUI_LIB
|
||||
QextSerialRegistrationWidget *notificationWidget;
|
||||
#endif
|
||||
#endif /*Q_OS_WIN*/
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#ifdef Q_OS_MAC
|
||||
private:
|
||||
/*!
|
||||
* Search for serial ports using IOKit.
|
||||
* \param infoList list with result.
|
||||
*/
|
||||
static void scanPortsOSX(QList<QextPortInfo> & infoList);
|
||||
static void iterateServicesOSX(io_object_t service, QList<QextPortInfo> & infoList);
|
||||
static bool getServiceDetailsOSX(io_object_t service, QextPortInfo *portInfo);
|
||||
|
||||
void setUpNotificationOSX();
|
||||
void onDeviceDiscoveredOSX(io_object_t service);
|
||||
void onDeviceTerminatedOSX(io_object_t service);
|
||||
friend void deviceDiscoveredCallbackOSX(void *ctxt, io_iterator_t serialPortIterator);
|
||||
friend void deviceTerminatedCallbackOSX(void *ctxt, io_iterator_t serialPortIterator);
|
||||
|
||||
IONotificationPortRef notificationPortRef;
|
||||
|
||||
#else // Q_OS_MAC
|
||||
private:
|
||||
/*!
|
||||
* Search for serial ports on unix.
|
||||
* \param infoList list with result.
|
||||
*/
|
||||
static void scanPortsNix(QList<QextPortInfo> & infoList);
|
||||
#endif // Q_OS_MAC
|
||||
#endif /* Q_OS_UNIX */
|
||||
|
||||
public:
|
||||
/*!
|
||||
Get list of ports.
|
||||
\return list of ports currently available in the system.
|
||||
*/
|
||||
static QList<QextPortInfo> getPorts();
|
||||
/*!
|
||||
Enable event-driven notifications of board discovery/removal.
|
||||
*/
|
||||
void setUpNotifications();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
A new device has been connected to the system.
|
||||
|
||||
setUpNotifications() must be called first to enable event-driven device notifications.
|
||||
Currently only implemented on Windows and OS X.
|
||||
\param info The device that has been discovered.
|
||||
*/
|
||||
void deviceDiscovered(const QextPortInfo & info);
|
||||
/*!
|
||||
A device has been disconnected from the system.
|
||||
|
||||
setUpNotifications() must be called first to enable event-driven device notifications.
|
||||
Currently only implemented on Windows and OS X.
|
||||
\param info The device that was disconnected.
|
||||
*/
|
||||
void deviceRemoved(const QextPortInfo & info);
|
||||
};
|
||||
|
||||
#endif /*_QEXTSERIALENUMERATOR_H_*/
|
@ -1,285 +0,0 @@
|
||||
#include "qextserialenumerator.h"
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
#include <CoreFoundation/CFNumber.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator()
|
||||
{
|
||||
if (!QMetaType::isRegistered(QMetaType::type("QextPortInfo"))) {
|
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo");
|
||||
}
|
||||
}
|
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator()
|
||||
{
|
||||
IONotificationPortDestroy(notificationPortRef);
|
||||
}
|
||||
|
||||
// static
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts()
|
||||
{
|
||||
QList<QextPortInfo> infoList;
|
||||
io_iterator_t serialPortIterator = 0;
|
||||
kern_return_t kernResult = KERN_FAILURE;
|
||||
CFMutableDictionaryRef matchingDictionary;
|
||||
|
||||
// first try to get any serialbsd devices, then try any USBCDC devices
|
||||
if (!(matchingDictionary = IOServiceMatching(kIOSerialBSDServiceValue))) {
|
||||
qWarning("IOServiceMatching returned a NULL dictionary.");
|
||||
return infoList;
|
||||
}
|
||||
CFDictionaryAddValue(matchingDictionary, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
|
||||
|
||||
// then create the iterator with all the matching devices
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS) {
|
||||
qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult;
|
||||
return infoList;
|
||||
}
|
||||
iterateServicesOSX(serialPortIterator, infoList);
|
||||
IOObjectRelease(serialPortIterator);
|
||||
serialPortIterator = 0;
|
||||
|
||||
return infoList;
|
||||
}
|
||||
|
||||
void QextSerialEnumerator::iterateServicesOSX(io_object_t service, QList<QextPortInfo> & infoList)
|
||||
{
|
||||
// Iterate through all modems found.
|
||||
io_object_t usbService;
|
||||
|
||||
while ((usbService = IOIteratorNext(service))) {
|
||||
QextPortInfo info;
|
||||
info.vendorID = 0;
|
||||
info.productID = 0;
|
||||
getServiceDetailsOSX(usbService, &info);
|
||||
infoList.append(info);
|
||||
}
|
||||
}
|
||||
|
||||
bool QextSerialEnumerator::getServiceDetailsOSX(io_object_t service, QextPortInfo *portInfo)
|
||||
{
|
||||
bool retval = true;
|
||||
CFTypeRef bsdPathAsCFString = NULL;
|
||||
CFTypeRef productNameAsCFString = NULL;
|
||||
CFTypeRef vendorIdAsCFNumber = NULL;
|
||||
CFTypeRef productIdAsCFNumber = NULL;
|
||||
|
||||
// check the name of the modem's callout device
|
||||
bsdPathAsCFString = IORegistryEntryCreateCFProperty(service, CFSTR(kIOCalloutDeviceKey),
|
||||
kCFAllocatorDefault, 0);
|
||||
|
||||
// wander up the hierarchy until we find the level that can give us the
|
||||
// vendor/product IDs and the product name, if available
|
||||
io_registry_entry_t parent;
|
||||
kern_return_t kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
|
||||
while (kernResult == KERN_SUCCESS && !vendorIdAsCFNumber && !productIdAsCFNumber) {
|
||||
if (!productNameAsCFString) {
|
||||
productNameAsCFString = IORegistryEntrySearchCFProperty(parent,
|
||||
kIOServicePlane,
|
||||
CFSTR("Product Name"),
|
||||
kCFAllocatorDefault, 0);
|
||||
}
|
||||
vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
|
||||
kIOServicePlane,
|
||||
CFSTR(kUSBVendorID),
|
||||
kCFAllocatorDefault, 0);
|
||||
productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
|
||||
kIOServicePlane,
|
||||
CFSTR(kUSBProductID),
|
||||
kCFAllocatorDefault, 0);
|
||||
io_registry_entry_t oldparent = parent;
|
||||
kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
|
||||
IOObjectRelease(oldparent);
|
||||
}
|
||||
|
||||
io_string_t ioPathName;
|
||||
IORegistryEntryGetPath(service, kIOServicePlane, ioPathName);
|
||||
portInfo->portName = ioPathName;
|
||||
|
||||
|
||||
if (bsdPathAsCFString) {
|
||||
char path[MAXPATHLEN];
|
||||
if (CFStringGetCString((CFStringRef)bsdPathAsCFString, path,
|
||||
PATH_MAX, kCFStringEncodingUTF8)) {
|
||||
portInfo->physName = path;
|
||||
portInfo->friendName = path;
|
||||
}
|
||||
CFRelease(bsdPathAsCFString);
|
||||
}
|
||||
|
||||
if (productNameAsCFString) {
|
||||
char productName[MAXPATHLEN];
|
||||
if (CFStringGetCString((CFStringRef)productNameAsCFString, productName,
|
||||
PATH_MAX, kCFStringEncodingUTF8)) {
|
||||
portInfo->friendName = productName;
|
||||
}
|
||||
CFRelease(productNameAsCFString);
|
||||
}
|
||||
|
||||
if (vendorIdAsCFNumber) {
|
||||
SInt32 vID;
|
||||
if (CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID)) {
|
||||
portInfo->vendorID = vID;
|
||||
}
|
||||
CFRelease(vendorIdAsCFNumber);
|
||||
}
|
||||
|
||||
if (productIdAsCFNumber) {
|
||||
SInt32 pID;
|
||||
if (CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID)) {
|
||||
portInfo->productID = pID;
|
||||
}
|
||||
CFRelease(productIdAsCFNumber);
|
||||
}
|
||||
IOObjectRelease(service);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// IOKit callbacks registered via setupNotifications()
|
||||
void deviceDiscoveredCallbackOSX(void *ctxt, io_iterator_t serialPortIterator);
|
||||
void deviceTerminatedCallbackOSX(void *ctxt, io_iterator_t serialPortIterator);
|
||||
|
||||
void deviceDiscoveredCallbackOSX(void *ctxt, io_iterator_t serialPortIterator)
|
||||
{
|
||||
QextSerialEnumerator *qese = (QextSerialEnumerator *)ctxt;
|
||||
io_object_t serialService;
|
||||
|
||||
while ((serialService = IOIteratorNext(serialPortIterator))) {
|
||||
qese->onDeviceDiscoveredOSX(serialService);
|
||||
}
|
||||
}
|
||||
|
||||
void deviceTerminatedCallbackOSX(void *ctxt, io_iterator_t serialPortIterator)
|
||||
{
|
||||
QextSerialEnumerator *qese = (QextSerialEnumerator *)ctxt;
|
||||
io_object_t serialService;
|
||||
|
||||
while ((serialService = IOIteratorNext(serialPortIterator))) {
|
||||
qese->onDeviceTerminatedOSX(serialService);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
A device has been discovered via IOKit.
|
||||
Create a QextPortInfo if possible, and emit the signal indicating that we've found it.
|
||||
*/
|
||||
void QextSerialEnumerator::onDeviceDiscoveredOSX(io_object_t service)
|
||||
{
|
||||
QextPortInfo info;
|
||||
|
||||
info.vendorID = 0;
|
||||
info.productID = 0;
|
||||
if (getServiceDetailsOSX(service, &info)) {
|
||||
emit deviceDiscovered(info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Notification via IOKit that a device has been removed.
|
||||
Create a QextPortInfo if possible, and emit the signal indicating that it's gone.
|
||||
*/
|
||||
void QextSerialEnumerator::onDeviceTerminatedOSX(io_object_t service)
|
||||
{
|
||||
QextPortInfo info;
|
||||
|
||||
info.vendorID = 0;
|
||||
info.productID = 0;
|
||||
if (getServiceDetailsOSX(service, &info)) {
|
||||
emit deviceRemoved(info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Create matching dictionaries for the devices we want to get notifications for,
|
||||
and add them to the current run loop. Invoke the callbacks that will be responding
|
||||
to these notifications once to arm them, and discover any devices that
|
||||
are currently connected at the time notifications are setup.
|
||||
*/
|
||||
void QextSerialEnumerator::setUpNotifications()
|
||||
{
|
||||
kern_return_t kernResult;
|
||||
mach_port_t masterPort;
|
||||
CFRunLoopSourceRef notificationRunLoopSource;
|
||||
CFMutableDictionaryRef classesToMatch;
|
||||
CFMutableDictionaryRef cdcClassesToMatch;
|
||||
io_iterator_t portIterator;
|
||||
|
||||
kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
|
||||
if (KERN_SUCCESS != kernResult) {
|
||||
qDebug() << "IOMasterPort returned:" << kernResult;
|
||||
return;
|
||||
}
|
||||
|
||||
classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
|
||||
if (classesToMatch == NULL) {
|
||||
qDebug("IOServiceMatching returned a NULL dictionary.");
|
||||
} else {
|
||||
CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
|
||||
}
|
||||
|
||||
if (!(cdcClassesToMatch = IOServiceNameMatching("AppleUSBCDC"))) {
|
||||
qWarning("couldn't create cdc matching dict");
|
||||
return;
|
||||
}
|
||||
|
||||
// Retain an additional reference since each call to IOServiceAddMatchingNotification consumes one.
|
||||
classesToMatch = (CFMutableDictionaryRef)CFRetain(classesToMatch);
|
||||
cdcClassesToMatch = (CFMutableDictionaryRef)CFRetain(cdcClassesToMatch);
|
||||
|
||||
notificationPortRef = IONotificationPortCreate(masterPort);
|
||||
if (notificationPortRef == NULL) {
|
||||
qDebug("IONotificationPortCreate return a NULL IONotificationPortRef.");
|
||||
return;
|
||||
}
|
||||
|
||||
notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationPortRef);
|
||||
if (notificationRunLoopSource == NULL) {
|
||||
qDebug("IONotificationPortGetRunLoopSource returned NULL CFRunLoopSourceRef.");
|
||||
return;
|
||||
}
|
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, classesToMatch,
|
||||
deviceDiscoveredCallbackOSX, this, &portIterator);
|
||||
if (kernResult != KERN_SUCCESS) {
|
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// arm the callback, and grab any devices that are already connected
|
||||
deviceDiscoveredCallbackOSX(this, portIterator);
|
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, cdcClassesToMatch,
|
||||
deviceDiscoveredCallbackOSX, this, &portIterator);
|
||||
if (kernResult != KERN_SUCCESS) {
|
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// arm the callback, and grab any devices that are already connected
|
||||
deviceDiscoveredCallbackOSX(this, portIterator);
|
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, classesToMatch,
|
||||
deviceTerminatedCallbackOSX, this, &portIterator);
|
||||
if (kernResult != KERN_SUCCESS) {
|
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// arm the callback, and clear any devices that are terminated
|
||||
deviceTerminatedCallbackOSX(this, portIterator);
|
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, cdcClassesToMatch,
|
||||
deviceTerminatedCallbackOSX, this, &portIterator);
|
||||
if (kernResult != KERN_SUCCESS) {
|
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// arm the callback, and clear any devices that are terminated
|
||||
deviceTerminatedCallbackOSX(this, portIterator);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#include "qextserialenumerator.h"
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
#include <QStringList>
|
||||
#include <QDir>
|
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator()
|
||||
{
|
||||
if (!QMetaType::isRegistered(QMetaType::type("QextPortInfo"))) {
|
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo");
|
||||
}
|
||||
}
|
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator()
|
||||
{}
|
||||
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts()
|
||||
{
|
||||
QList<QextPortInfo> infoList;
|
||||
#ifdef Q_OS_LINUX
|
||||
QStringList portNamePrefixes, portNameList;
|
||||
portNamePrefixes << "ttyS*"; // list normal serial ports first
|
||||
|
||||
QDir dir("/dev");
|
||||
portNameList = dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);
|
||||
|
||||
// remove the values which are not serial ports for e.g. /dev/ttysa
|
||||
for (int i = 0; i < portNameList.size(); i++) {
|
||||
bool ok;
|
||||
QString current = portNameList.at(i);
|
||||
// remove the ttyS part, and check, if the other part is a number
|
||||
current.remove(0, 4).toInt(&ok, 10);
|
||||
if (!ok) {
|
||||
portNameList.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// get the non standard serial ports names
|
||||
// (USB-serial, bluetooth-serial, 18F PICs, and so on)
|
||||
// if you know an other name prefix for serial ports please let us know
|
||||
portNamePrefixes.clear();
|
||||
portNamePrefixes << "ttyACM*" << "ttyUSB*" << "rfcomm*";
|
||||
portNameList.append(dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name));
|
||||
|
||||
foreach(QString str, portNameList) {
|
||||
QextPortInfo inf;
|
||||
|
||||
inf.physName = "/dev/" + str;
|
||||
inf.portName = str;
|
||||
|
||||
if (str.contains("ttyS")) {
|
||||
inf.friendName = "Serial port " + str.remove(0, 4);
|
||||
} else if (str.contains("ttyUSB")) {
|
||||
inf.friendName = "USB-serial adapter " + str.remove(0, 6);
|
||||
} else if (str.contains("rfcomm")) {
|
||||
inf.friendName = "Bluetooth-serial adapter " + str.remove(0, 6);
|
||||
} else if (str.contains("ttyACM")) {
|
||||
inf.friendName = "USB VCP adapter " + str.remove(0, 6);
|
||||
}
|
||||
inf.enumName = "/dev"; // is there a more helpful name for this?
|
||||
infoList.append(inf);
|
||||
}
|
||||
#else // ifdef Q_OS_LINUX
|
||||
qCritical("Enumeration for POSIX systems (except Linux) is not implemented yet.");
|
||||
#endif // ifdef Q_OS_LINUX
|
||||
return infoList;
|
||||
}
|
||||
|
||||
void QextSerialEnumerator::setUpNotifications()
|
||||
{
|
||||
qCritical("Notifications for *Nix/FreeBSD are not implemented yet");
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
#include "qextserialenumerator.h"
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <objbase.h>
|
||||
#include <initguid.h>
|
||||
#include "qextserialport.h"
|
||||
#include <QRegExp>
|
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator()
|
||||
{
|
||||
if (!QMetaType::isRegistered(QMetaType::type("QextPortInfo"))) {
|
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo");
|
||||
}
|
||||
#if (defined QT_GUI_LIB)
|
||||
notificationWidget = 0;
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator()
|
||||
{
|
||||
#if (defined QT_GUI_LIB)
|
||||
if (notificationWidget) {
|
||||
delete notificationWidget;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/ms791134.aspx for list of GUID classes
|
||||
#ifndef GUID_DEVCLASS_PORTS
|
||||
DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18);
|
||||
#endif
|
||||
|
||||
/* Gordon Schumacher's macros for TCHAR -> QString conversions and vice versa */
|
||||
#ifdef UNICODE
|
||||
#define QStringToTCHAR(x) (wchar_t *)x.utf16()
|
||||
#define PQStringToTCHAR(x) (wchar_t *)x->utf16()
|
||||
#define TCHARToQString(x) QString::fromUtf16((ushort *)(x))
|
||||
#define TCHARToQStringN(x, y) QString::fromUtf16((ushort *)(x), (y))
|
||||
#else
|
||||
#define QStringToTCHAR(x) x.local8Bit().constData()
|
||||
#define PQStringToTCHAR(x) x->local8Bit().constData()
|
||||
#define TCHARToQString(x) QString::fromLocal8Bit((x))
|
||||
#define TCHARToQStringN(x, y) QString::fromLocal8Bit((x), (y))
|
||||
#endif /*UNICODE*/
|
||||
|
||||
|
||||
// static
|
||||
QString QextSerialEnumerator::getRegKeyValue(HKEY key, LPCTSTR property)
|
||||
{
|
||||
DWORD size = 0;
|
||||
DWORD type;
|
||||
|
||||
RegQueryValueEx(key, property, NULL, NULL, NULL, &size);
|
||||
BYTE *buff = new BYTE[size];
|
||||
QString result;
|
||||
if (RegQueryValueEx(key, property, NULL, &type, buff, &size) == ERROR_SUCCESS) {
|
||||
result = TCHARToQString(buff);
|
||||
}
|
||||
RegCloseKey(key);
|
||||
delete[] buff;
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
QString QextSerialEnumerator::getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property)
|
||||
{
|
||||
DWORD buffSize = 0;
|
||||
|
||||
SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, NULL, 0, &buffSize);
|
||||
BYTE *buff = new BYTE[buffSize];
|
||||
SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, buff, buffSize, NULL);
|
||||
QString result = TCHARToQString(buff);
|
||||
delete[] buff;
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts()
|
||||
{
|
||||
QList<QextPortInfo> ports;
|
||||
enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports);
|
||||
return ports;
|
||||
}
|
||||
|
||||
void QextSerialEnumerator::enumerateDevicesWin(const GUID & guid, QList<QextPortInfo> *infoList)
|
||||
{
|
||||
HDEVINFO devInfo;
|
||||
|
||||
if ((devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT)) != INVALID_HANDLE_VALUE) {
|
||||
SP_DEVINFO_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
for (int i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devInfoData); i++) {
|
||||
QextPortInfo info;
|
||||
info.productID = info.vendorID = 0;
|
||||
getDeviceDetailsWin(&info, devInfo, &devInfoData);
|
||||
infoList->append(info);
|
||||
}
|
||||
SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
bool QextSerialRegistrationWidget::winEvent(MSG *message, long *result)
|
||||
{
|
||||
if (message->message == WM_DEVICECHANGE) {
|
||||
qese->onDeviceChangeWin(message->wParam, message->lParam);
|
||||
*result = 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void QextSerialEnumerator::setUpNotifications()
|
||||
{
|
||||
#ifdef QT_GUI_LIB
|
||||
if (notificationWidget) {
|
||||
return;
|
||||
}
|
||||
notificationWidget = new QextSerialRegistrationWidget(this);
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE dbh;
|
||||
ZeroMemory(&dbh, sizeof(dbh));
|
||||
dbh.dbcc_size = sizeof(dbh);
|
||||
dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
CopyMemory(&dbh.dbcc_classguid, &GUID_DEVCLASS_PORTS, sizeof(GUID));
|
||||
if (RegisterDeviceNotification(notificationWidget->winId(), &dbh, DEVICE_NOTIFY_WINDOW_HANDLE) == NULL) {
|
||||
qWarning() << "RegisterDeviceNotification failed:" << GetLastError();
|
||||
}
|
||||
// setting up notifications doesn't tell us about devices already connected
|
||||
// so get those manually
|
||||
foreach(QextPortInfo port, getPorts())
|
||||
emit deviceDiscovered(port);
|
||||
#else
|
||||
qWarning("QextSerialEnumerator: GUI not enabled - can't register for device notifications.");
|
||||
#endif // QT_GUI_LIB
|
||||
}
|
||||
|
||||
LRESULT QextSerialEnumerator::onDeviceChangeWin(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) {
|
||||
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
|
||||
if (pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
|
||||
// delimiters are different across APIs...change to backslash. ugh.
|
||||
QString deviceID = TCHARToQString(pDevInf->dbcc_name).toUpper().replace("#", "\\");
|
||||
|
||||
matchAndDispatchChangedDevice(deviceID, GUID_DEVCLASS_PORTS, wParam);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool QextSerialEnumerator::matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam)
|
||||
{
|
||||
bool rv = false;
|
||||
DWORD dwFlag = (DBT_DEVICEARRIVAL == wParam) ? DIGCF_PRESENT : DIGCF_ALLCLASSES;
|
||||
HDEVINFO devInfo;
|
||||
|
||||
if ((devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, dwFlag)) != INVALID_HANDLE_VALUE) {
|
||||
SP_DEVINFO_DATA spDevInfoData;
|
||||
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
for (int i = 0; SetupDiEnumDeviceInfo(devInfo, i, &spDevInfoData); i++) {
|
||||
DWORD nSize = 0;
|
||||
TCHAR buf[MAX_PATH];
|
||||
if (SetupDiGetDeviceInstanceId(devInfo, &spDevInfoData, buf, MAX_PATH, &nSize) &&
|
||||
deviceID.contains(TCHARToQString(buf))) { // we found a match
|
||||
rv = true;
|
||||
QextPortInfo info;
|
||||
info.productID = info.vendorID = 0;
|
||||
getDeviceDetailsWin(&info, devInfo, &spDevInfoData, wParam);
|
||||
if (wParam == DBT_DEVICEARRIVAL) {
|
||||
emit deviceDiscovered(info);
|
||||
} else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
|
||||
emit deviceRemoved(info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool QextSerialEnumerator::getDeviceDetailsWin(QextPortInfo *portInfo, HDEVINFO devInfo, PSP_DEVINFO_DATA devData, WPARAM wParam)
|
||||
{
|
||||
portInfo->friendName = getDeviceProperty(devInfo, devData, SPDRP_FRIENDLYNAME);
|
||||
if (wParam == DBT_DEVICEARRIVAL) {
|
||||
portInfo->physName = getDeviceProperty(devInfo, devData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
|
||||
}
|
||||
portInfo->enumName = getDeviceProperty(devInfo, devData, SPDRP_ENUMERATOR_NAME);
|
||||
QString hardwareIDs = getDeviceProperty(devInfo, devData, SPDRP_HARDWAREID);
|
||||
HKEY devKey = SetupDiOpenDevRegKey(devInfo, devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
||||
portInfo->portName = QextSerialPort::fullPortNameWin(getRegKeyValue(devKey, TEXT("PortName")));
|
||||
QRegExp idRx("VID_(\\w+)&PID_(\\w+)");
|
||||
if (hardwareIDs.toUpper().contains(idRx)) {
|
||||
bool dummy;
|
||||
portInfo->vendorID = idRx.cap(1).toInt(&dummy, 16);
|
||||
portInfo->productID = idRx.cap(2).toInt(&dummy, 16);
|
||||
// qDebug() << "got vid:" << vid << "pid:" << pid;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include "qextserialport.h"
|
||||
|
||||
/*!
|
||||
Default constructor. Note that the name of the device used by a QextSerialPort constructed with
|
||||
this constructor will be determined by #defined constants, or lack thereof - the default behavior
|
||||
is the same as _TTY_LINUX_. Possible naming conventions and their associated constants are:
|
||||
|
||||
\verbatim
|
||||
|
||||
Constant Used By Naming Convention
|
||||
---------- ------------- ------------------------
|
||||
Q_OS_WIN Windows COM1, COM2
|
||||
_TTY_IRIX_ SGI/IRIX /dev/ttyf1, /dev/ttyf2
|
||||
_TTY_HPUX_ HP-UX /dev/tty1p0, /dev/tty2p0
|
||||
_TTY_SUN_ SunOS/Solaris /dev/ttya, /dev/ttyb
|
||||
_TTY_DIGITAL_ Digital UNIX /dev/tty01, /dev/tty02
|
||||
_TTY_FREEBSD_ FreeBSD /dev/ttyd0, /dev/ttyd1
|
||||
_TTY_OPENBSD_ OpenBSD /dev/tty00, /dev/tty01
|
||||
_TTY_LINUX_ Linux /dev/ttyS0, /dev/ttyS1
|
||||
<none> Linux /dev/ttyS0, /dev/ttyS1
|
||||
\endverbatim
|
||||
|
||||
This constructor assigns the device name to the name of the first port on the specified system.
|
||||
See the other constructors if you need to open a different port.
|
||||
*/
|
||||
QextSerialPort::QextSerialPort(QextSerialPort::QueryMode mode)
|
||||
: QIODevice()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
setPortName("COM1");
|
||||
|
||||
#elif defined(_TTY_IRIX_)
|
||||
setPortName("/dev/ttyf1");
|
||||
|
||||
#elif defined(_TTY_HPUX_)
|
||||
setPortName("/dev/tty1p0");
|
||||
|
||||
#elif defined(_TTY_SUN_)
|
||||
setPortName("/dev/ttya");
|
||||
|
||||
#elif defined(_TTY_DIGITAL_)
|
||||
setPortName("/dev/tty01");
|
||||
|
||||
#elif defined(_TTY_FREEBSD_)
|
||||
setPortName("/dev/ttyd1");
|
||||
|
||||
#elif defined(_TTY_OPENBSD_)
|
||||
setPortName("/dev/tty00");
|
||||
|
||||
#else // ifdef Q_OS_WIN
|
||||
setPortName("/dev/ttyS0");
|
||||
#endif // ifdef Q_OS_WIN
|
||||
|
||||
construct();
|
||||
setQueryMode(mode);
|
||||
platformSpecificInit();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a serial port attached to the port specified by name.
|
||||
name is the name of the device, which is windowsystem-specific,
|
||||
e.g."COM1" or "/dev/ttyS0".
|
||||
*/
|
||||
QextSerialPort::QextSerialPort(const QString & name, QextSerialPort::QueryMode mode)
|
||||
: QIODevice()
|
||||
{
|
||||
construct();
|
||||
setQueryMode(mode);
|
||||
setPortName(name);
|
||||
platformSpecificInit();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a port with default name and specified settings.
|
||||
*/
|
||||
QextSerialPort::QextSerialPort(const PortSettings & settings, QextSerialPort::QueryMode mode)
|
||||
: QIODevice()
|
||||
{
|
||||
construct();
|
||||
setBaudRate(settings.BaudRate);
|
||||
setDataBits(settings.DataBits);
|
||||
setParity(settings.Parity);
|
||||
setStopBits(settings.StopBits);
|
||||
setFlowControl(settings.FlowControl);
|
||||
setTimeout(settings.Timeout_Millisec);
|
||||
setQueryMode(mode);
|
||||
platformSpecificInit();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a port with specified name and settings.
|
||||
*/
|
||||
QextSerialPort::QextSerialPort(const QString & name, const PortSettings & settings, QextSerialPort::QueryMode mode)
|
||||
: QIODevice()
|
||||
{
|
||||
construct();
|
||||
setPortName(name);
|
||||
setBaudRate(settings.BaudRate);
|
||||
setDataBits(settings.DataBits);
|
||||
setParity(settings.Parity);
|
||||
setStopBits(settings.StopBits);
|
||||
setFlowControl(settings.FlowControl);
|
||||
setTimeout(settings.Timeout_Millisec);
|
||||
setQueryMode(mode);
|
||||
platformSpecificInit();
|
||||
}
|
||||
|
||||
/*!
|
||||
Common constructor function for setting up default port settings.
|
||||
(115200 Baud, 8N1, Hardware flow control where supported, otherwise no flow control, and 0 ms timeout).
|
||||
*/
|
||||
void QextSerialPort::construct()
|
||||
{
|
||||
lastErr = E_NO_ERROR;
|
||||
Settings.BaudRate = BAUD115200;
|
||||
Settings.DataBits = DATA_8;
|
||||
Settings.Parity = PAR_NONE;
|
||||
Settings.StopBits = STOP_1;
|
||||
Settings.FlowControl = FLOW_HARDWARE;
|
||||
Settings.Timeout_Millisec = 500;
|
||||
mutex = new QMutex(QMutex::Recursive);
|
||||
setOpenMode(QIODevice::NotOpen);
|
||||
}
|
||||
|
||||
void QextSerialPort::setQueryMode(QueryMode mechanism)
|
||||
{
|
||||
_queryMode = mechanism;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the name of the device associated with the object, e.g. "COM1", or "/dev/ttyS0".
|
||||
*/
|
||||
void QextSerialPort::setPortName(const QString & name)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
port = fullPortNameWin(name);
|
||||
#else
|
||||
port = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the name set by setPortName().
|
||||
*/
|
||||
QString QextSerialPort::portName() const
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
/*!
|
||||
Reads all available data from the device, and returns it as a QByteArray.
|
||||
This function has no way of reporting errors; returning an empty QByteArray()
|
||||
can mean either that no data was currently available for reading, or that an error occurred.
|
||||
*/
|
||||
QByteArray QextSerialPort::readAll()
|
||||
{
|
||||
int avail = this->bytesAvailable();
|
||||
|
||||
return (avail > 0) ? this->read(avail) : QByteArray();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the baud rate of the serial port. For a list of possible return values see
|
||||
the definition of the enum BaudRateType.
|
||||
*/
|
||||
BaudRateType QextSerialPort::baudRate(void) const
|
||||
{
|
||||
return Settings.BaudRate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of data bits used by the port. For a list of possible values returned by
|
||||
this function, see the definition of the enum DataBitsType.
|
||||
*/
|
||||
DataBitsType QextSerialPort::dataBits() const
|
||||
{
|
||||
return Settings.DataBits;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the type of parity used by the port. For a list of possible values returned by
|
||||
this function, see the definition of the enum ParityType.
|
||||
*/
|
||||
ParityType QextSerialPort::parity() const
|
||||
{
|
||||
return Settings.Parity;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of stop bits used by the port. For a list of possible return values, see
|
||||
the definition of the enum StopBitsType.
|
||||
*/
|
||||
StopBitsType QextSerialPort::stopBits() const
|
||||
{
|
||||
return Settings.StopBits;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the type of flow control used by the port. For a list of possible values returned
|
||||
by this function, see the definition of the enum FlowType.
|
||||
*/
|
||||
FlowType QextSerialPort::flowControl() const
|
||||
{
|
||||
return Settings.FlowControl;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if device is sequential, otherwise returns false. Serial port is sequential device
|
||||
so this function always returns true. Check QIODevice::isSequential() documentation for more
|
||||
information.
|
||||
*/
|
||||
bool QextSerialPort::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QextSerialPort::errorString()
|
||||
{
|
||||
switch (lastErr) {
|
||||
case E_NO_ERROR: return "No Error has occurred";
|
||||
|
||||
case E_INVALID_FD: return "Invalid file descriptor (port was not opened correctly)";
|
||||
|
||||
case E_NO_MEMORY: return "Unable to allocate memory tables (POSIX)";
|
||||
|
||||
case E_CAUGHT_NON_BLOCKED_SIGNAL: return "Caught a non-blocked signal (POSIX)";
|
||||
|
||||
case E_PORT_TIMEOUT: return "Operation timed out (POSIX)";
|
||||
|
||||
case E_INVALID_DEVICE: return "The file opened by the port is not a valid device";
|
||||
|
||||
case E_BREAK_CONDITION: return "The port detected a break condition";
|
||||
|
||||
case E_FRAMING_ERROR: return "The port detected a framing error (usually caused by incorrect baud rate settings)";
|
||||
|
||||
case E_IO_ERROR: return "There was an I/O error while communicating with the port";
|
||||
|
||||
case E_BUFFER_OVERRUN: return "Character buffer overrun";
|
||||
|
||||
case E_RECEIVE_OVERFLOW: return "Receive buffer overflow";
|
||||
|
||||
case E_RECEIVE_PARITY_ERROR: return "The port detected a parity error in the received data";
|
||||
|
||||
case E_TRANSMIT_OVERFLOW: return "Transmit buffer overflow";
|
||||
|
||||
case E_READ_FAILED: return "General read operation failure";
|
||||
|
||||
case E_WRITE_FAILED: return "General write operation failure";
|
||||
|
||||
case E_FILE_NOT_FOUND: return "The " + this->portName() + " file doesn't exists";
|
||||
|
||||
default: return QString("Unknown error: %1").arg(lastErr);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Standard destructor.
|
||||
*/
|
||||
QextSerialPort::~QextSerialPort()
|
||||
{
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
platformSpecificDestruct();
|
||||
delete mutex;
|
||||
}
|
@ -1,334 +0,0 @@
|
||||
#ifndef _QEXTSERIALPORT_H_
|
||||
#define _QEXTSERIALPORT_H_
|
||||
|
||||
#include "qextserialport_global.h"
|
||||
|
||||
/*if all warning messages are turned off, flag portability warnings to be turned off as well*/
|
||||
#ifdef _TTY_NOWARN_
|
||||
#define _TTY_NOWARN_PORT_
|
||||
#endif
|
||||
|
||||
/*macros for warning and debug messages*/
|
||||
#ifdef _TTY_NOWARN_PORT_
|
||||
#define TTY_PORTABILITY_WARNING(s)
|
||||
#else
|
||||
#define TTY_PORTABILITY_WARNING(s) qWarning(s)
|
||||
#endif /*_TTY_NOWARN_PORT_*/
|
||||
#ifdef _TTY_NOWARN_
|
||||
#define TTY_WARNING(s)
|
||||
#else
|
||||
#define TTY_WARNING(s) qWarning(s)
|
||||
#endif /*_TTY_NOWARN_*/
|
||||
|
||||
|
||||
/*line status constants*/
|
||||
#define LS_CTS 0x01
|
||||
#define LS_DSR 0x02
|
||||
#define LS_DCD 0x04
|
||||
#define LS_RI 0x08
|
||||
#define LS_RTS 0x10
|
||||
#define LS_DTR 0x20
|
||||
#define LS_ST 0x40
|
||||
#define LS_SR 0x80
|
||||
|
||||
/*error constants*/
|
||||
#define E_NO_ERROR 0
|
||||
#define E_INVALID_FD 1
|
||||
#define E_NO_MEMORY 2
|
||||
#define E_CAUGHT_NON_BLOCKED_SIGNAL 3
|
||||
#define E_PORT_TIMEOUT 4
|
||||
#define E_INVALID_DEVICE 5
|
||||
#define E_BREAK_CONDITION 6
|
||||
#define E_FRAMING_ERROR 7
|
||||
#define E_IO_ERROR 8
|
||||
#define E_BUFFER_OVERRUN 9
|
||||
#define E_RECEIVE_OVERFLOW 10
|
||||
#define E_RECEIVE_PARITY_ERROR 11
|
||||
#define E_TRANSMIT_OVERFLOW 12
|
||||
#define E_READ_FAILED 13
|
||||
#define E_WRITE_FAILED 14
|
||||
#define E_FILE_NOT_FOUND 15
|
||||
|
||||
enum BaudRateType {
|
||||
BAUD50, // POSIX ONLY
|
||||
BAUD75, // POSIX ONLY
|
||||
BAUD110,
|
||||
BAUD134, // POSIX ONLY
|
||||
BAUD150, // POSIX ONLY
|
||||
BAUD200, // POSIX ONLY
|
||||
BAUD300,
|
||||
BAUD600,
|
||||
BAUD1200,
|
||||
BAUD1800, // POSIX ONLY
|
||||
BAUD2400,
|
||||
BAUD4800,
|
||||
BAUD9600,
|
||||
BAUD14400, // WINDOWS ONLY
|
||||
BAUD19200,
|
||||
BAUD38400,
|
||||
BAUD56000, // WINDOWS ONLY
|
||||
BAUD57600,
|
||||
BAUD76800, // POSIX ONLY
|
||||
BAUD115200,
|
||||
BAUD128000, // WINDOWS ONLY
|
||||
BAUD230400, // WINDOWS ONLY
|
||||
BAUD256000, // WINDOWS ONLY
|
||||
BAUD460800, // WINDOWS ONLY
|
||||
BAUD921600 // WINDOWS ONLY
|
||||
};
|
||||
|
||||
enum DataBitsType {
|
||||
DATA_5,
|
||||
DATA_6,
|
||||
DATA_7,
|
||||
DATA_8
|
||||
};
|
||||
|
||||
enum ParityType {
|
||||
PAR_NONE,
|
||||
PAR_ODD,
|
||||
PAR_EVEN,
|
||||
PAR_MARK, // WINDOWS ONLY
|
||||
PAR_SPACE
|
||||
};
|
||||
|
||||
enum StopBitsType {
|
||||
STOP_1,
|
||||
STOP_1_5, // WINDOWS ONLY
|
||||
STOP_2
|
||||
};
|
||||
|
||||
enum FlowType {
|
||||
FLOW_OFF,
|
||||
FLOW_HARDWARE,
|
||||
FLOW_XONXOFF
|
||||
};
|
||||
|
||||
/**
|
||||
* structure to contain port settings
|
||||
*/
|
||||
struct PortSettings {
|
||||
BaudRateType BaudRate;
|
||||
DataBitsType DataBits;
|
||||
ParityType Parity;
|
||||
StopBitsType StopBits;
|
||||
FlowType FlowControl;
|
||||
long Timeout_Millisec;
|
||||
};
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <QSocketNotifier>
|
||||
#elif (defined Q_OS_WIN)
|
||||
#include <QTimer>
|
||||
#include <windows.h>
|
||||
#include <QThread>
|
||||
#include <QReadWriteLock>
|
||||
#include "utils/qwineventnotifier_p.h"
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Encapsulates a serial port on both POSIX and Windows systems.
|
||||
|
||||
\note
|
||||
Be sure to check the full list of members, as QIODevice provides quite a lot of
|
||||
functionality for QextSerialPort.
|
||||
|
||||
\section Usage
|
||||
QextSerialPort offers both a polling and event driven API. Event driven is typically easier
|
||||
to use, since you never have to worry about checking for new data.
|
||||
|
||||
\b Example
|
||||
\code
|
||||
QextSerialPort* port = new QextSerialPort("COM1", QextSerialPort::EventDriven);
|
||||
connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable()));
|
||||
port->open();
|
||||
|
||||
void MyClass::onDataAvailable() {
|
||||
int avail = port->bytesAvailable();
|
||||
if( avail > 0 ) {
|
||||
QByteArray usbdata;
|
||||
usbdata.resize(avail);
|
||||
int read = port->read(usbdata.data(), usbdata.size());
|
||||
if( read > 0 ) {
|
||||
processNewData(usbdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section Compatibility
|
||||
The user will be notified of errors and possible portability conflicts at run-time
|
||||
by default - this behavior can be turned off by defining _TTY_NOWARN_
|
||||
(to turn off all warnings) or _TTY_NOWARN_PORT_ (to turn off portability warnings) in the project.
|
||||
|
||||
On Windows NT/2000/XP this class uses Win32 serial port functions by default. The user may
|
||||
select POSIX behavior under NT, 2000, or XP ONLY by defining Q_OS_UNIX in the project.
|
||||
No guarantees are made as to the quality of POSIX support under NT/2000 however.
|
||||
|
||||
\author Stefan Sander, Michal Policht, Brandon Fosdick, Liam Staskawicz
|
||||
*/
|
||||
class QEXTSERIALPORT_EXPORT QextSerialPort : public QIODevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum QueryMode {
|
||||
Polling,
|
||||
EventDriven
|
||||
};
|
||||
|
||||
QextSerialPort(QueryMode mode = EventDriven);
|
||||
QextSerialPort(const QString & name, QueryMode mode = EventDriven);
|
||||
QextSerialPort(PortSettings const & s, QueryMode mode = EventDriven);
|
||||
QextSerialPort(const QString & name, PortSettings const & s, QueryMode mode = EventDriven);
|
||||
~QextSerialPort();
|
||||
|
||||
void setPortName(const QString & name);
|
||||
QString portName() const;
|
||||
|
||||
/**!
|
||||
* Get query mode.
|
||||
* \return query mode.
|
||||
*/
|
||||
inline QueryMode queryMode() const
|
||||
{
|
||||
return _queryMode;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set desired serial communication handling style. You may choose from polling
|
||||
* or event driven approach. This function does nothing when port is open; to
|
||||
* apply changes port must be reopened.
|
||||
*
|
||||
* In event driven approach read() and write() functions are acting
|
||||
* asynchronously. They return immediately and the operation is performed in
|
||||
* the background, so they doesn't freeze the calling thread.
|
||||
* To determine when operation is finished, QextSerialPort runs separate thread
|
||||
* and monitors serial port events. Whenever the event occurs, adequate signal
|
||||
* is emitted.
|
||||
*
|
||||
* When polling is set, read() and write() are acting synchronously. Signals are
|
||||
* not working in this mode and some functions may not be available. The advantage
|
||||
* of polling is that it generates less overhead due to lack of signals emissions
|
||||
* and it doesn't start separate thread to monitor events.
|
||||
*
|
||||
* Generally event driven approach is more capable and friendly, although some
|
||||
* applications may need as low overhead as possible and then polling comes.
|
||||
*
|
||||
* \param mode query mode.
|
||||
*/
|
||||
void setQueryMode(QueryMode mode);
|
||||
|
||||
void setBaudRate(BaudRateType);
|
||||
BaudRateType baudRate() const;
|
||||
|
||||
void setDataBits(DataBitsType);
|
||||
DataBitsType dataBits() const;
|
||||
|
||||
void setParity(ParityType);
|
||||
ParityType parity() const;
|
||||
|
||||
void setStopBits(StopBitsType);
|
||||
StopBitsType stopBits() const;
|
||||
|
||||
void setFlowControl(FlowType);
|
||||
FlowType flowControl() const;
|
||||
|
||||
void setTimeout(long);
|
||||
|
||||
bool open(OpenMode mode);
|
||||
bool isSequential() const;
|
||||
void close();
|
||||
void flush();
|
||||
|
||||
qint64 size() const;
|
||||
qint64 bytesAvailable() const;
|
||||
QByteArray readAll();
|
||||
|
||||
void ungetChar(char c);
|
||||
|
||||
ulong lastError() const;
|
||||
void translateError(ulong error);
|
||||
|
||||
void setDtr(bool set = true);
|
||||
void setRts(bool set = true);
|
||||
ulong lineStatus();
|
||||
QString errorString();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
virtual bool waitForReadyRead(int msecs); ///< @todo implement.
|
||||
virtual qint64 bytesToWrite() const;
|
||||
static QString fullPortNameWin(const QString & name);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
QMutex *mutex;
|
||||
QString port;
|
||||
PortSettings Settings;
|
||||
ulong lastErr;
|
||||
QueryMode _queryMode;
|
||||
|
||||
// platform specific members
|
||||
#ifdef Q_OS_UNIX
|
||||
int fd;
|
||||
QSocketNotifier *readNotifier;
|
||||
struct termios Posix_CommConfig;
|
||||
struct termios old_termios;
|
||||
struct timeval Posix_Timeout;
|
||||
struct timeval Posix_Copy_Timeout;
|
||||
#elif (defined Q_OS_WIN)
|
||||
HANDLE Win_Handle;
|
||||
OVERLAPPED overlap;
|
||||
COMMCONFIG Win_CommConfig;
|
||||
COMMTIMEOUTS Win_CommTimeouts;
|
||||
QWinEventNotifier *winEventNotifier;
|
||||
DWORD eventMask;
|
||||
QList<OVERLAPPED *> pendingWrites;
|
||||
QReadWriteLock *bytesToWriteLock;
|
||||
qint64 _bytesToWrite;
|
||||
#endif
|
||||
|
||||
void construct(); // common construction
|
||||
void platformSpecificDestruct();
|
||||
void platformSpecificInit();
|
||||
qint64 readData(char *data, qint64 maxSize);
|
||||
qint64 writeData(const char *data, qint64 maxSize);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
private slots:
|
||||
void onWinEvent(HANDLE h);
|
||||
void triggerTxEmpty();
|
||||
private:
|
||||
QTimer fakeTxEmpty;
|
||||
#endif
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QextSerialPort)
|
||||
|
||||
signals:
|
||||
///**
|
||||
// * This signal is emitted whenever port settings are updated.
|
||||
// * \param valid \p true if settings are valid, \p false otherwise.
|
||||
// *
|
||||
// * @todo implement.
|
||||
// */
|
||||
//// void validSettings(bool valid);
|
||||
|
||||
/*!
|
||||
* This signal is emitted whenever dsr line has changed its state. You may
|
||||
* use this signal to check if device is connected.
|
||||
* \param status \p true when DSR signal is on, \p false otherwise.
|
||||
*
|
||||
* \see lineStatus().
|
||||
*/
|
||||
void dsrChanged(bool status);
|
||||
};
|
||||
|
||||
#endif // ifndef _QEXTSERIALPORT_H_
|
@ -1,12 +0,0 @@
|
||||
#ifndef QEXTSERIALPORT_GLOBAL_H
|
||||
#define QEXTSERIALPORT_GLOBAL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifdef QEXTSERIALPORT_LIBRARY
|
||||
# define QEXTSERIALPORT_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define QEXTSERIALPORT_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#endif // QEXTSERIALPORT_GLOBAL_H
|
@ -1,28 +0,0 @@
|
||||
TEMPLATE = lib
|
||||
TARGET = QExtSerialPort
|
||||
DEFINES += QEXTSERIALPORT_LIBRARY
|
||||
include(../../../openpilotgcslibrary.pri)
|
||||
|
||||
#VERSION = 1.2.0
|
||||
|
||||
# event driven device enumeration on windows requires the gui module
|
||||
!win32:QT -= gui
|
||||
|
||||
HEADERS = qextserialport.h \
|
||||
qextserialenumerator.h \
|
||||
qextserialport_global.h
|
||||
SOURCES = qextserialport.cpp
|
||||
|
||||
unix:SOURCES += posix_qextserialport.cpp
|
||||
unix:!macx:SOURCES += qextserialenumerator_unix.cpp
|
||||
macx {
|
||||
SOURCES += qextserialenumerator_osx.cpp
|
||||
LIBS += -framework IOKit -framework CoreFoundation
|
||||
}
|
||||
|
||||
win32 {
|
||||
SOURCES += win_qextserialport.cpp qextserialenumerator_win.cpp
|
||||
DEFINES += WINVER=0x0501 # needed for mingw to pull in appropriate dbt business...probably a better way to do this
|
||||
LIBS += -lsetupapi
|
||||
}
|
||||
|
@ -1,944 +0,0 @@
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
#include <QRegExp>
|
||||
#include "qextserialport.h"
|
||||
|
||||
#if (defined Q_OS_WIN)
|
||||
#define CBR_230400 230400
|
||||
#define CBR_460800 460800
|
||||
#define CBR_921600 921600
|
||||
#endif
|
||||
|
||||
void QextSerialPort::platformSpecificInit()
|
||||
{
|
||||
Win_Handle = INVALID_HANDLE_VALUE;
|
||||
ZeroMemory(&overlap, sizeof(OVERLAPPED));
|
||||
overlap.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
winEventNotifier = 0;
|
||||
bytesToWriteLock = new QReadWriteLock;
|
||||
_bytesToWrite = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Standard destructor.
|
||||
*/
|
||||
void QextSerialPort::platformSpecificDestruct()
|
||||
{
|
||||
CloseHandle(overlap.hEvent);
|
||||
delete bytesToWriteLock;
|
||||
}
|
||||
|
||||
QString QextSerialPort::fullPortNameWin(const QString & name)
|
||||
{
|
||||
QRegExp rx("^COM(\\d+)");
|
||||
QString fullName(name);
|
||||
|
||||
if (fullName.contains(rx)) {
|
||||
int portnum = rx.cap(1).toInt();
|
||||
if (portnum > 9) { // COM ports greater than 9 need \\.\ prepended
|
||||
fullName.prepend("\\\\.\\");
|
||||
}
|
||||
}
|
||||
return fullName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Opens a serial port. Note that this function does not specify which device to open. If you need
|
||||
to open a device by name, see QextSerialPort::open(const char*). This function has no effect
|
||||
if the port associated with the class is already open. The port is also configured to the current
|
||||
settings, as stored in the Settings structure.
|
||||
*/
|
||||
bool QextSerialPort::open(OpenMode mode)
|
||||
{
|
||||
unsigned long confSize = sizeof(COMMCONFIG);
|
||||
|
||||
Win_CommConfig.dwSize = confSize;
|
||||
DWORD dwFlagsAndAttributes = 0;
|
||||
if (queryMode() == QextSerialPort::EventDriven) {
|
||||
dwFlagsAndAttributes += FILE_FLAG_OVERLAPPED;
|
||||
}
|
||||
QMutexLocker lock(mutex);
|
||||
if (mode == QIODevice::NotOpen) {
|
||||
return isOpen();
|
||||
}
|
||||
if (!isOpen()) {
|
||||
/*open the port*/
|
||||
Win_Handle = CreateFileA(port.toAscii(), GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
|
||||
if (Win_Handle != INVALID_HANDLE_VALUE) {
|
||||
QIODevice::open(mode);
|
||||
/*configure port settings*/
|
||||
GetCommConfig(Win_Handle, &Win_CommConfig, &confSize);
|
||||
GetCommState(Win_Handle, &(Win_CommConfig.dcb));
|
||||
|
||||
/*set up parameters*/
|
||||
Win_CommConfig.dcb.fBinary = TRUE;
|
||||
Win_CommConfig.dcb.fInX = FALSE;
|
||||
Win_CommConfig.dcb.fOutX = FALSE;
|
||||
Win_CommConfig.dcb.fAbortOnError = FALSE;
|
||||
Win_CommConfig.dcb.fNull = FALSE;
|
||||
setBaudRate(Settings.BaudRate);
|
||||
setDataBits(Settings.DataBits);
|
||||
setStopBits(Settings.StopBits);
|
||||
setParity(Settings.Parity);
|
||||
setFlowControl(Settings.FlowControl);
|
||||
setTimeout(Settings.Timeout_Millisec);
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
|
||||
// init event driven approach
|
||||
if (queryMode() == QextSerialPort::EventDriven) {
|
||||
Win_CommTimeouts.ReadIntervalTimeout = MAXDWORD;
|
||||
Win_CommTimeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
Win_CommTimeouts.ReadTotalTimeoutConstant = 0;
|
||||
Win_CommTimeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
Win_CommTimeouts.WriteTotalTimeoutConstant = 0;
|
||||
SetCommTimeouts(Win_Handle, &Win_CommTimeouts);
|
||||
if (!SetCommMask(Win_Handle, EV_TXEMPTY | EV_RXCHAR | EV_DSR)) {
|
||||
qWarning() << "failed to set Comm Mask. Error code:", GetLastError();
|
||||
return false;
|
||||
}
|
||||
winEventNotifier = new QWinEventNotifier(overlap.hEvent, this);
|
||||
connect(winEventNotifier, SIGNAL(activated(HANDLE)), this, SLOT(onWinEvent(HANDLE)));
|
||||
connect(&fakeTxEmpty, SIGNAL(timeout()), this, SLOT(triggerTxEmpty()));
|
||||
fakeTxEmpty.start(10000);
|
||||
WaitCommEvent(Win_Handle, &eventMask, &overlap);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
/*!
|
||||
Closes a serial port. This function has no effect if the serial port associated with the class
|
||||
is not currently open.
|
||||
*/
|
||||
void QextSerialPort::close()
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
fakeTxEmpty.stop();
|
||||
disconnect(&fakeTxEmpty, SIGNAL(timeout()), this, SLOT(triggerTxEmpty()));
|
||||
if (isOpen()) {
|
||||
flush();
|
||||
QIODevice::close(); // mark ourselves as closed
|
||||
CancelIo(Win_Handle);
|
||||
if (CloseHandle(Win_Handle)) {
|
||||
Win_Handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (winEventNotifier) {
|
||||
winEventNotifier->deleteLater();
|
||||
}
|
||||
|
||||
_bytesToWrite = 0;
|
||||
|
||||
foreach(OVERLAPPED * o, pendingWrites) {
|
||||
CloseHandle(o->hEvent);
|
||||
delete o;
|
||||
}
|
||||
pendingWrites.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Flushes all pending I/O to the serial port. This function has no effect if the serial port
|
||||
associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::flush()
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
FlushFileBuffers(Win_Handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
This function will return the number of bytes waiting in the receive queue of the serial port.
|
||||
It is included primarily to provide a complete QIODevice interface, and will not record errors
|
||||
in the lastErr member (because it is const). This function is also not thread-safe - in
|
||||
multithreading situations, use QextSerialPort::bytesAvailable() instead.
|
||||
*/
|
||||
qint64 QextSerialPort::size() const
|
||||
{
|
||||
int availBytes;
|
||||
COMSTAT Win_ComStat;
|
||||
DWORD Win_ErrorMask = 0;
|
||||
|
||||
ClearCommError(Win_Handle, &Win_ErrorMask, &Win_ComStat);
|
||||
availBytes = Win_ComStat.cbInQue;
|
||||
return (qint64)availBytes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of bytes waiting in the port's receive queue. This function will return 0 if
|
||||
the port is not currently open, or -1 on error.
|
||||
*/
|
||||
qint64 QextSerialPort::bytesAvailable() const
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
DWORD Errors;
|
||||
COMSTAT Status;
|
||||
if (ClearCommError(Win_Handle, &Errors, &Status)) {
|
||||
return Status.cbInQue + QIODevice::bytesAvailable();
|
||||
}
|
||||
return (qint64) - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Translates a system-specific error code to a QextSerialPort error code. Used internally.
|
||||
*/
|
||||
void QextSerialPort::translateError(ulong error)
|
||||
{
|
||||
if (error & CE_BREAK) {
|
||||
lastErr = E_BREAK_CONDITION;
|
||||
} else if (error & CE_FRAME) {
|
||||
lastErr = E_FRAMING_ERROR;
|
||||
} else if (error & CE_IOE) {
|
||||
lastErr = E_IO_ERROR;
|
||||
} else if (error & CE_MODE) {
|
||||
lastErr = E_INVALID_FD;
|
||||
} else if (error & CE_OVERRUN) {
|
||||
lastErr = E_BUFFER_OVERRUN;
|
||||
} else if (error & CE_RXPARITY) {
|
||||
lastErr = E_RECEIVE_PARITY_ERROR;
|
||||
} else if (error & CE_RXOVER) {
|
||||
lastErr = E_RECEIVE_OVERFLOW;
|
||||
} else if (error & CE_TXFULL) {
|
||||
lastErr = E_TRANSMIT_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Reads a block of data from the serial port. This function will read at most maxlen bytes from
|
||||
the serial port and place them in the buffer pointed to by data. Return value is the number of
|
||||
bytes actually read, or -1 on error.
|
||||
|
||||
\warning before calling this function ensure that serial port associated with this class
|
||||
is currently open (use isOpen() function to check if port is open).
|
||||
*/
|
||||
qint64 QextSerialPort::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
DWORD retVal;
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
retVal = 0;
|
||||
if (queryMode() == QextSerialPort::EventDriven) {
|
||||
OVERLAPPED overlapRead;
|
||||
ZeroMemory(&overlapRead, sizeof(OVERLAPPED));
|
||||
if (!ReadFile(Win_Handle, (void *)data, (DWORD)maxSize, &retVal, &overlapRead)) {
|
||||
if (GetLastError() == ERROR_IO_PENDING) {
|
||||
GetOverlappedResult(Win_Handle, &overlapRead, &retVal, true);
|
||||
} else {
|
||||
lastErr = E_READ_FAILED;
|
||||
retVal = (DWORD)-1;
|
||||
}
|
||||
}
|
||||
} else if (!ReadFile(Win_Handle, (void *)data, (DWORD)maxSize, &retVal, NULL)) {
|
||||
lastErr = E_READ_FAILED;
|
||||
retVal = (DWORD)-1;
|
||||
}
|
||||
return (qint64)retVal;
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes a block of data to the serial port. This function will write len bytes
|
||||
from the buffer pointed to by data to the serial port. Return value is the number
|
||||
of bytes actually written, or -1 on error.
|
||||
|
||||
\warning before calling this function ensure that serial port associated with this class
|
||||
is currently open (use isOpen() function to check if port is open).
|
||||
*/
|
||||
qint64 QextSerialPort::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
DWORD retVal = 0;
|
||||
|
||||
if (queryMode() == QextSerialPort::EventDriven) {
|
||||
OVERLAPPED *newOverlapWrite = new OVERLAPPED;
|
||||
ZeroMemory(newOverlapWrite, sizeof(OVERLAPPED));
|
||||
newOverlapWrite->hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
if (WriteFile(Win_Handle, (void *)data, (DWORD)maxSize, &retVal, newOverlapWrite)) {
|
||||
CloseHandle(newOverlapWrite->hEvent);
|
||||
delete newOverlapWrite;
|
||||
} else if (GetLastError() == ERROR_IO_PENDING) {
|
||||
// writing asynchronously...not an error
|
||||
QWriteLocker writelocker(bytesToWriteLock);
|
||||
_bytesToWrite += maxSize;
|
||||
pendingWrites.append(newOverlapWrite);
|
||||
} else {
|
||||
qDebug() << "serialport write error:" << GetLastError();
|
||||
lastErr = E_WRITE_FAILED;
|
||||
retVal = (DWORD)-1;
|
||||
if (!CancelIo(newOverlapWrite->hEvent)) {
|
||||
qDebug() << "serialport: couldn't cancel IO";
|
||||
}
|
||||
if (!CloseHandle(newOverlapWrite->hEvent)) {
|
||||
qDebug() << "serialport: couldn't close OVERLAPPED handle";
|
||||
}
|
||||
delete newOverlapWrite;
|
||||
}
|
||||
} else if (!WriteFile(Win_Handle, (void *)data, (DWORD)maxSize, &retVal, NULL)) {
|
||||
lastErr = E_WRITE_FAILED;
|
||||
retVal = (DWORD)-1;
|
||||
}
|
||||
return (qint64)retVal;
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is included to implement the full QIODevice interface, and currently has no
|
||||
purpose within this class. This function is meaningless on an unbuffered device and currently
|
||||
only prints a warning message to that effect.
|
||||
*/
|
||||
void QextSerialPort::ungetChar(char c)
|
||||
{
|
||||
/*meaningless on unbuffered sequential device - return error and print a warning*/
|
||||
TTY_WARNING("QextSerialPort: ungetChar() called on an unbuffered sequential device - operation is meaningless");
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the flow control used by the port. Possible values of flow are:
|
||||
\verbatim
|
||||
FLOW_OFF No flow control
|
||||
FLOW_HARDWARE Hardware (RTS/CTS) flow control
|
||||
FLOW_XONXOFF Software (XON/XOFF) flow control
|
||||
\endverbatim
|
||||
*/
|
||||
void QextSerialPort::setFlowControl(FlowType flow)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.FlowControl != flow) {
|
||||
Settings.FlowControl = flow;
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (flow) {
|
||||
/*no flow control*/
|
||||
case FLOW_OFF:
|
||||
Win_CommConfig.dcb.fOutxCtsFlow = FALSE;
|
||||
Win_CommConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
Win_CommConfig.dcb.fInX = FALSE;
|
||||
Win_CommConfig.dcb.fOutX = FALSE;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
break;
|
||||
|
||||
/*software (XON/XOFF) flow control*/
|
||||
case FLOW_XONXOFF:
|
||||
Win_CommConfig.dcb.fOutxCtsFlow = FALSE;
|
||||
Win_CommConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
Win_CommConfig.dcb.fInX = TRUE;
|
||||
Win_CommConfig.dcb.fOutX = TRUE;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
break;
|
||||
|
||||
case FLOW_HARDWARE:
|
||||
Win_CommConfig.dcb.fOutxCtsFlow = TRUE;
|
||||
Win_CommConfig.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||||
Win_CommConfig.dcb.fInX = FALSE;
|
||||
Win_CommConfig.dcb.fOutX = FALSE;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the parity associated with the serial port. The possible values of parity are:
|
||||
\verbatim
|
||||
PAR_SPACE Space Parity
|
||||
PAR_MARK Mark Parity
|
||||
PAR_NONE No Parity
|
||||
PAR_EVEN Even Parity
|
||||
PAR_ODD Odd Parity
|
||||
\endverbatim
|
||||
*/
|
||||
void QextSerialPort::setParity(ParityType parity)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.Parity != parity) {
|
||||
Settings.Parity = parity;
|
||||
}
|
||||
if (isOpen()) {
|
||||
Win_CommConfig.dcb.Parity = (unsigned char)parity;
|
||||
switch (parity) {
|
||||
/*space parity*/
|
||||
case PAR_SPACE:
|
||||
if (Settings.DataBits == DATA_8) {
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Space parity with 8 data bits is not supported by POSIX systems.");
|
||||
}
|
||||
Win_CommConfig.dcb.fParity = TRUE;
|
||||
break;
|
||||
|
||||
/*mark parity - WINDOWS ONLY*/
|
||||
case PAR_MARK:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Mark parity is not supported by POSIX systems");
|
||||
Win_CommConfig.dcb.fParity = TRUE;
|
||||
break;
|
||||
|
||||
/*no parity*/
|
||||
case PAR_NONE:
|
||||
Win_CommConfig.dcb.fParity = FALSE;
|
||||
break;
|
||||
|
||||
/*even parity*/
|
||||
case PAR_EVEN:
|
||||
Win_CommConfig.dcb.fParity = TRUE;
|
||||
break;
|
||||
|
||||
/*odd parity*/
|
||||
case PAR_ODD:
|
||||
Win_CommConfig.dcb.fParity = TRUE;
|
||||
break;
|
||||
}
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the number of data bits used by the serial port. Possible values of dataBits are:
|
||||
\verbatim
|
||||
DATA_5 5 data bits
|
||||
DATA_6 6 data bits
|
||||
DATA_7 7 data bits
|
||||
DATA_8 8 data bits
|
||||
\endverbatim
|
||||
|
||||
\note
|
||||
This function is subject to the following restrictions:
|
||||
\par
|
||||
5 data bits cannot be used with 2 stop bits.
|
||||
\par
|
||||
1.5 stop bits can only be used with 5 data bits.
|
||||
\par
|
||||
8 data bits cannot be used with space parity on POSIX systems.
|
||||
*/
|
||||
void QextSerialPort::setDataBits(DataBitsType dataBits)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.DataBits != dataBits) {
|
||||
if ((Settings.StopBits == STOP_2 && dataBits == DATA_5) ||
|
||||
(Settings.StopBits == STOP_1_5 && dataBits != DATA_5)) {} else {
|
||||
Settings.DataBits = dataBits;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (dataBits) {
|
||||
/*5 data bits*/
|
||||
case DATA_5:
|
||||
if (Settings.StopBits == STOP_2) {
|
||||
TTY_WARNING("QextSerialPort: 5 Data bits cannot be used with 2 stop bits.");
|
||||
} else {
|
||||
Win_CommConfig.dcb.ByteSize = 5;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
|
||||
/*6 data bits*/
|
||||
case DATA_6:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 6 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Win_CommConfig.dcb.ByteSize = 6;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
|
||||
/*7 data bits*/
|
||||
case DATA_7:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 7 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Win_CommConfig.dcb.ByteSize = 7;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
|
||||
/*8 data bits*/
|
||||
case DATA_8:
|
||||
if (Settings.StopBits == STOP_1_5) {
|
||||
TTY_WARNING("QextSerialPort: 8 Data bits cannot be used with 1.5 stop bits.");
|
||||
} else {
|
||||
Win_CommConfig.dcb.ByteSize = 8;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the number of stop bits used by the serial port. Possible values of stopBits are:
|
||||
\verbatim
|
||||
STOP_1 1 stop bit
|
||||
STOP_1_5 1.5 stop bits
|
||||
STOP_2 2 stop bits
|
||||
\endverbatim
|
||||
|
||||
\note
|
||||
This function is subject to the following restrictions:
|
||||
\par
|
||||
2 stop bits cannot be used with 5 data bits.
|
||||
\par
|
||||
1.5 stop bits cannot be used with 6 or more data bits.
|
||||
\par
|
||||
POSIX does not support 1.5 stop bits.
|
||||
*/
|
||||
void QextSerialPort::setStopBits(StopBitsType stopBits)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.StopBits != stopBits) {
|
||||
if ((Settings.DataBits == DATA_5 && stopBits == STOP_2) ||
|
||||
(stopBits == STOP_1_5 && Settings.DataBits != DATA_5)) {} else {
|
||||
Settings.StopBits = stopBits;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (stopBits) {
|
||||
/*one stop bit*/
|
||||
case STOP_1:
|
||||
Win_CommConfig.dcb.StopBits = ONESTOPBIT;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
break;
|
||||
|
||||
/*1.5 stop bits*/
|
||||
case STOP_1_5:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: 1.5 stop bit operation is not supported by POSIX.");
|
||||
if (Settings.DataBits != DATA_5) {
|
||||
TTY_WARNING("QextSerialPort: 1.5 stop bits can only be used with 5 data bits");
|
||||
} else {
|
||||
Win_CommConfig.dcb.StopBits = ONE5STOPBITS;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
|
||||
/*two stop bits*/
|
||||
case STOP_2:
|
||||
if (Settings.DataBits == DATA_5) {
|
||||
TTY_WARNING("QextSerialPort: 2 stop bits cannot be used with 5 data bits");
|
||||
} else {
|
||||
Win_CommConfig.dcb.StopBits = TWOSTOPBITS;
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the baud rate of the serial port. Note that not all rates are applicable on
|
||||
all platforms. The following table shows translations of the various baud rate
|
||||
constants on Windows(including NT/2000) and POSIX platforms. Speeds marked with an *
|
||||
are speeds that are usable on both Windows and POSIX.
|
||||
\verbatim
|
||||
|
||||
RATE Windows Speed POSIX Speed
|
||||
----------- ------------- -----------
|
||||
BAUD50 110 50
|
||||
BAUD75 110 75
|
||||
* BAUD110 110 110
|
||||
BAUD134 110 134.5
|
||||
BAUD150 110 150
|
||||
BAUD200 110 200
|
||||
* BAUD300 300 300
|
||||
* BAUD600 600 600
|
||||
* BAUD1200 1200 1200
|
||||
BAUD1800 1200 1800
|
||||
* BAUD2400 2400 2400
|
||||
* BAUD4800 4800 4800
|
||||
* BAUD9600 9600 9600
|
||||
BAUD14400 14400 9600
|
||||
* BAUD19200 19200 19200
|
||||
* BAUD38400 38400 38400
|
||||
BAUD56000 56000 38400
|
||||
* BAUD57600 57600 57600
|
||||
BAUD76800 57600 76800
|
||||
* BAUD115200 115200 115200
|
||||
BAUD128000 128000 115200
|
||||
BAUD256000 256000 115200
|
||||
\endverbatim
|
||||
*/
|
||||
void QextSerialPort::setBaudRate(BaudRateType baudRate)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (Settings.BaudRate != baudRate) {
|
||||
switch (baudRate) {
|
||||
case BAUD50:
|
||||
case BAUD75:
|
||||
case BAUD134:
|
||||
case BAUD150:
|
||||
case BAUD200:
|
||||
Settings.BaudRate = BAUD110;
|
||||
break;
|
||||
|
||||
case BAUD1800:
|
||||
Settings.BaudRate = BAUD1200;
|
||||
break;
|
||||
|
||||
case BAUD76800:
|
||||
Settings.BaudRate = BAUD57600;
|
||||
break;
|
||||
|
||||
default:
|
||||
Settings.BaudRate = baudRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isOpen()) {
|
||||
switch (baudRate) {
|
||||
/*50 baud*/
|
||||
case BAUD50:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 50 baud operation. Switching to 110 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*75 baud*/
|
||||
case BAUD75:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 75 baud operation. Switching to 110 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*110 baud*/
|
||||
case BAUD110:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*134.5 baud*/
|
||||
case BAUD134:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 134.5 baud operation. Switching to 110 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*150 baud*/
|
||||
case BAUD150:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 150 baud operation. Switching to 110 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*200 baud*/
|
||||
case BAUD200:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 200 baud operation. Switching to 110 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_110;
|
||||
break;
|
||||
|
||||
/*300 baud*/
|
||||
case BAUD300:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_300;
|
||||
break;
|
||||
|
||||
/*600 baud*/
|
||||
case BAUD600:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_600;
|
||||
break;
|
||||
|
||||
/*1200 baud*/
|
||||
case BAUD1200:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_1200;
|
||||
break;
|
||||
|
||||
/*1800 baud*/
|
||||
case BAUD1800:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 1800 baud operation. Switching to 1200 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_1200;
|
||||
break;
|
||||
|
||||
/*2400 baud*/
|
||||
case BAUD2400:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_2400;
|
||||
break;
|
||||
|
||||
/*4800 baud*/
|
||||
case BAUD4800:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_4800;
|
||||
break;
|
||||
|
||||
/*9600 baud*/
|
||||
case BAUD9600:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_9600;
|
||||
break;
|
||||
|
||||
/*14400 baud*/
|
||||
case BAUD14400:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 14400 baud operation.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_14400;
|
||||
break;
|
||||
|
||||
/*19200 baud*/
|
||||
case BAUD19200:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_19200;
|
||||
break;
|
||||
|
||||
/*38400 baud*/
|
||||
case BAUD38400:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_38400;
|
||||
break;
|
||||
|
||||
/*56000 baud*/
|
||||
case BAUD56000:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 56000 baud operation.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_56000;
|
||||
break;
|
||||
|
||||
/*57600 baud*/
|
||||
case BAUD57600:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_57600;
|
||||
break;
|
||||
|
||||
/*76800 baud*/
|
||||
case BAUD76800:
|
||||
TTY_WARNING("QextSerialPort: Windows does not support 76800 baud operation. Switching to 57600 baud.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_57600;
|
||||
break;
|
||||
|
||||
/*115200 baud*/
|
||||
case BAUD115200:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_115200;
|
||||
break;
|
||||
|
||||
/*128000 baud*/
|
||||
case BAUD128000:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 128000 baud operation.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_128000;
|
||||
break;
|
||||
|
||||
/*230400 baud*/
|
||||
case BAUD230400:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_230400;
|
||||
break;
|
||||
|
||||
/*256000 baud*/
|
||||
case BAUD256000:
|
||||
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 256000 baud operation.");
|
||||
Win_CommConfig.dcb.BaudRate = CBR_256000;
|
||||
break;
|
||||
|
||||
/*460800 baud*/
|
||||
case BAUD460800:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_460800;
|
||||
break;
|
||||
|
||||
/*921600 baud*/
|
||||
case BAUD921600:
|
||||
Win_CommConfig.dcb.BaudRate = CBR_921600;
|
||||
break;
|
||||
}
|
||||
SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets DTR line to the requested state (high by default). This function will have no effect if
|
||||
the port associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::setDtr(bool set)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
if (set) {
|
||||
EscapeCommFunction(Win_Handle, SETDTR);
|
||||
} else {
|
||||
EscapeCommFunction(Win_Handle, CLRDTR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets RTS line to the requested state (high by default). This function will have no effect if
|
||||
the port associated with the class is not currently open.
|
||||
*/
|
||||
void QextSerialPort::setRts(bool set)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
if (set) {
|
||||
EscapeCommFunction(Win_Handle, SETRTS);
|
||||
} else {
|
||||
EscapeCommFunction(Win_Handle, CLRRTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the line status as stored by the port function. This function will retrieve the states
|
||||
of the following lines: DCD, CTS, DSR, and RI. On POSIX systems, the following additional lines
|
||||
can be monitored: DTR, RTS, Secondary TXD, and Secondary RXD. The value returned is an unsigned
|
||||
long with specific bits indicating which lines are high. The following constants should be used
|
||||
to examine the states of individual lines:
|
||||
|
||||
\verbatim
|
||||
Mask Line
|
||||
------ ----
|
||||
LS_CTS CTS
|
||||
LS_DSR DSR
|
||||
LS_DCD DCD
|
||||
LS_RI RI
|
||||
\endverbatim
|
||||
|
||||
This function will return 0 if the port associated with the class is not currently open.
|
||||
*/
|
||||
ulong QextSerialPort::lineStatus(void)
|
||||
{
|
||||
unsigned long Status = 0, Temp = 0;
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (isOpen()) {
|
||||
GetCommModemStatus(Win_Handle, &Temp);
|
||||
if (Temp & MS_CTS_ON) {
|
||||
Status |= LS_CTS;
|
||||
}
|
||||
if (Temp & MS_DSR_ON) {
|
||||
Status |= LS_DSR;
|
||||
}
|
||||
if (Temp & MS_RING_ON) {
|
||||
Status |= LS_RI;
|
||||
}
|
||||
if (Temp & MS_RLSD_ON) {
|
||||
Status |= LS_DCD;
|
||||
}
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
||||
bool QextSerialPort::waitForReadyRead(int msecs)
|
||||
{
|
||||
// @todo implement
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 QextSerialPort::bytesToWrite() const
|
||||
{
|
||||
QReadLocker rl(bytesToWriteLock);
|
||||
|
||||
return _bytesToWrite;
|
||||
}
|
||||
|
||||
/*
|
||||
Triggered when there's activity on our HANDLE.
|
||||
*/
|
||||
void QextSerialPort::onWinEvent(HANDLE h)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
if (h == overlap.hEvent) {
|
||||
if (eventMask & EV_RXCHAR) {
|
||||
if (sender() != this && bytesAvailable() > 0) {
|
||||
emit readyRead();
|
||||
}
|
||||
}
|
||||
if (eventMask & EV_TXEMPTY) {
|
||||
/*
|
||||
A write completed. Run through the list of OVERLAPPED writes, and if
|
||||
they completed successfully, take them off the list and delete them.
|
||||
Otherwise, leave them on there so they can finish.
|
||||
*/
|
||||
qint64 totalBytesWritten = 0;
|
||||
QList<OVERLAPPED *> overlapsToDelete;
|
||||
foreach(OVERLAPPED * o, pendingWrites) {
|
||||
DWORD numBytes = 0;
|
||||
|
||||
if (GetOverlappedResult(Win_Handle, o, &numBytes, false)) {
|
||||
overlapsToDelete.append(o);
|
||||
totalBytesWritten += numBytes;
|
||||
} else if (GetLastError() != ERROR_IO_INCOMPLETE) {
|
||||
overlapsToDelete.append(o);
|
||||
qWarning() << "CommEvent overlapped write error:" << GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
if (sender() != this && totalBytesWritten > 0) {
|
||||
QWriteLocker writelocker(bytesToWriteLock);
|
||||
emit bytesWritten(totalBytesWritten);
|
||||
_bytesToWrite = 0;
|
||||
}
|
||||
|
||||
foreach(OVERLAPPED * o, overlapsToDelete) {
|
||||
OVERLAPPED *toDelete = pendingWrites.takeAt(pendingWrites.indexOf(o));
|
||||
|
||||
CloseHandle(toDelete->hEvent);
|
||||
delete toDelete;
|
||||
}
|
||||
}
|
||||
if (eventMask & EV_DSR) {
|
||||
if (lineStatus() & LS_DSR) {
|
||||
emit dsrChanged(true);
|
||||
} else {
|
||||
emit dsrChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
WaitCommEvent(Win_Handle, &eventMask, &overlap);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the read and write timeouts for the port to millisec milliseconds.
|
||||
Setting 0 indicates that timeouts are not used for read nor write operations;
|
||||
however read() and write() functions will still block. Set -1 to provide
|
||||
non-blocking behaviour (read() and write() will return immediately).
|
||||
|
||||
\note this function does nothing in event driven mode.
|
||||
*/
|
||||
void QextSerialPort::setTimeout(long millisec)
|
||||
{
|
||||
QMutexLocker lock(mutex);
|
||||
|
||||
Settings.Timeout_Millisec = millisec;
|
||||
|
||||
if (millisec == -1) {
|
||||
Win_CommTimeouts.ReadIntervalTimeout = MAXDWORD;
|
||||
Win_CommTimeouts.ReadTotalTimeoutConstant = 0;
|
||||
} else {
|
||||
Win_CommTimeouts.ReadIntervalTimeout = millisec;
|
||||
Win_CommTimeouts.ReadTotalTimeoutConstant = millisec;
|
||||
}
|
||||
Win_CommTimeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
Win_CommTimeouts.WriteTotalTimeoutMultiplier = millisec;
|
||||
Win_CommTimeouts.WriteTotalTimeoutConstant = 0;
|
||||
if (queryMode() != QextSerialPort::EventDriven) {
|
||||
SetCommTimeouts(Win_Handle, &Win_CommTimeouts);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
emulates the EV_TXEMPTY system event not present on some BT interfaces
|
||||
*/
|
||||
void QextSerialPort::triggerTxEmpty()
|
||||
{
|
||||
if (bytesToWrite() > 500) {
|
||||
QMutexLocker lock(mutex);
|
||||
qint64 totalBytesWritten = 0;
|
||||
QList<OVERLAPPED *> overlapsToDelete;
|
||||
foreach(OVERLAPPED * o, pendingWrites) {
|
||||
DWORD numBytes = 0;
|
||||
|
||||
if (GetOverlappedResult(Win_Handle, o, &numBytes, false)) {
|
||||
overlapsToDelete.append(o);
|
||||
totalBytesWritten += numBytes;
|
||||
} else if (GetLastError() != ERROR_IO_INCOMPLETE) {
|
||||
overlapsToDelete.append(o);
|
||||
qWarning() << "CommEvent overlapped write error:" << GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
if (totalBytesWritten > 0) {
|
||||
QWriteLocker writelocker(bytesToWriteLock);
|
||||
_bytesToWrite = 0;
|
||||
// qDebug()<<"zeroed bytesToWrite";
|
||||
}
|
||||
// qDebug()<<"overlapsToDelete"<<overlapsToDelete.count();
|
||||
foreach(OVERLAPPED * o, overlapsToDelete) {
|
||||
OVERLAPPED *toDelete = pendingWrites.takeAt(pendingWrites.indexOf(o));
|
||||
|
||||
CloseHandle(toDelete->hEvent);
|
||||
delete toDelete;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user