From 8ad76c1872cc96dc8fb0369bda7b49b5622b1a9f Mon Sep 17 00:00:00 2001 From: ephy Date: Sun, 14 Mar 2010 11:29:57 +0000 Subject: [PATCH] Addition of qextserialport (code.google.com/p/qextserialport/) to GCS code base. Only tested to compile on Ubuntu 9.10. git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@309 ebee16cc-31ac-478f-84a7-5cbb03baadba --- .../libs/qextserialport/qextserialport.pri | 2 + .../libs/qextserialport/qextserialport.pro | 5 + .../src/posix_qextserialport.cpp | 959 ++++++++++++++++++ .../qextserialport/src/qextserialenumerator.h | 199 ++++ .../src/qextserialenumerator_osx.cpp | 288 ++++++ .../src/qextserialenumerator_unix.cpp | 75 ++ .../src/qextserialenumerator_win.cpp | 206 ++++ .../qextserialport/src/qextserialport.cpp | 254 +++++ .../libs/qextserialport/src/qextserialport.h | 333 ++++++ .../src/qextserialport_global.h | 15 + ground/src/libs/qextserialport/src/src.pro | 30 + .../qextserialport/src/win_qextserialport.cpp | 868 ++++++++++++++++ 12 files changed, 3234 insertions(+) create mode 100644 ground/src/libs/qextserialport/qextserialport.pri create mode 100644 ground/src/libs/qextserialport/qextserialport.pro create mode 100644 ground/src/libs/qextserialport/src/posix_qextserialport.cpp create mode 100644 ground/src/libs/qextserialport/src/qextserialenumerator.h create mode 100644 ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp create mode 100644 ground/src/libs/qextserialport/src/qextserialenumerator_unix.cpp create mode 100644 ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp create mode 100644 ground/src/libs/qextserialport/src/qextserialport.cpp create mode 100644 ground/src/libs/qextserialport/src/qextserialport.h create mode 100644 ground/src/libs/qextserialport/src/qextserialport_global.h create mode 100644 ground/src/libs/qextserialport/src/src.pro create mode 100644 ground/src/libs/qextserialport/src/win_qextserialport.cpp diff --git a/ground/src/libs/qextserialport/qextserialport.pri b/ground/src/libs/qextserialport/qextserialport.pri new file mode 100644 index 000000000..1f89982c5 --- /dev/null +++ b/ground/src/libs/qextserialport/qextserialport.pri @@ -0,0 +1,2 @@ +LIBS += -l$$qtLibraryTarget(QExtSerialPort) + diff --git a/ground/src/libs/qextserialport/qextserialport.pro b/ground/src/libs/qextserialport/qextserialport.pro new file mode 100644 index 000000000..277ca41bd --- /dev/null +++ b/ground/src/libs/qextserialport/qextserialport.pro @@ -0,0 +1,5 @@ +# +TEMPLATE = subdirs + +SUBDIRS = src \ + diff --git a/ground/src/libs/qextserialport/src/posix_qextserialport.cpp b/ground/src/libs/qextserialport/src/posix_qextserialport.cpp new file mode 100644 index 000000000..c7f923cd5 --- /dev/null +++ b/ground/src/libs/qextserialport/src/posix_qextserialport.cpp @@ -0,0 +1,959 @@ + + +#include +#include +#include "qextserialport.h" +#include +#include + +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; +} diff --git a/ground/src/libs/qextserialport/src/qextserialenumerator.h b/ground/src/libs/qextserialport/src/qextserialenumerator.h new file mode 100644 index 000000000..de173715c --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialenumerator.h @@ -0,0 +1,199 @@ +/*! + * \file qextserialenumerator.h + * \author Michal Policht + * \see QextSerialEnumerator + */ + +#ifndef _QEXTSERIALENUMERATOR_H_ +#define _QEXTSERIALENUMERATOR_H_ + + +#include +#include +#include +#include "qextserialport_global.h" + +#ifdef Q_OS_WIN + #include + #include + #include +#endif /*Q_OS_WIN*/ + +#ifdef Q_OS_MAC + #include +#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 +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 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 + The Code Project (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 & 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* 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 & infoList); + static void iterateServicesOSX(io_object_t service, QList & 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 & 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 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_*/ diff --git a/ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp b/ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp new file mode 100644 index 000000000..229d73f37 --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialenumerator_osx.cpp @@ -0,0 +1,288 @@ + + + +#include "qextserialenumerator.h" +#include +#include + +#include +#include +#include + +QextSerialEnumerator::QextSerialEnumerator( ) +{ + if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) + qRegisterMetaType("QextPortInfo"); +} + +QextSerialEnumerator::~QextSerialEnumerator( ) +{ + IONotificationPortDestroy( notificationPortRef ); +} + +// static +QList QextSerialEnumerator::getPorts() +{ + QList 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 & 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 ); +} + diff --git a/ground/src/libs/qextserialport/src/qextserialenumerator_unix.cpp b/ground/src/libs/qextserialport/src/qextserialenumerator_unix.cpp new file mode 100644 index 000000000..f43a75b68 --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialenumerator_unix.cpp @@ -0,0 +1,75 @@ + + + +#include "qextserialenumerator.h" +#include +#include +#include +#include + +QextSerialEnumerator::QextSerialEnumerator( ) +{ + if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) + qRegisterMetaType("QextPortInfo"); +} + +QextSerialEnumerator::~QextSerialEnumerator( ) +{ +} + +QList QextSerialEnumerator::getPorts() +{ + QList 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"); +} diff --git a/ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp b/ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp new file mode 100644 index 000000000..6026b5458 --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialenumerator_win.cpp @@ -0,0 +1,206 @@ + + + +#include "qextserialenumerator.h" +#include +#include + +#include +#include +#include "qextserialport.h" +#include + +QextSerialEnumerator::QextSerialEnumerator( ) +{ + if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) + qRegisterMetaType("QextPortInfo"); +#if (defined QT_GUI_LIB) + notificationWidget = 0; +#endif // Q_OS_WIN +} + +QextSerialEnumerator::~QextSerialEnumerator( ) +{ +#if (defined QT_GUI_LIB) + if( notificationWidget ) + delete notificationWidget; +#endif +} + + + +// see http://msdn.microsoft.com/en-us/library/ms791134.aspx for list of GUID classes +#ifndef GUID_DEVCLASS_PORTS + DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 ); +#endif + +/* Gordon Schumacher's macros for TCHAR -> QString conversions and vice versa */ +#ifdef UNICODE + #define QStringToTCHAR(x) (wchar_t*) x.utf16() + #define PQStringToTCHAR(x) (wchar_t*) x->utf16() + #define TCHARToQString(x) QString::fromUtf16((ushort*)(x)) + #define TCHARToQStringN(x,y) QString::fromUtf16((ushort*)(x),(y)) +#else + #define QStringToTCHAR(x) x.local8Bit().constData() + #define PQStringToTCHAR(x) x->local8Bit().constData() + #define TCHARToQString(x) QString::fromLocal8Bit((x)) + #define TCHARToQStringN(x,y) QString::fromLocal8Bit((x),(y)) +#endif /*UNICODE*/ + + +//static +QString QextSerialEnumerator::getRegKeyValue(HKEY key, LPCTSTR property) +{ + DWORD size = 0; + DWORD type; + RegQueryValueEx(key, property, NULL, NULL, NULL, & size); + BYTE* buff = new BYTE[size]; + QString result; + if( RegQueryValueEx(key, property, NULL, &type, buff, & size) == ERROR_SUCCESS ) + result = TCHARToQString(buff); + RegCloseKey(key); + delete [] buff; + return result; +} + +//static +QString QextSerialEnumerator::getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property) +{ + DWORD buffSize = 0; + SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, NULL, 0, & buffSize); + BYTE* buff = new BYTE[buffSize]; + SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, buff, buffSize, NULL); + QString result = TCHARToQString(buff); + delete [] buff; + return result; +} + +QList QextSerialEnumerator::getPorts() +{ + QList ports; + enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports); + return ports; +} + +void QextSerialEnumerator::enumerateDevicesWin( const GUID & guid, QList* 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; +} + diff --git a/ground/src/libs/qextserialport/src/qextserialport.cpp b/ground/src/libs/qextserialport/src/qextserialport.cpp new file mode 100644 index 000000000..ccf359839 --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialport.cpp @@ -0,0 +1,254 @@ + + +#include +#include "qextserialport.h" + +/*! +Default constructor. Note that the name of the device used by a QextSerialPort constructed with +this constructor will be determined by #defined constants, or lack thereof - the default behavior +is the same as _TTY_LINUX_. Possible naming conventions and their associated constants are: + +\verbatim + +Constant Used By Naming Convention +---------- ------------- ------------------------ +Q_OS_WIN Windows COM1, COM2 +_TTY_IRIX_ SGI/IRIX /dev/ttyf1, /dev/ttyf2 +_TTY_HPUX_ HP-UX /dev/tty1p0, /dev/tty2p0 +_TTY_SUN_ SunOS/Solaris /dev/ttya, /dev/ttyb +_TTY_DIGITAL_ Digital UNIX /dev/tty01, /dev/tty02 +_TTY_FREEBSD_ FreeBSD /dev/ttyd0, /dev/ttyd1 +_TTY_OPENBSD_ OpenBSD /dev/tty00, /dev/tty01 +_TTY_LINUX_ Linux /dev/ttyS0, /dev/ttyS1 + Linux /dev/ttyS0, /dev/ttyS1 +\endverbatim + +This constructor assigns the device name to the name of the first port on the specified system. +See the other constructors if you need to open a different port. +*/ +QextSerialPort::QextSerialPort(QextSerialPort::QueryMode mode) + : QIODevice() +{ + +#ifdef Q_OS_WIN + setPortName("COM1"); + +#elif defined(_TTY_IRIX_) + setPortName("/dev/ttyf1"); + +#elif defined(_TTY_HPUX_) + setPortName("/dev/tty1p0"); + +#elif defined(_TTY_SUN_) + setPortName("/dev/ttya"); + +#elif defined(_TTY_DIGITAL_) + setPortName("/dev/tty01"); + +#elif defined(_TTY_FREEBSD_) + setPortName("/dev/ttyd1"); + +#elif defined(_TTY_OPENBSD_) + setPortName("/dev/tty00"); + +#else + setPortName("/dev/ttyS0"); +#endif + + construct(); + setQueryMode(mode); + platformSpecificInit(); +} + +/*! +Constructs a serial port attached to the port specified by name. +name is the name of the device, which is windowsystem-specific, +e.g."COM1" or "/dev/ttyS0". +*/ +QextSerialPort::QextSerialPort(const QString & name, QextSerialPort::QueryMode mode) + : QIODevice() +{ + construct(); + setQueryMode(mode); + setPortName(name); + platformSpecificInit(); +} + +/*! +Constructs a port with default name and specified settings. +*/ +QextSerialPort::QextSerialPort(const PortSettings& settings, QextSerialPort::QueryMode mode) + : QIODevice() +{ + construct(); + setBaudRate(settings.BaudRate); + setDataBits(settings.DataBits); + setParity(settings.Parity); + setStopBits(settings.StopBits); + setFlowControl(settings.FlowControl); + setTimeout(settings.Timeout_Millisec); + setQueryMode(mode); + platformSpecificInit(); +} + +/*! +Constructs a port with specified name and settings. +*/ +QextSerialPort::QextSerialPort(const QString & name, const PortSettings& settings, QextSerialPort::QueryMode mode) + : QIODevice() +{ + construct(); + setPortName(name); + setBaudRate(settings.BaudRate); + setDataBits(settings.DataBits); + setParity(settings.Parity); + setStopBits(settings.StopBits); + setFlowControl(settings.FlowControl); + setTimeout(settings.Timeout_Millisec); + setQueryMode(mode); + platformSpecificInit(); +} + +/*! +Common constructor function for setting up default port settings. +(115200 Baud, 8N1, Hardware flow control where supported, otherwise no flow control, and 0 ms timeout). +*/ +void QextSerialPort::construct() +{ + lastErr = E_NO_ERROR; + Settings.BaudRate=BAUD115200; + Settings.DataBits=DATA_8; + Settings.Parity=PAR_NONE; + Settings.StopBits=STOP_1; + Settings.FlowControl=FLOW_HARDWARE; + Settings.Timeout_Millisec=500; + mutex = new QMutex( QMutex::Recursive ); + setOpenMode(QIODevice::NotOpen); +} + +void QextSerialPort::setQueryMode(QueryMode mechanism) +{ + _queryMode = mechanism; +} + +/*! +Sets the name of the device associated with the object, e.g. "COM1", or "/dev/ttyS0". +*/ +void QextSerialPort::setPortName(const QString & name) +{ + #ifdef Q_OS_WIN + port = fullPortNameWin( name ); + #else + port = name; + #endif +} + +/*! +Returns the name set by setPortName(). +*/ +QString QextSerialPort::portName() const +{ + return port; +} + +/*! + Reads all available data from the device, and returns it as a QByteArray. + This function has no way of reporting errors; returning an empty QByteArray() + can mean either that no data was currently available for reading, or that an error occurred. +*/ +QByteArray QextSerialPort::readAll() +{ + int avail = this->bytesAvailable(); + return (avail > 0) ? this->read(avail) : QByteArray(); +} + +/*! +Returns the baud rate of the serial port. For a list of possible return values see +the definition of the enum BaudRateType. +*/ +BaudRateType QextSerialPort::baudRate(void) const +{ + return Settings.BaudRate; +} + +/*! +Returns the number of data bits used by the port. For a list of possible values returned by +this function, see the definition of the enum DataBitsType. +*/ +DataBitsType QextSerialPort::dataBits() const +{ + return Settings.DataBits; +} + +/*! +Returns the type of parity used by the port. For a list of possible values returned by +this function, see the definition of the enum ParityType. +*/ +ParityType QextSerialPort::parity() const +{ + return Settings.Parity; +} + +/*! +Returns the number of stop bits used by the port. For a list of possible return values, see +the definition of the enum StopBitsType. +*/ +StopBitsType QextSerialPort::stopBits() const +{ + return Settings.StopBits; +} + +/*! +Returns the type of flow control used by the port. For a list of possible values returned +by this function, see the definition of the enum FlowType. +*/ +FlowType QextSerialPort::flowControl() const +{ + return Settings.FlowControl; +} + +/*! +Returns true if device is sequential, otherwise returns false. Serial port is sequential device +so this function always returns true. Check QIODevice::isSequential() documentation for more +information. +*/ +bool QextSerialPort::isSequential() const +{ + return true; +} + +QString QextSerialPort::errorString() +{ + switch(lastErr) + { + case E_NO_ERROR: return "No Error has occurred"; + case E_INVALID_FD: return "Invalid file descriptor (port was not opened correctly)"; + case E_NO_MEMORY: return "Unable to allocate memory tables (POSIX)"; + case E_CAUGHT_NON_BLOCKED_SIGNAL: return "Caught a non-blocked signal (POSIX)"; + case E_PORT_TIMEOUT: return "Operation timed out (POSIX)"; + case E_INVALID_DEVICE: return "The file opened by the port is not a valid device"; + case E_BREAK_CONDITION: return "The port detected a break condition"; + case E_FRAMING_ERROR: return "The port detected a framing error (usually caused by incorrect baud rate settings)"; + case E_IO_ERROR: return "There was an I/O error while communicating with the port"; + case E_BUFFER_OVERRUN: return "Character buffer overrun"; + case E_RECEIVE_OVERFLOW: return "Receive buffer overflow"; + case E_RECEIVE_PARITY_ERROR: return "The port detected a parity error in the received data"; + case E_TRANSMIT_OVERFLOW: return "Transmit buffer overflow"; + case E_READ_FAILED: return "General read operation failure"; + case E_WRITE_FAILED: return "General write operation failure"; + case E_FILE_NOT_FOUND: return "The "+this->portName()+" file doesn't exists"; + default: return QString("Unknown error: %1").arg(lastErr); + } +} + +/*! +Standard destructor. +*/ +QextSerialPort::~QextSerialPort() +{ + if (isOpen()) { + close(); + } + platformSpecificDestruct(); + delete mutex; +} diff --git a/ground/src/libs/qextserialport/src/qextserialport.h b/ground/src/libs/qextserialport/src/qextserialport.h new file mode 100644 index 000000000..a6a2096de --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialport.h @@ -0,0 +1,333 @@ + +#ifndef _QEXTSERIALPORT_H_ +#define _QEXTSERIALPORT_H_ + +#include "qextserialport_global.h" + +/*if all warning messages are turned off, flag portability warnings to be turned off as well*/ +#ifdef _TTY_NOWARN_ +#define _TTY_NOWARN_PORT_ +#endif + +/*macros for warning and debug messages*/ +#ifdef _TTY_NOWARN_PORT_ +#define TTY_PORTABILITY_WARNING(s) +#else +#define TTY_PORTABILITY_WARNING(s) qWarning(s) +#endif /*_TTY_NOWARN_PORT_*/ +#ifdef _TTY_NOWARN_ +#define TTY_WARNING(s) +#else +#define TTY_WARNING(s) qWarning(s) +#endif /*_TTY_NOWARN_*/ + + +/*line status constants*/ +#define LS_CTS 0x01 +#define LS_DSR 0x02 +#define LS_DCD 0x04 +#define LS_RI 0x08 +#define LS_RTS 0x10 +#define LS_DTR 0x20 +#define LS_ST 0x40 +#define LS_SR 0x80 + +/*error constants*/ +#define E_NO_ERROR 0 +#define E_INVALID_FD 1 +#define E_NO_MEMORY 2 +#define E_CAUGHT_NON_BLOCKED_SIGNAL 3 +#define E_PORT_TIMEOUT 4 +#define E_INVALID_DEVICE 5 +#define E_BREAK_CONDITION 6 +#define E_FRAMING_ERROR 7 +#define E_IO_ERROR 8 +#define E_BUFFER_OVERRUN 9 +#define E_RECEIVE_OVERFLOW 10 +#define E_RECEIVE_PARITY_ERROR 11 +#define E_TRANSMIT_OVERFLOW 12 +#define E_READ_FAILED 13 +#define E_WRITE_FAILED 14 +#define E_FILE_NOT_FOUND 15 + +enum BaudRateType +{ + BAUD50, //POSIX ONLY + BAUD75, //POSIX ONLY + BAUD110, + BAUD134, //POSIX ONLY + BAUD150, //POSIX ONLY + BAUD200, //POSIX ONLY + BAUD300, + BAUD600, + BAUD1200, + BAUD1800, //POSIX ONLY + BAUD2400, + BAUD4800, + BAUD9600, + BAUD14400, //WINDOWS ONLY + BAUD19200, + BAUD38400, + BAUD56000, //WINDOWS ONLY + BAUD57600, + BAUD76800, //POSIX ONLY + BAUD115200, + BAUD128000, //WINDOWS ONLY + BAUD256000 //WINDOWS ONLY +}; + +enum DataBitsType +{ + DATA_5, + DATA_6, + DATA_7, + DATA_8 +}; + +enum ParityType +{ + PAR_NONE, + PAR_ODD, + PAR_EVEN, + PAR_MARK, //WINDOWS ONLY + PAR_SPACE +}; + +enum StopBitsType +{ + STOP_1, + STOP_1_5, //WINDOWS ONLY + STOP_2 +}; + +enum FlowType +{ + FLOW_OFF, + FLOW_HARDWARE, + FLOW_XONXOFF +}; + +/** + * structure to contain port settings + */ +struct PortSettings +{ + BaudRateType BaudRate; + DataBitsType DataBits; + ParityType Parity; + StopBitsType StopBits; + FlowType FlowControl; + long Timeout_Millisec; +}; + +#include +#include +#ifdef Q_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#elif (defined Q_OS_WIN) +#include +#include +#include +#include +#endif + +/*! +Encapsulates a serial port on both POSIX and Windows systems. + +\note +Be sure to check the full list of members, as QIODevice provides quite a lot of +functionality for QextSerialPort. + +\section Usage +QextSerialPort offers both a polling and event driven API. Event driven is typically easier +to use, since you never have to worry about checking for new data. + +\b Example +\code +QextSerialPort* port = new QextSerialPort("COM1", QextSerialPort::EventDriven); +connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable())); +port->open(); + +void MyClass::onDataAvailable() { + int avail = port->bytesAvailable(); + if( avail > 0 ) { + QByteArray usbdata; + usbdata.resize(avail); + int read = port->read(usbdata.data(), usbdata.size()); + if( read > 0 ) { + processNewData(usbdata); + } + } +} +\endcode + +\section Compatibility +The user will be notified of errors and possible portability conflicts at run-time +by default - this behavior can be turned off by defining _TTY_NOWARN_ +(to turn off all warnings) or _TTY_NOWARN_PORT_ (to turn off portability warnings) in the project. + +On Windows NT/2000/XP this class uses Win32 serial port functions by default. The user may +select POSIX behavior under NT, 2000, or XP ONLY by defining Q_OS_UNIX in the project. +No guarantees are made as to the quality of POSIX support under NT/2000 however. + +\author Stefan Sander, Michal Policht, Brandon Fosdick, Liam Staskawicz +*/ +class QEXTSERIALPORT_EXPORT QextSerialPort: public QIODevice +{ + Q_OBJECT + public: + enum QueryMode { + Polling, + EventDriven + }; + + QextSerialPort(QueryMode mode = EventDriven); + QextSerialPort(const QString & name, QueryMode mode = EventDriven); + QextSerialPort(PortSettings const& s, QueryMode mode = EventDriven); + QextSerialPort(const QString & name, PortSettings const& s, QueryMode mode = EventDriven); + ~QextSerialPort(); + + void setPortName(const QString & name); + QString portName() const; + + /**! + * Get query mode. + * \return query mode. + */ + inline QueryMode queryMode() const { return _queryMode; } + + /*! + * Set desired serial communication handling style. You may choose from polling + * or event driven approach. This function does nothing when port is open; to + * apply changes port must be reopened. + * + * In event driven approach read() and write() functions are acting + * asynchronously. They return immediately and the operation is performed in + * the background, so they doesn't freeze the calling thread. + * To determine when operation is finished, QextSerialPort runs separate thread + * and monitors serial port events. Whenever the event occurs, adequate signal + * is emitted. + * + * When polling is set, read() and write() are acting synchronously. Signals are + * not working in this mode and some functions may not be available. The advantage + * of polling is that it generates less overhead due to lack of signals emissions + * and it doesn't start separate thread to monitor events. + * + * Generally event driven approach is more capable and friendly, although some + * applications may need as low overhead as possible and then polling comes. + * + * \param mode query mode. + */ + void setQueryMode(QueryMode mode); + + void setBaudRate(BaudRateType); + BaudRateType baudRate() const; + + void setDataBits(DataBitsType); + DataBitsType dataBits() const; + + void setParity(ParityType); + ParityType parity() const; + + void setStopBits(StopBitsType); + StopBitsType stopBits() const; + + void setFlowControl(FlowType); + FlowType flowControl() const; + + void setTimeout(long); + + bool open(OpenMode mode); + bool isSequential() const; + void close(); + void flush(); + + qint64 size() const; + qint64 bytesAvailable() const; + QByteArray readAll(); + + void ungetChar(char c); + + ulong lastError() const; + void translateError(ulong error); + + void setDtr(bool set=true); + void setRts(bool set=true); + ulong lineStatus(); + QString errorString(); + +#ifdef Q_OS_WIN + virtual bool waitForReadyRead(int msecs); ///< @todo implement. + virtual qint64 bytesToWrite() const; + static QString fullPortNameWin(const QString & name); +#endif + + protected: + QMutex* mutex; + QString port; + PortSettings Settings; + ulong lastErr; + QueryMode _queryMode; + + // platform specific members +#ifdef Q_OS_UNIX + int fd; + QSocketNotifier *readNotifier; + struct termios Posix_CommConfig; + struct termios old_termios; + struct timeval Posix_Timeout; + struct timeval Posix_Copy_Timeout; +#elif (defined Q_OS_WIN) + HANDLE Win_Handle; + OVERLAPPED overlap; + COMMCONFIG Win_CommConfig; + COMMTIMEOUTS Win_CommTimeouts; + QWinEventNotifier *winEventNotifier; + DWORD eventMask; + QList pendingWrites; + QReadWriteLock* bytesToWriteLock; + qint64 _bytesToWrite; +#endif + + void construct(); // common construction + void platformSpecificDestruct(); + void platformSpecificInit(); + qint64 readData(char * data, qint64 maxSize); + qint64 writeData(const char * data, qint64 maxSize); + +#ifdef Q_OS_WIN + private slots: + void onWinEvent(HANDLE h); +#endif + + private: + Q_DISABLE_COPY(QextSerialPort) + + signals: +// /** +// * This signal is emitted whenever port settings are updated. +// * \param valid \p true if settings are valid, \p false otherwise. +// * +// * @todo implement. +// */ +// // void validSettings(bool valid); + + /*! + * This signal is emitted whenever dsr line has changed its state. You may + * use this signal to check if device is connected. + * \param status \p true when DSR signal is on, \p false otherwise. + * + * \see lineStatus(). + */ + void dsrChanged(bool status); + +}; + +#endif diff --git a/ground/src/libs/qextserialport/src/qextserialport_global.h b/ground/src/libs/qextserialport/src/qextserialport_global.h new file mode 100644 index 000000000..31d36c3c1 --- /dev/null +++ b/ground/src/libs/qextserialport/src/qextserialport_global.h @@ -0,0 +1,15 @@ + + +#ifndef QEXTSERIALPORT_GLOBAL_H +#define QEXTSERIALPORT_GLOBAL_H + +#include + +#ifdef QEXTSERIALPORT_LIB +# define QEXTSERIALPORT_EXPORT Q_DECL_EXPORT +#else +# define QEXTSERIALPORT_EXPORT Q_DECL_IMPORT +#endif + +#endif // QEXTSERIALPORT_GLOBAL_H + diff --git a/ground/src/libs/qextserialport/src/src.pro b/ground/src/libs/qextserialport/src/src.pro new file mode 100644 index 000000000..dd83c53b5 --- /dev/null +++ b/ground/src/libs/qextserialport/src/src.pro @@ -0,0 +1,30 @@ +TEMPLATE = lib +TARGET = QExtSerialPort + +include(../../../openpilotgcslibrary.pri) + +DEFINES += QEXTSERIALPORT_LIBRARY + +VERSION = 1.2.0 + +# event driven device enumeration on windows requires the gui module +!win32:QT -= gui + +HEADERS = qextserialport.h \ + qextserialenumerator.h \ + qextserialport_global.h +SOURCES = qextserialport.cpp + +unix:SOURCES += posix_qextserialport.cpp +unix:!macx:SOURCES += qextserialenumerator_unix.cpp +macx { + SOURCES += qextserialenumerator_osx.cpp + LIBS += -framework IOKit -framework CoreFoundation +} + +win32 { + SOURCES += win_qextserialport.cpp qextserialenumerator_win.cpp + DEFINES += WINVER=0x0501 # needed for mingw to pull in appropriate dbt business...probably a better way to do this + LIBS += -lsetupapi +} + diff --git a/ground/src/libs/qextserialport/src/win_qextserialport.cpp b/ground/src/libs/qextserialport/src/win_qextserialport.cpp new file mode 100644 index 000000000..af1cdb607 --- /dev/null +++ b/ground/src/libs/qextserialport/src/win_qextserialport.cpp @@ -0,0 +1,868 @@ + + +#include +#include +#include +#include "qextserialport.h" + +void QextSerialPort::platformSpecificInit() +{ + Win_Handle=INVALID_HANDLE_VALUE; + ZeroMemory(&overlap, sizeof(OVERLAPPED)); + overlap.hEvent = CreateEvent(NULL, true, false, NULL); + winEventNotifier = 0; + bytesToWriteLock = new QReadWriteLock; + _bytesToWrite = 0; +} + +/*! +Standard destructor. +*/ +void QextSerialPort::platformSpecificDestruct() { + CloseHandle(overlap.hEvent); + delete bytesToWriteLock; +} + +QString QextSerialPort::fullPortNameWin(const QString & name) +{ + QRegExp rx("^COM(\\d+)"); + QString fullName(name); + if(fullName.contains(rx)) { + int portnum = rx.cap(1).toInt(); + if(portnum > 9) // COM ports greater than 9 need \\.\ prepended + fullName.prepend("\\\\.\\"); + } + return fullName; +} + +/*! +Opens a serial port. Note that this function does not specify which device to open. If you need +to open a device by name, see QextSerialPort::open(const char*). This function has no effect +if the port associated with the class is already open. The port is also configured to the current +settings, as stored in the Settings structure. +*/ +bool QextSerialPort::open(OpenMode mode) { + unsigned long confSize = sizeof(COMMCONFIG); + Win_CommConfig.dwSize = confSize; + DWORD dwFlagsAndAttributes = 0; + if (queryMode() == QextSerialPort::EventDriven) + dwFlagsAndAttributes += FILE_FLAG_OVERLAPPED; + + QMutexLocker lock(mutex); + if (mode == QIODevice::NotOpen) + return isOpen(); + if (!isOpen()) { + /*open the port*/ + Win_Handle=CreateFileA(port.toAscii(), GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); + if (Win_Handle!=INVALID_HANDLE_VALUE) { + QIODevice::open(mode); + /*configure port settings*/ + GetCommConfig(Win_Handle, &Win_CommConfig, &confSize); + GetCommState(Win_Handle, &(Win_CommConfig.dcb)); + + /*set up parameters*/ + Win_CommConfig.dcb.fBinary=TRUE; + Win_CommConfig.dcb.fInX=FALSE; + Win_CommConfig.dcb.fOutX=FALSE; + Win_CommConfig.dcb.fAbortOnError=FALSE; + Win_CommConfig.dcb.fNull=FALSE; + setBaudRate(Settings.BaudRate); + setDataBits(Settings.DataBits); + setStopBits(Settings.StopBits); + setParity(Settings.Parity); + setFlowControl(Settings.FlowControl); + setTimeout(Settings.Timeout_Millisec); + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + + //init event driven approach + if (queryMode() == QextSerialPort::EventDriven) { + Win_CommTimeouts.ReadIntervalTimeout = MAXDWORD; + Win_CommTimeouts.ReadTotalTimeoutMultiplier = 0; + Win_CommTimeouts.ReadTotalTimeoutConstant = 0; + Win_CommTimeouts.WriteTotalTimeoutMultiplier = 0; + Win_CommTimeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(Win_Handle, &Win_CommTimeouts); + if (!SetCommMask( Win_Handle, EV_TXEMPTY | EV_RXCHAR | EV_DSR)) { + qWarning() << "failed to set Comm Mask. Error code:", GetLastError(); + return false; + } + winEventNotifier = new QWinEventNotifier(overlap.hEvent, this); + connect(winEventNotifier, SIGNAL(activated(HANDLE)), this, SLOT(onWinEvent(HANDLE))); + WaitCommEvent(Win_Handle, &eventMask, &overlap); + } + } + } else { + return false; + } + return isOpen(); +} + +/*! +Closes a serial port. This function has no effect if the serial port associated with the class +is not currently open. +*/ +void QextSerialPort::close() +{ + QMutexLocker lock(mutex); + if (isOpen()) { + flush(); + QIODevice::close(); // mark ourselves as closed + CancelIo(Win_Handle); + if (CloseHandle(Win_Handle)) + Win_Handle = INVALID_HANDLE_VALUE; + if (winEventNotifier) + winEventNotifier->deleteLater(); + + _bytesToWrite = 0; + + foreach(OVERLAPPED* o, pendingWrites) { + CloseHandle(o->hEvent); + delete o; + } + pendingWrites.clear(); + } +} + +/*! +Flushes all pending I/O to the serial port. This function has no effect if the serial port +associated with the class is not currently open. +*/ +void QextSerialPort::flush() { + QMutexLocker lock(mutex); + if (isOpen()) { + FlushFileBuffers(Win_Handle); + } +} + +/*! +This function will return the number of bytes waiting in the receive queue of the serial port. +It is included primarily to provide a complete QIODevice interface, and will not record errors +in the lastErr member (because it is const). This function is also not thread-safe - in +multithreading situations, use QextSerialPort::bytesAvailable() instead. +*/ +qint64 QextSerialPort::size() const { + int availBytes; + COMSTAT Win_ComStat; + DWORD Win_ErrorMask=0; + ClearCommError(Win_Handle, &Win_ErrorMask, &Win_ComStat); + availBytes = Win_ComStat.cbInQue; + return (qint64)availBytes; +} + +/*! +Returns the number of bytes waiting in the port's receive queue. This function will return 0 if +the port is not currently open, or -1 on error. +*/ +qint64 QextSerialPort::bytesAvailable() const { + QMutexLocker lock(mutex); + if (isOpen()) { + DWORD Errors; + COMSTAT Status; + if (ClearCommError(Win_Handle, &Errors, &Status)) { + return Status.cbInQue + QIODevice::bytesAvailable(); + } + return (qint64)-1; + } + return 0; +} + +/*! +Translates a system-specific error code to a QextSerialPort error code. Used internally. +*/ +void QextSerialPort::translateError(ulong error) { + if (error&CE_BREAK) { + lastErr=E_BREAK_CONDITION; + } + else if (error&CE_FRAME) { + lastErr=E_FRAMING_ERROR; + } + else if (error&CE_IOE) { + lastErr=E_IO_ERROR; + } + else if (error&CE_MODE) { + lastErr=E_INVALID_FD; + } + else if (error&CE_OVERRUN) { + lastErr=E_BUFFER_OVERRUN; + } + else if (error&CE_RXPARITY) { + lastErr=E_RECEIVE_PARITY_ERROR; + } + else if (error&CE_RXOVER) { + lastErr=E_RECEIVE_OVERFLOW; + } + else if (error&CE_TXFULL) { + lastErr=E_TRANSMIT_OVERFLOW; + } +} + +/*! +Reads a block of data from the serial port. This function will read at most maxlen bytes from +the serial port and place them in the buffer pointed to by data. Return value is the number of +bytes actually read, or -1 on error. + +\warning before calling this function ensure that serial port associated with this class +is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPort::readData(char *data, qint64 maxSize) +{ + DWORD retVal; + QMutexLocker lock(mutex); + retVal = 0; + if (queryMode() == QextSerialPort::EventDriven) { + OVERLAPPED overlapRead; + ZeroMemory(&overlapRead, sizeof(OVERLAPPED)); + if (!ReadFile(Win_Handle, (void*)data, (DWORD)maxSize, & retVal, & overlapRead)) { + if (GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(Win_Handle, & overlapRead, & retVal, true); + else { + lastErr = E_READ_FAILED; + retVal = (DWORD)-1; + } + } + } else if (!ReadFile(Win_Handle, (void*)data, (DWORD)maxSize, & retVal, NULL)) { + lastErr = E_READ_FAILED; + retVal = (DWORD)-1; + } + return (qint64)retVal; +} + +/*! +Writes a block of data to the serial port. This function will write len bytes +from the buffer pointed to by data to the serial port. Return value is the number +of bytes actually written, or -1 on error. + +\warning before calling this function ensure that serial port associated with this class +is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPort::writeData(const char *data, qint64 maxSize) +{ + QMutexLocker lock( mutex ); + DWORD retVal = 0; + if (queryMode() == QextSerialPort::EventDriven) { + OVERLAPPED* newOverlapWrite = new OVERLAPPED; + ZeroMemory(newOverlapWrite, sizeof(OVERLAPPED)); + newOverlapWrite->hEvent = CreateEvent(NULL, true, false, NULL); + if (WriteFile(Win_Handle, (void*)data, (DWORD)maxSize, & retVal, newOverlapWrite)) { + CloseHandle(newOverlapWrite->hEvent); + delete newOverlapWrite; + } + else if (GetLastError() == ERROR_IO_PENDING) { + // writing asynchronously...not an error + QWriteLocker writelocker(bytesToWriteLock); + _bytesToWrite += maxSize; + pendingWrites.append(newOverlapWrite); + } + else { + qDebug() << "serialport write error:" << GetLastError(); + lastErr = E_WRITE_FAILED; + retVal = (DWORD)-1; + if(!CancelIo(newOverlapWrite->hEvent)) + qDebug() << "serialport: couldn't cancel IO"; + if(!CloseHandle(newOverlapWrite->hEvent)) + qDebug() << "serialport: couldn't close OVERLAPPED handle"; + delete newOverlapWrite; + } + } else if (!WriteFile(Win_Handle, (void*)data, (DWORD)maxSize, & retVal, NULL)) { + lastErr = E_WRITE_FAILED; + retVal = (DWORD)-1; + } + return (qint64)retVal; +} + +/*! +This function is included to implement the full QIODevice interface, and currently has no +purpose within this class. This function is meaningless on an unbuffered device and currently +only prints a warning message to that effect. +*/ +void QextSerialPort::ungetChar(char c) { + + /*meaningless on unbuffered sequential device - return error and print a warning*/ + TTY_WARNING("QextSerialPort: ungetChar() called on an unbuffered sequential device - operation is meaningless"); +} + +/*! +Sets the flow control used by the port. Possible values of flow are: +\verbatim + FLOW_OFF No flow control + FLOW_HARDWARE Hardware (RTS/CTS) flow control + FLOW_XONXOFF Software (XON/XOFF) flow control +\endverbatim +*/ +void QextSerialPort::setFlowControl(FlowType flow) { + QMutexLocker lock(mutex); + if (Settings.FlowControl!=flow) { + Settings.FlowControl=flow; + } + if (isOpen()) { + switch(flow) { + + /*no flow control*/ + case FLOW_OFF: + Win_CommConfig.dcb.fOutxCtsFlow=FALSE; + Win_CommConfig.dcb.fRtsControl=RTS_CONTROL_DISABLE; + Win_CommConfig.dcb.fInX=FALSE; + Win_CommConfig.dcb.fOutX=FALSE; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + break; + + /*software (XON/XOFF) flow control*/ + case FLOW_XONXOFF: + Win_CommConfig.dcb.fOutxCtsFlow=FALSE; + Win_CommConfig.dcb.fRtsControl=RTS_CONTROL_DISABLE; + Win_CommConfig.dcb.fInX=TRUE; + Win_CommConfig.dcb.fOutX=TRUE; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + break; + + case FLOW_HARDWARE: + Win_CommConfig.dcb.fOutxCtsFlow=TRUE; + Win_CommConfig.dcb.fRtsControl=RTS_CONTROL_HANDSHAKE; + Win_CommConfig.dcb.fInX=FALSE; + Win_CommConfig.dcb.fOutX=FALSE; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + break; + } + } +} + +/*! +Sets the parity associated with the serial port. The possible values of parity are: +\verbatim + PAR_SPACE Space Parity + PAR_MARK Mark Parity + PAR_NONE No Parity + PAR_EVEN Even Parity + PAR_ODD Odd Parity +\endverbatim +*/ +void QextSerialPort::setParity(ParityType parity) { + QMutexLocker lock(mutex); + if (Settings.Parity!=parity) { + Settings.Parity=parity; + } + if (isOpen()) { + Win_CommConfig.dcb.Parity=(unsigned char)parity; + switch (parity) { + + /*space parity*/ + case PAR_SPACE: + if (Settings.DataBits==DATA_8) { + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Space parity with 8 data bits is not supported by POSIX systems."); + } + Win_CommConfig.dcb.fParity=TRUE; + break; + + /*mark parity - WINDOWS ONLY*/ + case PAR_MARK: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: Mark parity is not supported by POSIX systems"); + Win_CommConfig.dcb.fParity=TRUE; + break; + + /*no parity*/ + case PAR_NONE: + Win_CommConfig.dcb.fParity=FALSE; + break; + + /*even parity*/ + case PAR_EVEN: + Win_CommConfig.dcb.fParity=TRUE; + break; + + /*odd parity*/ + case PAR_ODD: + Win_CommConfig.dcb.fParity=TRUE; + break; + } + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } +} + +/*! +Sets the number of data bits used by the serial port. Possible values of dataBits are: +\verbatim + DATA_5 5 data bits + DATA_6 6 data bits + DATA_7 7 data bits + DATA_8 8 data bits +\endverbatim + +\note +This function is subject to the following restrictions: +\par + 5 data bits cannot be used with 2 stop bits. +\par + 1.5 stop bits can only be used with 5 data bits. +\par + 8 data bits cannot be used with space parity on POSIX systems. +*/ +void QextSerialPort::setDataBits(DataBitsType dataBits) { + QMutexLocker lock(mutex); + if (Settings.DataBits!=dataBits) { + if ((Settings.StopBits==STOP_2 && dataBits==DATA_5) || + (Settings.StopBits==STOP_1_5 && dataBits!=DATA_5)) { + } + else { + Settings.DataBits=dataBits; + } + } + if (isOpen()) { + switch(dataBits) { + + /*5 data bits*/ + case DATA_5: + if (Settings.StopBits==STOP_2) { + TTY_WARNING("QextSerialPort: 5 Data bits cannot be used with 2 stop bits."); + } + else { + Win_CommConfig.dcb.ByteSize=5; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + + /*6 data bits*/ + case DATA_6: + if (Settings.StopBits==STOP_1_5) { + TTY_WARNING("QextSerialPort: 6 Data bits cannot be used with 1.5 stop bits."); + } + else { + Win_CommConfig.dcb.ByteSize=6; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + + /*7 data bits*/ + case DATA_7: + if (Settings.StopBits==STOP_1_5) { + TTY_WARNING("QextSerialPort: 7 Data bits cannot be used with 1.5 stop bits."); + } + else { + Win_CommConfig.dcb.ByteSize=7; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + + /*8 data bits*/ + case DATA_8: + if (Settings.StopBits==STOP_1_5) { + TTY_WARNING("QextSerialPort: 8 Data bits cannot be used with 1.5 stop bits."); + } + else { + Win_CommConfig.dcb.ByteSize=8; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + } + } +} + +/*! +Sets the number of stop bits used by the serial port. Possible values of stopBits are: +\verbatim + STOP_1 1 stop bit + STOP_1_5 1.5 stop bits + STOP_2 2 stop bits +\endverbatim + +\note +This function is subject to the following restrictions: +\par + 2 stop bits cannot be used with 5 data bits. +\par + 1.5 stop bits cannot be used with 6 or more data bits. +\par + POSIX does not support 1.5 stop bits. +*/ +void QextSerialPort::setStopBits(StopBitsType stopBits) { + QMutexLocker lock(mutex); + if (Settings.StopBits!=stopBits) { + if ((Settings.DataBits==DATA_5 && stopBits==STOP_2) || + (stopBits==STOP_1_5 && Settings.DataBits!=DATA_5)) { + } + else { + Settings.StopBits=stopBits; + } + } + if (isOpen()) { + switch (stopBits) { + + /*one stop bit*/ + case STOP_1: + Win_CommConfig.dcb.StopBits=ONESTOPBIT; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + break; + + /*1.5 stop bits*/ + case STOP_1_5: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: 1.5 stop bit operation is not supported by POSIX."); + if (Settings.DataBits!=DATA_5) { + TTY_WARNING("QextSerialPort: 1.5 stop bits can only be used with 5 data bits"); + } + else { + Win_CommConfig.dcb.StopBits=ONE5STOPBITS; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + + /*two stop bits*/ + case STOP_2: + if (Settings.DataBits==DATA_5) { + TTY_WARNING("QextSerialPort: 2 stop bits cannot be used with 5 data bits"); + } + else { + Win_CommConfig.dcb.StopBits=TWOSTOPBITS; + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } + break; + } + } +} + +/*! +Sets the baud rate of the serial port. Note that not all rates are applicable on +all platforms. The following table shows translations of the various baud rate +constants on Windows(including NT/2000) and POSIX platforms. Speeds marked with an * +are speeds that are usable on both Windows and POSIX. +\verbatim + + RATE Windows Speed POSIX Speed + ----------- ------------- ----------- + BAUD50 110 50 + BAUD75 110 75 + *BAUD110 110 110 + BAUD134 110 134.5 + BAUD150 110 150 + BAUD200 110 200 + *BAUD300 300 300 + *BAUD600 600 600 + *BAUD1200 1200 1200 + BAUD1800 1200 1800 + *BAUD2400 2400 2400 + *BAUD4800 4800 4800 + *BAUD9600 9600 9600 + BAUD14400 14400 9600 + *BAUD19200 19200 19200 + *BAUD38400 38400 38400 + BAUD56000 56000 38400 + *BAUD57600 57600 57600 + BAUD76800 57600 76800 + *BAUD115200 115200 115200 + BAUD128000 128000 115200 + BAUD256000 256000 115200 +\endverbatim +*/ +void QextSerialPort::setBaudRate(BaudRateType baudRate) { + QMutexLocker lock(mutex); + if (Settings.BaudRate!=baudRate) { + switch (baudRate) { + case BAUD50: + case BAUD75: + case BAUD134: + case BAUD150: + case BAUD200: + Settings.BaudRate=BAUD110; + break; + + case BAUD1800: + Settings.BaudRate=BAUD1200; + break; + + case BAUD76800: + Settings.BaudRate=BAUD57600; + break; + + default: + Settings.BaudRate=baudRate; + break; + } + } + if (isOpen()) { + switch (baudRate) { + + /*50 baud*/ + case BAUD50: + TTY_WARNING("QextSerialPort: Windows does not support 50 baud operation. Switching to 110 baud."); + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*75 baud*/ + case BAUD75: + TTY_WARNING("QextSerialPort: Windows does not support 75 baud operation. Switching to 110 baud."); + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*110 baud*/ + case BAUD110: + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*134.5 baud*/ + case BAUD134: + TTY_WARNING("QextSerialPort: Windows does not support 134.5 baud operation. Switching to 110 baud."); + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*150 baud*/ + case BAUD150: + TTY_WARNING("QextSerialPort: Windows does not support 150 baud operation. Switching to 110 baud."); + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*200 baud*/ + case BAUD200: + TTY_WARNING("QextSerialPort: Windows does not support 200 baud operation. Switching to 110 baud."); + Win_CommConfig.dcb.BaudRate=CBR_110; + break; + + /*300 baud*/ + case BAUD300: + Win_CommConfig.dcb.BaudRate=CBR_300; + break; + + /*600 baud*/ + case BAUD600: + Win_CommConfig.dcb.BaudRate=CBR_600; + break; + + /*1200 baud*/ + case BAUD1200: + Win_CommConfig.dcb.BaudRate=CBR_1200; + break; + + /*1800 baud*/ + case BAUD1800: + TTY_WARNING("QextSerialPort: Windows does not support 1800 baud operation. Switching to 1200 baud."); + Win_CommConfig.dcb.BaudRate=CBR_1200; + break; + + /*2400 baud*/ + case BAUD2400: + Win_CommConfig.dcb.BaudRate=CBR_2400; + break; + + /*4800 baud*/ + case BAUD4800: + Win_CommConfig.dcb.BaudRate=CBR_4800; + break; + + /*9600 baud*/ + case BAUD9600: + Win_CommConfig.dcb.BaudRate=CBR_9600; + break; + + /*14400 baud*/ + case BAUD14400: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 14400 baud operation."); + Win_CommConfig.dcb.BaudRate=CBR_14400; + break; + + /*19200 baud*/ + case BAUD19200: + Win_CommConfig.dcb.BaudRate=CBR_19200; + break; + + /*38400 baud*/ + case BAUD38400: + Win_CommConfig.dcb.BaudRate=CBR_38400; + break; + + /*56000 baud*/ + case BAUD56000: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 56000 baud operation."); + Win_CommConfig.dcb.BaudRate=CBR_56000; + break; + + /*57600 baud*/ + case BAUD57600: + Win_CommConfig.dcb.BaudRate=CBR_57600; + break; + + /*76800 baud*/ + case BAUD76800: + TTY_WARNING("QextSerialPort: Windows does not support 76800 baud operation. Switching to 57600 baud."); + Win_CommConfig.dcb.BaudRate=CBR_57600; + break; + + /*115200 baud*/ + case BAUD115200: + Win_CommConfig.dcb.BaudRate=CBR_115200; + break; + + /*128000 baud*/ + case BAUD128000: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 128000 baud operation."); + Win_CommConfig.dcb.BaudRate=CBR_128000; + break; + + /*256000 baud*/ + case BAUD256000: + TTY_PORTABILITY_WARNING("QextSerialPort Portability Warning: POSIX does not support 256000 baud operation."); + Win_CommConfig.dcb.BaudRate=CBR_256000; + break; + } + SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); + } +} + +/*! +Sets DTR line to the requested state (high by default). This function will have no effect if +the port associated with the class is not currently open. +*/ +void QextSerialPort::setDtr(bool set) { + QMutexLocker lock(mutex); + if (isOpen()) { + if (set) { + EscapeCommFunction(Win_Handle, SETDTR); + } + else { + EscapeCommFunction(Win_Handle, CLRDTR); + } + } +} + +/*! +Sets RTS line to the requested state (high by default). This function will have no effect if +the port associated with the class is not currently open. +*/ +void QextSerialPort::setRts(bool set) { + QMutexLocker lock(mutex); + if (isOpen()) { + if (set) { + EscapeCommFunction(Win_Handle, SETRTS); + } + else { + EscapeCommFunction(Win_Handle, CLRRTS); + } + } +} + +/*! +Returns the line status as stored by the port function. This function will retrieve the states +of the following lines: DCD, CTS, DSR, and RI. On POSIX systems, the following additional lines +can be monitored: DTR, RTS, Secondary TXD, and Secondary RXD. The value returned is an unsigned +long with specific bits indicating which lines are high. The following constants should be used +to examine the states of individual lines: + +\verbatim +Mask Line +------ ---- +LS_CTS CTS +LS_DSR DSR +LS_DCD DCD +LS_RI RI +\endverbatim + +This function will return 0 if the port associated with the class is not currently open. +*/ +ulong QextSerialPort::lineStatus(void) { + unsigned long Status=0, Temp=0; + QMutexLocker lock(mutex); + if (isOpen()) { + GetCommModemStatus(Win_Handle, &Temp); + if (Temp&MS_CTS_ON) { + Status|=LS_CTS; + } + if (Temp&MS_DSR_ON) { + Status|=LS_DSR; + } + if (Temp&MS_RING_ON) { + Status|=LS_RI; + } + if (Temp&MS_RLSD_ON) { + Status|=LS_DCD; + } + } + return Status; +} + +bool QextSerialPort::waitForReadyRead(int msecs) +{ + //@todo implement + return false; +} + +qint64 QextSerialPort::bytesToWrite() const +{ + QReadLocker rl(bytesToWriteLock); + return _bytesToWrite; +} + +/* + Triggered when there's activity on our HANDLE. +*/ +void QextSerialPort::onWinEvent(HANDLE h) +{ + QMutexLocker lock(mutex); + if(h == overlap.hEvent) { + if (eventMask & EV_RXCHAR) { + if (sender() != this && bytesAvailable() > 0) + emit readyRead(); + } + if (eventMask & EV_TXEMPTY) { + /* + A write completed. Run through the list of OVERLAPPED writes, and if + they completed successfully, take them off the list and delete them. + Otherwise, leave them on there so they can finish. + */ + qint64 totalBytesWritten = 0; + QList overlapsToDelete; + foreach(OVERLAPPED* o, pendingWrites) { + DWORD numBytes = 0; + if (GetOverlappedResult(Win_Handle, o, & numBytes, false)) { + overlapsToDelete.append(o); + totalBytesWritten += numBytes; + } else if( GetLastError() != ERROR_IO_INCOMPLETE ) { + overlapsToDelete.append(o); + qWarning() << "CommEvent overlapped write error:" << GetLastError(); + } + } + + if (sender() != this && totalBytesWritten > 0) { + QWriteLocker writelocker(bytesToWriteLock); + emit bytesWritten(totalBytesWritten); + _bytesToWrite = 0; + } + + foreach(OVERLAPPED* o, overlapsToDelete) { + OVERLAPPED *toDelete = pendingWrites.takeAt(pendingWrites.indexOf(o)); + CloseHandle(toDelete->hEvent); + delete toDelete; + } + } + if (eventMask & EV_DSR) { + if (lineStatus() & LS_DSR) + emit dsrChanged(true); + else + emit dsrChanged(false); + } + } + WaitCommEvent(Win_Handle, &eventMask, &overlap); +} + +/*! +Sets the read and write timeouts for the port to millisec milliseconds. +Setting 0 indicates that timeouts are not used for read nor write operations; +however read() and write() functions will still block. Set -1 to provide +non-blocking behaviour (read() and write() will return immediately). + +\note this function does nothing in event driven mode. +*/ +void QextSerialPort::setTimeout(long millisec) { + QMutexLocker lock(mutex); + Settings.Timeout_Millisec = millisec; + + if (millisec == -1) { + Win_CommTimeouts.ReadIntervalTimeout = MAXDWORD; + Win_CommTimeouts.ReadTotalTimeoutConstant = 0; + } else { + Win_CommTimeouts.ReadIntervalTimeout = millisec; + Win_CommTimeouts.ReadTotalTimeoutConstant = millisec; + } + Win_CommTimeouts.ReadTotalTimeoutMultiplier = 0; + Win_CommTimeouts.WriteTotalTimeoutMultiplier = millisec; + Win_CommTimeouts.WriteTotalTimeoutConstant = 0; + if (queryMode() != QextSerialPort::EventDriven) + SetCommTimeouts(Win_Handle, &Win_CommTimeouts); +} +