123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- /****************************************************************************
- ** Copyright (c) 2000-2003 Wayne Roth
- ** Copyright (c) 2004-2007 Stefan Sander
- ** Copyright (c) 2007 Michal Policht
- ** Copyright (c) 2008 Brandon Fosdick
- ** Copyright (c) 2009-2010 Liam Staskawicz
- ** Copyright (c) 2011 Debao Zhang
- ** All right reserved.
- ** Web: http://code.google.com/p/qextserialport/
- **
- ** Permission is hereby granted, free of charge, to any person obtaining
- ** a copy of this software and associated documentation files (the
- ** "Software"), to deal in the Software without restriction, including
- ** without limitation the rights to use, copy, modify, merge, publish,
- ** distribute, sublicense, and/or sell copies of the Software, and to
- ** permit persons to whom the Software is furnished to do so, subject to
- ** the following conditions:
- **
- ** The above copyright notice and this permission notice shall be
- ** included in all copies or substantial portions of the Software.
- **
- ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- **
- ****************************************************************************/
-
- #include "qextserialport.h"
- #include "qextserialport_p.h"
- #include <QtCore/QThread>
- #include <QtCore/QReadWriteLock>
- #include <QtCore/QMutexLocker>
- #include <QtCore/QDebug>
- #include <QtCore/QRegExp>
- #include <QtCore/QMetaType>
- #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
- # include <QtCore/QWinEventNotifier>
- #else
- # include <QtCore/private/qwineventnotifier_p.h>
- #endif
- void QextSerialPortPrivate::platformSpecificInit()
- {
- handle = INVALID_HANDLE_VALUE;
- ZeroMemory(&overlap, sizeof(OVERLAPPED));
- overlap.hEvent = CreateEvent(NULL, true, false, NULL);
- winEventNotifier = 0;
- bytesToWriteLock = new QReadWriteLock;
- }
-
- void QextSerialPortPrivate::platformSpecificDestruct() {
- CloseHandle(overlap.hEvent);
- delete bytesToWriteLock;
- }
-
-
- /*!
- \internal
- COM ports greater than 9 need \\.\ prepended
-
- This is only need when open the port.
- */
- static QString fullPortNameWin(const QString &name)
- {
- QRegExp rx(QLatin1String("^COM(\\d+)"));
- QString fullName(name);
- if (fullName.contains(rx))
- fullName.prepend(QLatin1String("\\\\.\\"));
- return fullName;
- }
-
- bool QextSerialPortPrivate::open_sys(QIODevice::OpenMode mode)
- {
- Q_Q(QextSerialPort);
- DWORD confSize = sizeof(COMMCONFIG);
- commConfig.dwSize = confSize;
- DWORD dwFlagsAndAttributes = 0;
- if (queryMode == QextSerialPort::EventDriven)
- dwFlagsAndAttributes += FILE_FLAG_OVERLAPPED;
-
- /*open the port*/
- handle = CreateFileW((wchar_t *)fullPortNameWin(port).utf16(), GENERIC_READ|GENERIC_WRITE,
- 0, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
- if (handle != INVALID_HANDLE_VALUE) {
- q->setOpenMode(mode);
- /*configure port settings*/
- GetCommConfig(handle, &commConfig, &confSize);
- GetCommState(handle, &(commConfig.dcb));
-
- /*set up parameters*/
- commConfig.dcb.fBinary = TRUE;
- commConfig.dcb.fInX = FALSE;
- commConfig.dcb.fOutX = FALSE;
- commConfig.dcb.fAbortOnError = FALSE;
- commConfig.dcb.fNull = FALSE;
- /* Dtr default to true. See Issue 122*/
- commConfig.dcb.fDtrControl = TRUE;
- /*flush all settings*/
- settingsDirtyFlags = DFE_ALL;
- updatePortSettings();
-
- //init event driven approach
- if (queryMode == QextSerialPort::EventDriven) {
- if (!SetCommMask(handle, EV_TXEMPTY | EV_RXCHAR | EV_DSR)) {
- QESP_WARNING()<<"failed to set Comm Mask. Error code:"<<GetLastError();
- return false;
- }
- winEventNotifier = new QWinEventNotifier(overlap.hEvent, q);
- qRegisterMetaType<HANDLE>("HANDLE");
- q->connect(winEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onWinEvent(HANDLE)), Qt::DirectConnection);
- WaitCommEvent(handle, &eventMask, &overlap);
- }
- return true;
- }
- return false;
- }
-
- bool QextSerialPortPrivate::close_sys()
- {
- flush_sys();
- CancelIo(handle);
- if (CloseHandle(handle))
- handle = INVALID_HANDLE_VALUE;
- if (winEventNotifier) {
- winEventNotifier->setEnabled(false);
- winEventNotifier->deleteLater();
- winEventNotifier = 0;
- }
-
- foreach (OVERLAPPED *o, pendingWrites) {
- CloseHandle(o->hEvent);
- delete o;
- }
- pendingWrites.clear();
- return true;
- }
-
- bool QextSerialPortPrivate::flush_sys()
- {
- FlushFileBuffers(handle);
- return true;
- }
-
- qint64 QextSerialPortPrivate::bytesAvailable_sys() const
- {
- DWORD Errors;
- COMSTAT Status;
- if (ClearCommError(handle, &Errors, &Status))
- return Status.cbInQue;
-
- return (qint64)-1;
- }
-
- /*
- Translates a system-specific error code to a QextSerialPort error code. Used internally.
- */
- void QextSerialPortPrivate::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 QextSerialPortPrivate::readData_sys(char *data, qint64 maxSize)
- {
- DWORD bytesRead = 0;
- bool failed = false;
- if (queryMode == QextSerialPort::EventDriven) {
- OVERLAPPED overlapRead;
- ZeroMemory(&overlapRead, sizeof(OVERLAPPED));
- if (!ReadFile(handle, (void *)data, (DWORD)maxSize, &bytesRead, &overlapRead)) {
- if (GetLastError() == ERROR_IO_PENDING)
- GetOverlappedResult(handle, &overlapRead, &bytesRead, true);
- else
- failed = true;
- }
- } else if (!ReadFile(handle, (void *)data, (DWORD)maxSize, &bytesRead, NULL)) {
- failed = true;
- }
- if (!failed)
- return (qint64)bytesRead;
-
- lastErr = E_READ_FAILED;
- return -1;
- }
-
- /*
- 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 QextSerialPortPrivate::writeData_sys(const char *data, qint64 maxSize)
- {
- DWORD bytesWritten = 0;
- bool failed = false;
- if (queryMode == QextSerialPort::EventDriven) {
- OVERLAPPED *newOverlapWrite = new OVERLAPPED;
- ZeroMemory(newOverlapWrite, sizeof(OVERLAPPED));
- newOverlapWrite->hEvent = CreateEvent(NULL, true, false, NULL);
- if (WriteFile(handle, (void *)data, (DWORD)maxSize, &bytesWritten, newOverlapWrite)) {
- CloseHandle(newOverlapWrite->hEvent);
- delete newOverlapWrite;
- } else if (GetLastError() == ERROR_IO_PENDING) {
- // writing asynchronously...not an error
- QWriteLocker writelocker(bytesToWriteLock);
- pendingWrites.append(newOverlapWrite);
- } else {
- QESP_WARNING()<<"QextSerialPort write error:"<<GetLastError();
- failed = true;
- if (!CancelIo(newOverlapWrite->hEvent))
- QESP_WARNING("QextSerialPort: couldn't cancel IO");
- if (!CloseHandle(newOverlapWrite->hEvent))
- QESP_WARNING("QextSerialPort: couldn't close OVERLAPPED handle");
- delete newOverlapWrite;
- }
- } else if (!WriteFile(handle, (void *)data, (DWORD)maxSize, &bytesWritten, NULL)) {
- failed = true;
- }
-
- if (!failed)
- return (qint64)bytesWritten;
-
- lastErr = E_WRITE_FAILED;
- return -1;
- }
-
- void QextSerialPortPrivate::setDtr_sys(bool set) {
- EscapeCommFunction(handle, set ? SETDTR : CLRDTR);
- }
-
- void QextSerialPortPrivate::setRts_sys(bool set) {
- EscapeCommFunction(handle, set ? SETRTS : CLRRTS);
- }
-
- ulong QextSerialPortPrivate::lineStatus_sys(void) {
- unsigned long Status = 0, Temp = 0;
- GetCommModemStatus(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;
- }
-
- /*
- Triggered when there's activity on our HANDLE.
- */
- void QextSerialPortPrivate::_q_onWinEvent(HANDLE h)
- {
- Q_Q(QextSerialPort);
- if (h == overlap.hEvent) {
- if (eventMask & EV_RXCHAR) {
- if (q->sender() != q && bytesAvailable_sys() > 0)
- _q_canRead();
- }
- if (eventMask & EV_TXEMPTY) {
- /*
- A write completed. Run through the list of OVERLAPPED writes, and if
- they completed successfully, take them off the list and delete them.
- Otherwise, leave them on there so they can finish.
- */
- qint64 totalBytesWritten = 0;
- QList<OVERLAPPED *> overlapsToDelete;
- QWriteLocker writelocker(bytesToWriteLock);
- foreach (OVERLAPPED *o, pendingWrites) {
- DWORD numBytes = 0;
- if (GetOverlappedResult(handle, o, &numBytes, false)) {
- overlapsToDelete.append(o);
- totalBytesWritten += numBytes;
- } else if (GetLastError() != ERROR_IO_INCOMPLETE) {
- overlapsToDelete.append(o);
- QESP_WARNING()<<"CommEvent overlapped write error:" << GetLastError();
- }
- }
-
- if (q->sender() != q && totalBytesWritten > 0)
- Q_EMIT q->bytesWritten(totalBytesWritten);
-
- foreach (OVERLAPPED *o, overlapsToDelete) {
- OVERLAPPED *toDelete = pendingWrites.takeAt(pendingWrites.indexOf(o));
- CloseHandle(toDelete->hEvent);
- delete toDelete;
- }
- }
- if (eventMask & EV_DSR) {
- if (lineStatus_sys() & LS_DSR)
- Q_EMIT q->dsrChanged(true);
- else
- Q_EMIT q->dsrChanged(false);
- }
- }
- WaitCommEvent(handle, &eventMask, &overlap);
- }
-
- void QextSerialPortPrivate::updatePortSettings()
- {
- if (!q_ptr->isOpen() || !settingsDirtyFlags)
- return;
-
- //fill struct : COMMCONFIG
- if (settingsDirtyFlags & DFE_BaudRate)
- commConfig.dcb.BaudRate = settings.BaudRate;
- if (settingsDirtyFlags & DFE_Parity) {
- commConfig.dcb.Parity = (BYTE)settings.Parity;
- commConfig.dcb.fParity = (settings.Parity == PAR_NONE) ? FALSE : TRUE;
- }
- if (settingsDirtyFlags & DFE_DataBits)
- commConfig.dcb.ByteSize = (BYTE)settings.DataBits;
- if (settingsDirtyFlags & DFE_StopBits) {
- switch (settings.StopBits) {
- case STOP_1:
- commConfig.dcb.StopBits = ONESTOPBIT;
- break;
- case STOP_1_5:
- commConfig.dcb.StopBits = ONE5STOPBITS;
- break;
- case STOP_2:
- commConfig.dcb.StopBits = TWOSTOPBITS;
- break;
- }
- }
- if (settingsDirtyFlags & DFE_Flow) {
- switch(settings.FlowControl) {
- /*no flow control*/
- case FLOW_OFF:
- commConfig.dcb.fOutxCtsFlow = FALSE;
- commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
- commConfig.dcb.fInX = FALSE;
- commConfig.dcb.fOutX = FALSE;
- break;
- /*software (XON/XOFF) flow control*/
- case FLOW_XONXOFF:
- commConfig.dcb.fOutxCtsFlow = FALSE;
- commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
- commConfig.dcb.fInX = TRUE;
- commConfig.dcb.fOutX = TRUE;
- break;
- /*hardware flow control*/
- case FLOW_HARDWARE:
- commConfig.dcb.fOutxCtsFlow = TRUE;
- commConfig.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
- commConfig.dcb.fInX = FALSE;
- commConfig.dcb.fOutX = FALSE;
- break;
- }
- }
-
- //fill struct : COMMTIMEOUTS
- if (settingsDirtyFlags & DFE_TimeOut) {
- if (queryMode != QextSerialPort::EventDriven) {
- int millisec = settings.Timeout_Millisec;
- if (millisec == -1) {
- commTimeouts.ReadIntervalTimeout = MAXDWORD;
- commTimeouts.ReadTotalTimeoutConstant = 0;
- } else {
- commTimeouts.ReadIntervalTimeout = millisec;
- commTimeouts.ReadTotalTimeoutConstant = millisec;
- }
- commTimeouts.ReadTotalTimeoutMultiplier = 0;
- commTimeouts.WriteTotalTimeoutMultiplier = millisec;
- commTimeouts.WriteTotalTimeoutConstant = 0;
- } else {
- commTimeouts.ReadIntervalTimeout = MAXDWORD;
- commTimeouts.ReadTotalTimeoutMultiplier = 0;
- commTimeouts.ReadTotalTimeoutConstant = 0;
- commTimeouts.WriteTotalTimeoutMultiplier = 0;
- commTimeouts.WriteTotalTimeoutConstant = 0;
- }
- }
-
-
- if (settingsDirtyFlags & DFE_Settings_Mask)
- SetCommConfig(handle, &commConfig, sizeof(COMMCONFIG));
- if ((settingsDirtyFlags & DFE_TimeOut))
- SetCommTimeouts(handle, &commTimeouts);
- settingsDirtyFlags = 0;
- }
|