mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-02-26 15:54:15 +01:00
Addition of qextserialport (code.google.com/p/qextserialport/) to GCS code base. Only tested to compile on Ubuntu 9.10.
git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@309 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
parent
b54e001241
commit
8ad76c1872
2
ground/src/libs/qextserialport/qextserialport.pri
Normal file
2
ground/src/libs/qextserialport/qextserialport.pri
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
LIBS += -l$$qtLibraryTarget(QExtSerialPort)
|
||||||
|
|
5
ground/src/libs/qextserialport/qextserialport.pro
Normal file
5
ground/src/libs/qextserialport/qextserialport.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#
|
||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS = src \
|
||||||
|
|
959
ground/src/libs/qextserialport/src/posix_qextserialport.cpp
Normal file
959
ground/src/libs/qextserialport/src/posix_qextserialport.cpp
Normal file
@ -0,0 +1,959 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
199
ground/src/libs/qextserialport/src/qextserialenumerator.h
Normal file
199
ground/src/libs/qextserialport/src/qextserialenumerator.h
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*!
|
||||||
|
* \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_*/
|
288
ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp
Normal file
288
ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
if( !(matchingDictionary = IOServiceNameMatching("AppleUSBCDC")) ) {
|
||||||
|
qWarning("IOServiceNameMatching returned a NULL dictionary.");
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) {
|
||||||
|
qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult;
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
iterateServicesOSX(serialPortIterator, infoList);
|
||||||
|
IOObjectRelease(serialPortIterator);
|
||||||
|
|
||||||
|
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->physName = ioPathName;
|
||||||
|
|
||||||
|
if( bsdPathAsCFString )
|
||||||
|
{
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
if( CFStringGetCString((CFStringRef)bsdPathAsCFString, path,
|
||||||
|
PATH_MAX, kCFStringEncodingUTF8) )
|
||||||
|
portInfo->portName = 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 );
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
inf.enumName = "/dev"; // is there a more helpful name for this?
|
||||||
|
infoList.append(inf);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
qCritical("Enumeration for POSIX systems (except Linux) is not implemented yet.");
|
||||||
|
#endif
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QextSerialEnumerator::setUpNotifications( )
|
||||||
|
{
|
||||||
|
qCritical("Notifications for *Nix/FreeBSD are not implemented yet");
|
||||||
|
}
|
206
ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp
Normal file
206
ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
254
ground/src/libs/qextserialport/src/qextserialport.cpp
Normal file
254
ground/src/libs/qextserialport/src/qextserialport.cpp
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
setPortName("/dev/ttyS0");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
333
ground/src/libs/qextserialport/src/qextserialport.h
Normal file
333
ground/src/libs/qextserialport/src/qextserialport.h
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
|
||||||
|
#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
|
||||||
|
BAUD256000 //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 <windows.h>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
#include <QtCore/private/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);
|
||||||
|
#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
|
15
ground/src/libs/qextserialport/src/qextserialport_global.h
Normal file
15
ground/src/libs/qextserialport/src/qextserialport_global.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#ifndef QEXTSERIALPORT_GLOBAL_H
|
||||||
|
#define QEXTSERIALPORT_GLOBAL_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#ifdef QEXTSERIALPORT_LIB
|
||||||
|
# define QEXTSERIALPORT_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define QEXTSERIALPORT_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // QEXTSERIALPORT_GLOBAL_H
|
||||||
|
|
30
ground/src/libs/qextserialport/src/src.pro
Normal file
30
ground/src/libs/qextserialport/src/src.pro
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = QExtSerialPort
|
||||||
|
|
||||||
|
include(../../../openpilotgcslibrary.pri)
|
||||||
|
|
||||||
|
DEFINES += QEXTSERIALPORT_LIBRARY
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
868
ground/src/libs/qextserialport/src/win_qextserialport.cpp
Normal file
868
ground/src/libs/qextserialport/src/win_qextserialport.cpp
Normal file
@ -0,0 +1,868 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include "qextserialport.h"
|
||||||
|
|
||||||
|
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)));
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*256000 baud*/
|
||||||
|
case BAUD256000:
|
||||||
|
TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 256000 baud operation.");
|
||||||
|
Win_CommConfig.dcb.BaudRate=CBR_256000;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user