OSDN Git Service

465545733b00079537071791a1dc4572a624c9a3
[qt-creator-jp/qt-creator-jp.git] / src / shared / symbianutils / virtualserialdevice_win.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "virtualserialdevice.h"
35 #include <windows.h>
36 #include <QtCore/private/qwineventnotifier_p.h>
37 #include <QtCore/QThread>
38 #include <QtCore/QWaitCondition>
39
40 namespace SymbianUtils {
41
42 class VirtualSerialDevicePrivate
43 {
44 public:
45     HANDLE portHandle;
46     OVERLAPPED writeOverlapped;
47     OVERLAPPED commEventOverlapped;
48     DWORD commEventMask;
49     QWinEventNotifier *writeCompleteNotifier;
50     QWinEventNotifier *commEventNotifier;
51 };
52
53 void VirtualSerialDevice::platInit()
54 {
55     d = new VirtualSerialDevicePrivate;
56     d->portHandle = INVALID_HANDLE_VALUE;
57     d->writeCompleteNotifier = NULL;
58     memset(&d->writeOverlapped, 0, sizeof(OVERLAPPED));
59     d->commEventNotifier = NULL;
60     memset(&d->commEventOverlapped, 0, sizeof(OVERLAPPED));
61 }
62
63 QString windowsPortName(const QString& port)
64 {
65     // Add the \\.\ to the name if it's a COM port and doesn't already have it
66     QString winPortName(port);
67     if (winPortName.startsWith(QLatin1String("COM"))) {
68         winPortName.prepend("\\\\.\\");
69     }
70     return winPortName;
71 }
72
73 // Copied from \creator\src\libs\utils\winutils.cpp
74 QString winErrorMessage(unsigned long error)
75 {
76     // Some of the windows error messages are a bit too obscure
77     switch (error)
78         {
79     case ERROR_FILE_NOT_FOUND:
80     case ERROR_NOT_FOUND:
81         return VirtualSerialDevice::tr("Port not found");
82         break;
83     case ERROR_ACCESS_DENIED:
84         return VirtualSerialDevice::tr("Port in use");
85     case ERROR_SEM_TIMEOUT: // Bluetooth ports sometimes return this
86         return VirtualSerialDevice::tr("Timed out");
87     case ERROR_NETWORK_UNREACHABLE:
88         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
89     default:
90         break;
91         }
92
93     QString rc = QString::fromLatin1("#%1: ").arg(error);
94     ushort *lpMsgBuf;
95
96     const int len = FormatMessage(
97             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
98             NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
99     if (len) {
100         rc = QString::fromUtf16(lpMsgBuf, len);
101         LocalFree(lpMsgBuf);
102     } else {
103         rc += QString::fromLatin1("<unknown error>");
104     }
105     return rc.trimmed();
106 }
107
108 bool VirtualSerialDevice::open(OpenMode mode)
109 {
110     Q_ASSERT(QThread::currentThread() == thread());
111     if (isOpen()) return true;
112
113     d->portHandle = CreateFileA(windowsPortName(portName).toAscii(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
114     if (d->portHandle == INVALID_HANDLE_VALUE) {
115         setErrorString(tr("The port %1 could not be opened: %2").
116                        arg(portName, winErrorMessage(GetLastError())));
117         return false;
118     }
119
120     DCB commState;
121     memset(&commState, 0, sizeof(DCB));
122     commState.DCBlength = sizeof(DCB);
123     bool ok = GetCommState(d->portHandle, &commState);
124     if (ok) {
125         commState.BaudRate = CBR_115200;
126         commState.fBinary = TRUE;
127         commState.fParity = FALSE;
128         commState.fOutxCtsFlow = FALSE;
129         commState.fOutxDsrFlow = FALSE;
130         commState.fInX = FALSE;
131         commState.fOutX = FALSE;
132         commState.fNull = FALSE;
133         commState.fAbortOnError = FALSE;
134         commState.fDsrSensitivity = FALSE;
135         commState.fDtrControl = DTR_CONTROL_DISABLE;
136         commState.ByteSize = 8;
137         commState.Parity = NOPARITY;
138         commState.StopBits = ONESTOPBIT;
139         ok = SetCommState(d->portHandle, &commState);
140     }
141     if (!ok) {
142         qWarning("%s setting comm state", qPrintable(winErrorMessage(GetLastError())));
143     }
144
145     // http://msdn.microsoft.com/en-us/library/aa363190(v=vs.85).aspx says this means
146     // "the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received"
147     COMMTIMEOUTS timeouts;
148     timeouts.ReadIntervalTimeout = MAXDWORD;
149     timeouts.ReadTotalTimeoutMultiplier = 0;
150     timeouts.ReadTotalTimeoutConstant = 0;
151     timeouts.WriteTotalTimeoutMultiplier = 0;
152     timeouts.WriteTotalTimeoutConstant = 0;
153     SetCommTimeouts(d->portHandle, &timeouts);
154
155     d->writeOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
156     d->writeCompleteNotifier = new QWinEventNotifier(d->writeOverlapped.hEvent, this);
157     connect(d->writeCompleteNotifier, SIGNAL(activated(HANDLE)), this, SLOT(writeCompleted()));
158
159     // This is how we implement readyRead notifications
160     d->commEventOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
161     d->commEventNotifier = new QWinEventNotifier(d->commEventOverlapped.hEvent, this);
162     connect(d->commEventNotifier, SIGNAL(activated(HANDLE)), this, SLOT(commEventOccurred()));
163
164     if (!SetCommMask(d->portHandle, EV_RXCHAR)) {
165         // What to do?
166         qWarning("%s: Could not set comm mask, err=%d", Q_FUNC_INFO, (int)GetLastError());
167     }
168     bool result = WaitCommEvent(d->portHandle, &d->commEventMask, &d->commEventOverlapped);
169     Q_ASSERT(result == false); // Can't see how it would make sense to be anything else...
170     (void)result; // For release build
171     if (GetLastError() != ERROR_IO_PENDING) {
172         setErrorString(tr("An error occurred while waiting for read notifications from %1: %2").
173                        arg(portName, winErrorMessage(GetLastError())));
174         close();
175         return false;
176     }
177
178     ok = QIODevice::open(mode);
179     if (!ok) close();
180     return ok;
181 }
182
183 void VirtualSerialDevice::platClose()
184 {
185     delete d->writeCompleteNotifier;
186     d->writeCompleteNotifier = NULL;
187     CloseHandle(d->writeOverlapped.hEvent);
188     d->writeOverlapped.hEvent = INVALID_HANDLE_VALUE;
189
190     delete d->commEventNotifier;
191     d->commEventNotifier = NULL;
192     d->commEventOverlapped.hEvent = INVALID_HANDLE_VALUE;
193
194     CloseHandle(d->portHandle);
195     d->portHandle = INVALID_HANDLE_VALUE;
196 }
197
198 VirtualSerialDevice::~VirtualSerialDevice()
199 {
200     close();
201     delete d;
202 }
203
204 qint64 VirtualSerialDevice::bytesAvailable() const
205 {
206     QMutexLocker locker(&lock);
207     if (!isOpen()) return 0;
208
209     qint64 avail = 0;
210     COMSTAT Status;
211     if (ClearCommError(d->portHandle, NULL, &Status)) {
212         avail = Status.cbInQue;
213     }
214     return avail + QIODevice::bytesAvailable();
215 }
216
217 void VirtualSerialDevice::commEventOccurred()
218 {
219     DWORD event = d->commEventMask;
220     if (event & EV_RXCHAR) {
221         emit readyRead();
222     }
223     ResetEvent(d->commEventOverlapped.hEvent);
224     WaitCommEvent(d->portHandle, &d->commEventMask, &d->commEventOverlapped);
225 }
226
227 qint64 VirtualSerialDevice::readData(char *data, qint64 maxSize)
228 {
229     QMutexLocker locker(&lock);
230     // We do our reads synchronously
231     OVERLAPPED readOverlapped;
232     memset(&readOverlapped, 0, sizeof(OVERLAPPED));
233     DWORD bytesRead;
234     BOOL done = ReadFile(d->portHandle, data, maxSize, &bytesRead, &readOverlapped);
235     if (done) return (qint64)bytesRead;
236
237     if (GetLastError() == ERROR_IO_PENDING) {
238         // Note the TRUE to wait for the read to complete
239         done = GetOverlappedResult(d->portHandle, &readOverlapped, &bytesRead, TRUE);
240         if (done) return (qint64)bytesRead;
241     }
242
243     // If we reach here an error has occurred
244     setErrorString(tr("An error occurred while reading from %1: %2").
245                    arg(portName, winErrorMessage(GetLastError())));
246     return -1;
247 }
248
249
250 qint64 VirtualSerialDevice::writeData(const char *data, qint64 maxSize)
251 {
252     QMutexLocker locker(&lock);
253
254     pendingWrites.append(QByteArray(data, maxSize)); // Can't see a way of doing async io safely without having to copy here...
255     if (pendingWrites.count() == 1) {
256         return writeNextBuffer(locker);
257     } else {
258         return maxSize;
259     }
260 }
261
262 qint64 VirtualSerialDevice::writeNextBuffer(QMutexLocker& locker)
263 {
264     Q_UNUSED(locker)
265     // Must be locked on entry
266     qint64 bufLen = pendingWrites[0].length();
267     BOOL ok = WriteFile(d->portHandle, pendingWrites[0].constData(), bufLen, NULL, &d->writeOverlapped);
268     if (ok || GetLastError() == ERROR_IO_PENDING) {
269         // Apparently it can return true for a small asynchronous write...
270         // Hopefully it still gets signalled in the same way!
271
272         // Wait for signal via writeCompleted
273         return bufLen;
274     }
275     else {
276         setErrorString(tr("An error occurred while writing to %1: %2").
277                        arg(portName, winErrorMessage(GetLastError())));
278         pendingWrites.removeFirst();
279         return -1;
280     }
281 }
282
283 void VirtualSerialDevice::writeCompleted()
284 {
285     QMutexLocker locker(&lock);
286     if (pendingWrites.count() == 0) {
287         qWarning("%s: writeCompleted called when there are no pending writes on %s!",
288                  Q_FUNC_INFO, qPrintable(portName));
289         return;
290     }
291
292     doWriteCompleted(locker);
293 }
294
295 void VirtualSerialDevice::doWriteCompleted(QMutexLocker &locker)
296 {
297     // Must be locked on entry
298     ResetEvent(d->writeOverlapped.hEvent);
299
300     qint64 len = pendingWrites.first().length();
301     pendingWrites.removeFirst();
302
303     if (pendingWrites.count() > 0) {
304         // Get the next write started before notifying in case client calls waitForBytesWritten in their slot
305         writeNextBuffer(locker);
306     }
307
308     emitBytesWrittenIfNeeded(locker, len);
309 }
310
311 bool VirtualSerialDevice::waitForBytesWritten(int msecs)
312 {
313     QMutexLocker locker(&lock);
314     if (pendingWrites.count() == 0) return false;
315
316     if (QThread::currentThread() != thread()) {
317         // Wait for signal from main thread
318         unsigned long timeout = msecs;
319         if (msecs == -1) timeout = ULONG_MAX;
320         if (waiterForBytesWritten == NULL)
321             waiterForBytesWritten = new QWaitCondition;
322         return waiterForBytesWritten->wait(&lock, timeout);
323     }
324
325     DWORD waitTime = msecs;
326     if (msecs == -1) waitTime = INFINITE; // Ok these are probably bitwise the same, but just to prove I've thought about it...
327     DWORD result = WaitForSingleObject(d->writeOverlapped.hEvent, waitTime); // Do I need WaitForSingleObjectEx and worry about alertable states?
328     if (result == WAIT_TIMEOUT) {
329         return false;
330     }
331     else if (result == WAIT_OBJECT_0) {
332         DWORD bytesWritten;
333         BOOL ok = GetOverlappedResult(d->portHandle, &d->writeOverlapped, &bytesWritten, TRUE);
334         if (!ok) {
335             setErrorString(tr("An error occurred while syncing on waitForBytesWritten for %1: %2").
336                            arg(portName, winErrorMessage(GetLastError())));
337             return false;
338         }
339         Q_ASSERT(bytesWritten == (DWORD)pendingWrites.first().length());
340
341         doWriteCompleted(locker);
342         return true;
343     }
344     else {
345         setErrorString(QString("An error occured in waitForBytesWritten() for %1: %2").
346                        arg(portName, winErrorMessage(GetLastError())));
347         return false;
348     }
349 }
350
351 void VirtualSerialDevice::flush()
352 {
353     while (waitForBytesWritten(-1)) { /* loop */ }
354 }
355
356 bool VirtualSerialDevice::waitForReadyRead(int msecs)
357 {
358     return QIODevice::waitForReadyRead(msecs); //TODO
359 }
360
361 } // namespace SymbianUtils