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