OSDN Git Service

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