dev
This commit is contained in:
parent
31997aad40
commit
f03d3feaf9
BIN
Ico/icon.png
Normal file
BIN
Ico/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
959
Sources/qextserialport/posix_qextserialport.cpp
Normal file
959
Sources/qextserialport/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
Sources/qextserialport/qextserialenumerator.h
Normal file
199
Sources/qextserialport/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 <QtGui/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
Sources/qextserialport/qextserialenumerator_osx.cpp
Normal file
288
Sources/qextserialport/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 );
|
||||||
|
}
|
||||||
|
|
||||||
75
Sources/qextserialport/qextserialenumerator_unix.cpp
Normal file
75
Sources/qextserialport/qextserialenumerator_unix.cpp
Normal file
@ -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
Sources/qextserialport/qextserialenumerator_win.cpp
Normal file
206
Sources/qextserialport/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((const char *)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((const char *)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;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user