summaryrefslogtreecommitdiffstats
path: root/tools/runonphone/symbianutils/virtualserialdevice_win.cpp
diff options
context:
space:
mode:
authorStephen Chong <steven.chong@nokia.com>2011-07-21 19:39:55 (GMT)
committerShane Kearns <shane.kearns@accenture.com>2011-07-28 12:28:24 (GMT)
commit16564e40046dea419925a4422094aed5a4d84a54 (patch)
tree30ab24752070fbf946014f5c12d7638ff4677816 /tools/runonphone/symbianutils/virtualserialdevice_win.cpp
parent5679d966b396523a1d555a4029e7da05515be2fa (diff)
downloadQt-16564e40046dea419925a4422094aed5a4d84a54.zip
Qt-16564e40046dea419925a4422094aed5a4d84a54.tar.gz
Qt-16564e40046dea419925a4422094aed5a4d84a54.tar.bz2
Runonphone with CODA support
Reviewed-By: Shane Kearns
Diffstat (limited to 'tools/runonphone/symbianutils/virtualserialdevice_win.cpp')
-rw-r--r--tools/runonphone/symbianutils/virtualserialdevice_win.cpp369
1 files changed, 369 insertions, 0 deletions
diff --git a/tools/runonphone/symbianutils/virtualserialdevice_win.cpp b/tools/runonphone/symbianutils/virtualserialdevice_win.cpp
new file mode 100644
index 0000000..389658c
--- /dev/null
+++ b/tools/runonphone/symbianutils/virtualserialdevice_win.cpp
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "virtualserialdevice.h"
+#include <windows.h>
+#include <QtCore/private/qwineventnotifier_p.h>
+#include <QtCore/QThread>
+#include <QtCore/QWaitCondition>
+
+namespace SymbianUtils {
+
+class VirtualSerialDevicePrivate
+{
+public:
+ HANDLE portHandle;
+ OVERLAPPED writeOverlapped;
+ OVERLAPPED commEventOverlapped;
+ DWORD commEventMask;
+ QWinEventNotifier *writeCompleteNotifier;
+ QWinEventNotifier *commEventNotifier;
+};
+
+void VirtualSerialDevice::platInit()
+{
+ d = new VirtualSerialDevicePrivate;
+ d->portHandle = INVALID_HANDLE_VALUE;
+ d->writeCompleteNotifier = NULL;
+ memset(&d->writeOverlapped, 0, sizeof(OVERLAPPED));
+ d->commEventNotifier = NULL;
+ memset(&d->commEventOverlapped, 0, sizeof(OVERLAPPED));
+}
+
+QString windowsPortName(const QString& port)
+{
+ // Add the \\.\ to the name if it's a COM port and doesn't already have it
+ QString winPortName(port);
+ if (winPortName.startsWith(QLatin1String("COM"))) {
+ winPortName.prepend("\\\\.\\");
+ }
+ return winPortName;
+}
+
+// Copied from \creator\src\libs\utils\winutils.cpp
+QString winErrorMessage(unsigned long error)
+{
+ // Some of the windows error messages are a bit too obscure
+ switch (error)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_NOT_FOUND:
+ return VirtualSerialDevice::tr("Port not found");
+ break;
+ case ERROR_ACCESS_DENIED:
+ return VirtualSerialDevice::tr("Port in use");
+ case ERROR_SEM_TIMEOUT: // Bluetooth ports sometimes return this
+ return VirtualSerialDevice::tr("Timed out");
+ case ERROR_NETWORK_UNREACHABLE:
+ return VirtualSerialDevice::tr("Port unreachable"); // I don't know what this error indicates... from observation, that the windows Bluetooth stack has got itself into a state and needs resetting
+ default:
+ break;
+ }
+
+ QString rc = QString::fromLatin1("#%1: ").arg(error);
+ ushort *lpMsgBuf;
+
+ const int len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
+ if (len) {
+ rc = QString::fromUtf16(lpMsgBuf, len);
+ LocalFree(lpMsgBuf);
+ } else {
+ rc += QString::fromLatin1("<unknown error>");
+ }
+ return rc.trimmed();
+}
+
+bool VirtualSerialDevice::open(OpenMode mode)
+{
+ Q_ASSERT(QThread::currentThread() == thread());
+ if (isOpen()) return true;
+
+ d->portHandle = CreateFileA(windowsPortName(portName).toAscii(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (d->portHandle == INVALID_HANDLE_VALUE) {
+ setErrorString(tr("The port %1 could not be opened: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ return false;
+ }
+
+ DCB commState;
+ memset(&commState, 0, sizeof(DCB));
+ commState.DCBlength = sizeof(DCB);
+ bool ok = GetCommState(d->portHandle, &commState);
+ if (ok) {
+ commState.BaudRate = CBR_115200;
+ commState.fBinary = TRUE;
+ commState.fParity = FALSE;
+ commState.fOutxCtsFlow = FALSE;
+ commState.fOutxDsrFlow = FALSE;
+ commState.fInX = FALSE;
+ commState.fOutX = FALSE;
+ commState.fNull = FALSE;
+ commState.fAbortOnError = FALSE;
+ commState.fDsrSensitivity = FALSE;
+ commState.fDtrControl = DTR_CONTROL_DISABLE;
+ commState.ByteSize = 8;
+ commState.Parity = NOPARITY;
+ commState.StopBits = ONESTOPBIT;
+ ok = SetCommState(d->portHandle, &commState);
+ }
+ if (!ok) {
+ qWarning("%s setting comm state", qPrintable(winErrorMessage(GetLastError())));
+ }
+
+ // http://msdn.microsoft.com/en-us/library/aa363190(v=vs.85).aspx says this means
+ // "the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received"
+ COMMTIMEOUTS timeouts;
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 0;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ SetCommTimeouts(d->portHandle, &timeouts);
+
+ d->writeOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ d->writeCompleteNotifier = new QWinEventNotifier(d->writeOverlapped.hEvent, this);
+ connect(d->writeCompleteNotifier, SIGNAL(activated(HANDLE)), this, SLOT(writeCompleted()));
+
+ // This is how we implement readyRead notifications
+ d->commEventOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ d->commEventNotifier = new QWinEventNotifier(d->commEventOverlapped.hEvent, this);
+ connect(d->commEventNotifier, SIGNAL(activated(HANDLE)), this, SLOT(commEventOccurred()));
+
+ if (!SetCommMask(d->portHandle, EV_RXCHAR)) {
+ // What to do?
+ qWarning("%s: Could not set comm mask, err=%d", Q_FUNC_INFO, (int)GetLastError());
+ }
+ bool result = WaitCommEvent(d->portHandle, &d->commEventMask, &d->commEventOverlapped);
+ Q_ASSERT(result == false); // Can't see how it would make sense to be anything else...
+ (void)result; // For release build
+ if (GetLastError() != ERROR_IO_PENDING) {
+ setErrorString(tr("An error occurred while waiting for read notifications from %1: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ close();
+ return false;
+ }
+
+ ok = QIODevice::open(mode);
+ if (!ok) close();
+ return ok;
+}
+
+void VirtualSerialDevice::platClose()
+{
+ delete d->writeCompleteNotifier;
+ d->writeCompleteNotifier = NULL;
+ CloseHandle(d->writeOverlapped.hEvent);
+ d->writeOverlapped.hEvent = INVALID_HANDLE_VALUE;
+
+ delete d->commEventNotifier;
+ d->commEventNotifier = NULL;
+ d->commEventOverlapped.hEvent = INVALID_HANDLE_VALUE;
+
+ CloseHandle(d->portHandle);
+ d->portHandle = INVALID_HANDLE_VALUE;
+}
+
+VirtualSerialDevice::~VirtualSerialDevice()
+{
+ close();
+ delete d;
+}
+
+qint64 VirtualSerialDevice::bytesAvailable() const
+{
+ QMutexLocker locker(&lock);
+ if (!isOpen()) return 0;
+
+ qint64 avail = 0;
+ COMSTAT Status;
+ if (ClearCommError(d->portHandle, NULL, &Status)) {
+ avail = Status.cbInQue;
+ }
+ return avail + QIODevice::bytesAvailable();
+}
+
+void VirtualSerialDevice::commEventOccurred()
+{
+ DWORD event = d->commEventMask;
+ if (event & EV_RXCHAR) {
+ emit readyRead();
+ }
+ ResetEvent(d->commEventOverlapped.hEvent);
+ WaitCommEvent(d->portHandle, &d->commEventMask, &d->commEventOverlapped);
+}
+
+qint64 VirtualSerialDevice::readData(char *data, qint64 maxSize)
+{
+ QMutexLocker locker(&lock);
+ // We do our reads synchronously
+ OVERLAPPED readOverlapped;
+ memset(&readOverlapped, 0, sizeof(OVERLAPPED));
+ DWORD bytesRead;
+ BOOL done = ReadFile(d->portHandle, data, maxSize, &bytesRead, &readOverlapped);
+ if (done) return (qint64)bytesRead;
+
+ if (GetLastError() == ERROR_IO_PENDING) {
+ // Note the TRUE to wait for the read to complete
+ done = GetOverlappedResult(d->portHandle, &readOverlapped, &bytesRead, TRUE);
+ if (done) return (qint64)bytesRead;
+ }
+
+ // If we reach here an error has occurred
+ setErrorString(tr("An error occurred while reading from %1: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ return -1;
+}
+
+
+qint64 VirtualSerialDevice::writeData(const char *data, qint64 maxSize)
+{
+ QMutexLocker locker(&lock);
+
+ pendingWrites.append(QByteArray(data, maxSize)); // Can't see a way of doing async io safely without having to copy here...
+ if (pendingWrites.count() == 1) {
+ return writeNextBuffer(locker);
+ } else {
+ return maxSize;
+ }
+}
+
+qint64 VirtualSerialDevice::writeNextBuffer(QMutexLocker& locker)
+{
+ Q_UNUSED(locker)
+ // Must be locked on entry
+ qint64 bufLen = pendingWrites[0].length();
+ BOOL ok = WriteFile(d->portHandle, pendingWrites[0].constData(), bufLen, NULL, &d->writeOverlapped);
+ if (ok || GetLastError() == ERROR_IO_PENDING) {
+ // Apparently it can return true for a small asynchronous write...
+ // Hopefully it still gets signalled in the same way!
+
+ // Wait for signal via writeCompleted
+ return bufLen;
+ }
+ else {
+ setErrorString(tr("An error occurred while writing to %1: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ pendingWrites.removeFirst();
+ return -1;
+ }
+}
+
+void VirtualSerialDevice::writeCompleted()
+{
+ QMutexLocker locker(&lock);
+ if (pendingWrites.count() == 0) {
+ qWarning("%s: writeCompleted called when there are no pending writes on %s!",
+ Q_FUNC_INFO, qPrintable(portName));
+ return;
+ }
+
+ doWriteCompleted(locker);
+}
+
+void VirtualSerialDevice::doWriteCompleted(QMutexLocker &locker)
+{
+ // Must be locked on entry
+ ResetEvent(d->writeOverlapped.hEvent);
+
+ qint64 len = pendingWrites.first().length();
+ pendingWrites.removeFirst();
+
+ if (pendingWrites.count() > 0) {
+ // Get the next write started before notifying in case client calls waitForBytesWritten in their slot
+ writeNextBuffer(locker);
+ }
+
+ emitBytesWrittenIfNeeded(locker, len);
+}
+
+bool VirtualSerialDevice::waitForBytesWritten(int msecs)
+{
+ QMutexLocker locker(&lock);
+ if (pendingWrites.count() == 0) return false;
+
+ if (QThread::currentThread() != thread()) {
+ // Wait for signal from main thread
+ unsigned long timeout = msecs;
+ if (msecs == -1) timeout = ULONG_MAX;
+ if (waiterForBytesWritten == NULL)
+ waiterForBytesWritten = new QWaitCondition;
+ return waiterForBytesWritten->wait(&lock, timeout);
+ }
+
+ DWORD waitTime = msecs;
+ if (msecs == -1) waitTime = INFINITE; // Ok these are probably bitwise the same, but just to prove I've thought about it...
+ DWORD result = WaitForSingleObject(d->writeOverlapped.hEvent, waitTime); // Do I need WaitForSingleObjectEx and worry about alertable states?
+ if (result == WAIT_TIMEOUT) {
+ return false;
+ }
+ else if (result == WAIT_OBJECT_0) {
+ DWORD bytesWritten;
+ BOOL ok = GetOverlappedResult(d->portHandle, &d->writeOverlapped, &bytesWritten, TRUE);
+ if (!ok) {
+ setErrorString(tr("An error occurred while syncing on waitForBytesWritten for %1: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ return false;
+ }
+ Q_ASSERT(bytesWritten == (DWORD)pendingWrites.first().length());
+
+ doWriteCompleted(locker);
+ return true;
+ }
+ else {
+ setErrorString(QString("An error occured in waitForBytesWritten() for %1: %2").
+ arg(portName, winErrorMessage(GetLastError())));
+ return false;
+ }
+}
+
+void VirtualSerialDevice::flush()
+{
+ while (waitForBytesWritten(-1)) { /* loop */ }
+}
+
+bool VirtualSerialDevice::waitForReadyRead(int msecs)
+{
+ return QIODevice::waitForReadyRead(msecs); //TODO
+}
+
+} // namespace SymbianUtils