VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/serialport-win.cpp@ 75879

Last change on this file since 75879 was 73720, checked in by vboxsync, 6 years ago

Runtime/serialport-win.cpp: Fixes, can't use CancelIo because it applies to the whole device and not only to the overlapped read (the required CancelIoEx is only available with Vista+). Luckily COMSTAT provides us with the necessary information to avoid CancelIo. Also set proper COM timeouts so we can get true non blocking behavior

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.7 KB
Line 
1/* $Id: serialport-win.cpp 73720 2018-08-16 17:57:44Z vboxsync $ */
2/** @file
3 * IPRT - Serial Port API, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/serialport.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/cdefs.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40#include <iprt/thread.h>
41#include <iprt/time.h>
42#include "internal/magics.h"
43
44#include <iprt/win/windows.h>
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50
51/**
52 * Internal serial port state.
53 */
54typedef struct RTSERIALPORTINTERNAL
55{
56 /** Magic value (RTSERIALPORT_MAGIC). */
57 uint32_t u32Magic;
58 /** Flags given while opening the serial port. */
59 uint32_t fOpenFlags;
60 /** The device handle. */
61 HANDLE hDev;
62 /** The overlapped write structure. */
63 OVERLAPPED OverlappedWrite;
64 /** The overlapped read structure. */
65 OVERLAPPED OverlappedRead;
66 /** The overlapped I/O structure when waiting on events. */
67 OVERLAPPED OverlappedEvt;
68 /** The event handle to wait on for the overlapped event operations of the device. */
69 HANDLE hEvtDev;
70 /** The event handle to wait on for the overlapped write operations of the device. */
71 HANDLE hEvtWrite;
72 /** The event handle to wait on for the overlapped read operations of the device. */
73 HANDLE hEvtRead;
74 /** The event handle to wait on for waking up waiting threads externally. */
75 HANDLE hEvtIntr;
76 /** Events currently waited for. */
77 uint32_t fEvtMask;
78 /** Flag whether a write is currently pending. */
79 bool fWritePending;
80 /** Bounce buffer for writes. */
81 uint8_t *pbBounceBuf;
82 /** Amount of used buffer space. */
83 size_t cbBounceBufUsed;
84 /** Amount of allocated buffer space. */
85 size_t cbBounceBufAlloc;
86 /** The current active port config. */
87 DCB PortCfg;
88} RTSERIALPORTINTERNAL;
89/** Pointer to the internal serial port state. */
90typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
91
92
93
94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
97/** The pipe buffer size we prefer. */
98#define RTSERIALPORT_NT_SIZE _32K
99
100
101
102/*********************************************************************************************************************************
103* Global variables *
104*********************************************************************************************************************************/
105
106
107
108/*********************************************************************************************************************************
109* Internal Functions *
110*********************************************************************************************************************************/
111
112/**
113 * Updatest the current event mask to wait for.
114 *
115 * @returns IPRT status code.
116 * @param pThis The internal serial port instance data.
117 * @param fEvtMask The new event mask to change to.
118 */
119static int rtSerialPortWinUpdateEvtMask(PRTSERIALPORTINTERNAL pThis, uint32_t fEvtMask)
120{
121 DWORD dwEvtMask = EV_ERR;
122
123 if (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
124 dwEvtMask |= EV_RXCHAR;
125 if (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
126 dwEvtMask |= EV_TXEMPTY;
127 if (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
128 dwEvtMask |= EV_BREAK;
129 if (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
130 dwEvtMask |= EV_CTS | EV_DSR | EV_RING | EV_RLSD;
131
132 int rc = VINF_SUCCESS;
133 if (!SetCommMask(pThis->hDev, dwEvtMask))
134 rc = RTErrConvertFromWin32(GetLastError());
135 else
136 pThis->fEvtMask = fEvtMask;
137
138 return rc;
139}
140
141
142/**
143 * Tries to set the default config on the given serial port.
144 *
145 * @returns IPRT status code.
146 * @param pThis The internal serial port instance data.
147 */
148static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
149{
150 if (!PurgeComm(pThis->hDev, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
151 return RTErrConvertFromWin32(GetLastError());
152
153 pThis->PortCfg.DCBlength = sizeof(pThis->PortCfg);
154 if (!GetCommState(pThis->hDev, &pThis->PortCfg))
155 return RTErrConvertFromWin32(GetLastError());
156
157 pThis->PortCfg.BaudRate = CBR_9600;
158 pThis->PortCfg.fBinary = TRUE;
159 pThis->PortCfg.fParity = TRUE;
160 pThis->PortCfg.fDtrControl = DTR_CONTROL_DISABLE;
161 pThis->PortCfg.ByteSize = 8;
162 pThis->PortCfg.Parity = NOPARITY;
163
164 int rc = VINF_SUCCESS;
165 if (!SetCommState(pThis->hDev, &pThis->PortCfg))
166 rc = RTErrConvertFromWin32(GetLastError());
167
168 if (RT_SUCCESS(rc))
169 {
170 /*
171 * Set timeouts for non blocking mode.
172 * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts .
173 */
174 COMMTIMEOUTS ComTimeouts;
175 RT_ZERO(ComTimeouts);
176 ComTimeouts.ReadIntervalTimeout = MAXDWORD;
177 if (!SetCommTimeouts(pThis->hDev, &ComTimeouts))
178 rc = RTErrConvertFromWin32(GetLastError());
179 }
180
181 return rc;
182}
183
184
185/**
186 * Common worker for handling I/O completion.
187 *
188 * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB.
189 *
190 * @returns IPRT status code.
191 * @param pThis The pipe instance handle.
192 */
193static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis)
194{
195 int rc = VINF_SUCCESS;
196 DWORD dwRc = WaitForSingleObject(pThis->OverlappedWrite.hEvent, 0);
197 if (dwRc == WAIT_OBJECT_0)
198 {
199 DWORD cbWritten = 0;
200 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE))
201 {
202 for (;;)
203 {
204 if (cbWritten >= pThis->cbBounceBufUsed)
205 {
206 pThis->fWritePending = false;
207 rc = VINF_SUCCESS;
208 break;
209 }
210
211 /* resubmit the remainder of the buffer - can this actually happen? */
212 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
213 rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
214 if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
215 &cbWritten, &pThis->OverlappedWrite))
216 {
217 if (GetLastError() == ERROR_IO_PENDING)
218 rc = VINF_TRY_AGAIN;
219 else
220 {
221 pThis->fWritePending = false;
222 rc = RTErrConvertFromWin32(GetLastError());
223 }
224 break;
225 }
226 Assert(cbWritten > 0);
227 }
228 }
229 else
230 {
231 pThis->fWritePending = false;
232 rc = RTErrConvertFromWin32(GetLastError());
233 }
234 }
235 else if (dwRc == WAIT_TIMEOUT)
236 rc = VINF_TRY_AGAIN;
237 else
238 {
239 pThis->fWritePending = false;
240 if (dwRc == WAIT_ABANDONED)
241 rc = VERR_INVALID_HANDLE;
242 else
243 rc = RTErrConvertFromWin32(GetLastError());
244 }
245 return rc;
246}
247
248
249RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
250{
251 AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
252 AssertReturn(VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
253 AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
254 AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
255 VERR_INVALID_PARAMETER);
256
257 int rc = VINF_SUCCESS;
258 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
259 if (pThis)
260 {
261 pThis->u32Magic = RTSERIALPORT_MAGIC;
262 pThis->fOpenFlags = fFlags;
263 pThis->fEvtMask = 0;
264 pThis->fWritePending = false;
265 pThis->pbBounceBuf = NULL;
266 pThis->cbBounceBufUsed = 0;
267 pThis->cbBounceBufAlloc = 0;
268 RT_ZERO(pThis->OverlappedEvt);
269 RT_ZERO(pThis->OverlappedWrite);
270 RT_ZERO(pThis->OverlappedRead);
271 pThis->hEvtDev = CreateEvent(NULL, TRUE, FALSE, NULL);
272 if (pThis->hEvtDev)
273 {
274 pThis->OverlappedEvt.hEvent = pThis->hEvtDev,
275 pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL);
276 if (pThis->hEvtIntr)
277 {
278 pThis->hEvtWrite = CreateEvent(NULL, TRUE, TRUE, NULL);
279 if (pThis->hEvtWrite)
280 {
281 pThis->OverlappedWrite.hEvent = pThis->hEvtWrite;
282 pThis->hEvtRead = CreateEvent(NULL, TRUE, TRUE, NULL);
283 if (pThis->hEvtRead)
284 {
285 pThis->OverlappedRead.hEvent = pThis->hEvtRead;
286 DWORD fWinFlags = 0;
287
288 if (fFlags & RTSERIALPORT_OPEN_F_WRITE)
289 fWinFlags |= GENERIC_WRITE;
290 if (fFlags & RTSERIALPORT_OPEN_F_READ)
291 fWinFlags |= GENERIC_READ;
292
293 pThis->hDev = CreateFile(pszPortAddress,
294 fWinFlags,
295 0, /* Must be opened with exclusive access. */
296 NULL, /* No SECURITY_ATTRIBUTES structure. */
297 OPEN_EXISTING, /* Must use OPEN_EXISTING. */
298 FILE_FLAG_OVERLAPPED, /* Overlapped I/O. */
299 NULL);
300 if (pThis->hDev)
301 {
302 rc = rtSerialPortSetDefaultCfg(pThis);
303 if (RT_SUCCESS(rc))
304 {
305 *phSerialPort = pThis;
306 return rc;
307 }
308 }
309 else
310 rc = RTErrConvertFromWin32(GetLastError());
311
312 CloseHandle(pThis->hEvtRead);
313 }
314
315 CloseHandle(pThis->hEvtWrite);
316 }
317
318 CloseHandle(pThis->hEvtIntr);
319 }
320 else
321 rc = RTErrConvertFromWin32(GetLastError());
322
323 CloseHandle(pThis->hEvtDev);
324 }
325 else
326 rc = RTErrConvertFromWin32(GetLastError());
327
328 RTMemFree(pThis);
329 }
330 else
331 rc = VERR_NO_MEMORY;
332
333 return rc;
334}
335
336
337RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
338{
339 PRTSERIALPORTINTERNAL pThis = hSerialPort;
340 if (pThis == NIL_RTSERIALPORT)
341 return VINF_SUCCESS;
342 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
343 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
344
345 /*
346 * Do the cleanup.
347 */
348 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
349
350 if (pThis->fWritePending)
351 rtSerialPortWriteCheckCompletion(pThis);
352
353 CloseHandle(pThis->hDev);
354 CloseHandle(pThis->hEvtDev);
355 CloseHandle(pThis->hEvtWrite);
356 CloseHandle(pThis->hEvtRead);
357 CloseHandle(pThis->hEvtIntr);
358 pThis->hDev = NULL;
359 pThis->hEvtDev = NULL;
360 pThis->hEvtWrite = NULL;
361 pThis->hEvtRead = NULL;
362 pThis->hEvtIntr = NULL;
363 RTMemFree(pThis);
364 return VINF_SUCCESS;
365}
366
367
368RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
369{
370 PRTSERIALPORTINTERNAL pThis = hSerialPort;
371 AssertPtrReturn(pThis, -1);
372 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
373
374 return (RTHCINTPTR)pThis->hDev;
375}
376
377
378RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
379{
380 PRTSERIALPORTINTERNAL pThis = hSerialPort;
381 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
382 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
383 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
384 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
385
386 /*
387 * Kick of an overlapped read.
388 */
389 int rc = VINF_SUCCESS;
390 uint8_t *pbBuf = (uint8_t *)pvBuf;
391
392 while ( cbToRead > 0
393 && RT_SUCCESS(rc))
394 {
395 BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
396 DWORD cbRead = 0;
397 if (ReadFile(pThis->hDev, pbBuf,
398 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
399 &cbRead, &pThis->OverlappedRead))
400 {
401 if (pcbRead)
402 {
403 *pcbRead = cbRead;
404 break;
405 }
406 rc = VINF_SUCCESS;
407 }
408 else if (GetLastError() == ERROR_IO_PENDING)
409 {
410 DWORD dwWait = WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE);
411 if (dwWait == WAIT_OBJECT_0)
412 {
413 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
414 {
415 if (pcbRead)
416 {
417 *pcbRead = cbRead;
418 break;
419 }
420 rc = VINF_SUCCESS;
421 }
422 else
423 rc = RTErrConvertFromWin32(GetLastError());
424 }
425 else
426 {
427 Assert(dwWait == WAIT_FAILED);
428 rc = RTErrConvertFromWin32(GetLastError());
429 }
430 }
431 else
432 rc = RTErrConvertFromWin32(GetLastError());
433
434 if (RT_SUCCESS(rc))
435 {
436 cbToRead -= cbRead;
437 pbBuf += cbRead;
438 }
439 }
440
441 return rc;
442}
443
444
445RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
446{
447 PRTSERIALPORTINTERNAL pThis = hSerialPort;
448 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
449 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
450 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
451 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
452 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
453
454 *pcbRead = 0;
455
456 /* Check whether there is data waiting in the input queue. */
457 int rc = VINF_SUCCESS;
458 COMSTAT ComStat; RT_ZERO(ComStat);
459 if (ClearCommError(pThis->hDev, NULL, &ComStat))
460 {
461 if (ComStat.cbInQue > 0)
462 {
463 DWORD dwToRead = RT_MIN(ComStat.cbInQue, (DWORD)cbToRead);
464 /* Kick of an overlapped read. It should return immediately */
465 BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
466 DWORD cbRead = 0;
467 if ( cbToRead == 0
468 || ReadFile(pThis->hDev, pvBuf, dwToRead,
469 &cbRead, &pThis->OverlappedRead))
470 *pcbRead = cbRead;
471 else if (GetLastError() == ERROR_IO_PENDING)
472 {
473 /* This shouldn't actually happen, so turn this into a synchronous read. */
474 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
475 *pcbRead = cbRead;
476 else
477 rc = RTErrConvertFromWin32(GetLastError());
478 }
479 else
480 rc = RTErrConvertFromWin32(GetLastError());
481 }
482 else
483 rc = VINF_TRY_AGAIN;
484 }
485 else
486 rc = RTErrConvertFromWin32(GetLastError());
487
488 return rc;
489}
490
491
492RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
493{
494 PRTSERIALPORTINTERNAL pThis = hSerialPort;
495 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
496 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
497 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
498 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
499
500 /* If I/O is pending, check if it has completed. */
501 int rc = VINF_SUCCESS;
502 if (pThis->fWritePending)
503 rc = rtSerialPortWriteCheckCompletion(pThis);
504 if (rc == VINF_SUCCESS)
505 {
506 const uint8_t *pbBuf = (const uint8_t *)pvBuf;
507
508 while ( cbToWrite > 0
509 && RT_SUCCESS(rc))
510 {
511 BOOL fSucc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
512 DWORD cbWritten = 0;
513 if (WriteFile(pThis->hDev, pbBuf,
514 cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0,
515 &cbWritten, &pThis->OverlappedWrite))
516 {
517 if (pcbWritten)
518 {
519 *pcbWritten = cbWritten;
520 break;
521 }
522 rc = VINF_SUCCESS;
523 }
524 else if (GetLastError() == ERROR_IO_PENDING)
525 {
526 DWORD dwWait = WaitForSingleObject(pThis->OverlappedWrite.hEvent, INFINITE);
527 if (dwWait == WAIT_OBJECT_0)
528 {
529 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE /*fWait*/))
530 {
531 if (pcbWritten)
532 {
533 *pcbWritten = cbWritten;
534 break;
535 }
536 rc = VINF_SUCCESS;
537 }
538 else
539 rc = RTErrConvertFromWin32(GetLastError());
540 }
541 else
542 {
543 Assert(dwWait == WAIT_FAILED);
544 rc = RTErrConvertFromWin32(GetLastError());
545 }
546 }
547 else
548 rc = RTErrConvertFromWin32(GetLastError());
549
550 if (RT_SUCCESS(rc))
551 {
552 cbToWrite -= cbWritten;
553 pbBuf += cbWritten;
554 }
555 }
556 }
557
558 return rc;
559}
560
561
562RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
563{
564 PRTSERIALPORTINTERNAL pThis = hSerialPort;
565 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
566 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
567 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
568 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
569 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
570
571 /* If I/O is pending, check if it has completed. */
572 int rc = VINF_SUCCESS;
573 if (pThis->fWritePending)
574 rc = rtSerialPortWriteCheckCompletion(pThis);
575 if (rc == VINF_SUCCESS)
576 {
577 Assert(!pThis->fWritePending);
578
579 /* Do the bounce buffering. */
580 if ( pThis->cbBounceBufAlloc < cbToWrite
581 && pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE)
582 {
583 if (cbToWrite > RTSERIALPORT_NT_SIZE)
584 cbToWrite = RTSERIALPORT_NT_SIZE;
585 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
586 if (pv)
587 {
588 pThis->pbBounceBuf = (uint8_t *)pv;
589 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
590 }
591 else
592 rc = VERR_NO_MEMORY;
593 }
594 else if (cbToWrite > RTSERIALPORT_NT_SIZE)
595 cbToWrite = RTSERIALPORT_NT_SIZE;
596 if (RT_SUCCESS(rc) && cbToWrite)
597 {
598 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
599 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
600
601 /* Submit the write. */
602 rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
603 DWORD cbWritten = 0;
604 if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
605 &cbWritten, &pThis->OverlappedWrite))
606 {
607 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
608 rc = VINF_SUCCESS;
609 }
610 else if (GetLastError() == ERROR_IO_PENDING)
611 {
612 *pcbWritten = cbToWrite;
613 pThis->fWritePending = true;
614 rc = VINF_SUCCESS;
615 }
616 else
617 rc = RTErrConvertFromWin32(GetLastError());
618 }
619 else if (RT_SUCCESS(rc))
620 *pcbWritten = 0;
621 }
622 else if (RT_SUCCESS(rc))
623 *pcbWritten = 0;
624
625 return rc;
626}
627
628
629RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
630{
631 PRTSERIALPORTINTERNAL pThis = hSerialPort;
632 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
633 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
634
635 pCfg->uBaudRate = pThis->PortCfg.BaudRate;
636 switch (pThis->PortCfg.Parity)
637 {
638 case NOPARITY:
639 pCfg->enmParity = RTSERIALPORTPARITY_NONE;
640 break;
641 case EVENPARITY:
642 pCfg->enmParity = RTSERIALPORTPARITY_EVEN;
643 break;
644 case ODDPARITY:
645 pCfg->enmParity = RTSERIALPORTPARITY_ODD;
646 break;
647 case MARKPARITY:
648 pCfg->enmParity = RTSERIALPORTPARITY_MARK;
649 break;
650 case SPACEPARITY:
651 pCfg->enmParity = RTSERIALPORTPARITY_SPACE;
652 break;
653 default:
654 AssertFailed();
655 return VERR_INTERNAL_ERROR;
656 }
657
658 switch (pThis->PortCfg.ByteSize)
659 {
660 case 5:
661 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
662 break;
663 case 6:
664 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
665 break;
666 case 7:
667 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
668 break;
669 case 8:
670 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
671 break;
672 default:
673 AssertFailed();
674 return VERR_INTERNAL_ERROR;
675 }
676
677 switch (pThis->PortCfg.StopBits)
678 {
679 case ONESTOPBIT:
680 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
681 break;
682 case ONE5STOPBITS:
683 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
684 break;
685 case TWOSTOPBITS:
686 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
687 break;
688 default:
689 AssertFailed();
690 return VERR_INTERNAL_ERROR;
691 }
692
693 return VINF_SUCCESS;
694}
695
696
697RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
698{
699 PRTSERIALPORTINTERNAL pThis = hSerialPort;
700 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
701 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
702
703 RT_NOREF(pErrInfo);
704
705 DCB DcbNew;
706 memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew));
707 DcbNew.BaudRate = pCfg->uBaudRate;
708
709 switch (pCfg->enmParity)
710 {
711 case RTSERIALPORTPARITY_NONE:
712 DcbNew.Parity = NOPARITY;
713 break;
714 case RTSERIALPORTPARITY_EVEN:
715 DcbNew.Parity = EVENPARITY;
716 break;
717 case RTSERIALPORTPARITY_ODD:
718 DcbNew.Parity = ODDPARITY;
719 break;
720 case RTSERIALPORTPARITY_MARK:
721 DcbNew.Parity = MARKPARITY;
722 break;
723 case RTSERIALPORTPARITY_SPACE:
724 DcbNew.Parity = SPACEPARITY;
725 break;
726 default:
727 AssertFailedReturn(VERR_INVALID_PARAMETER);
728 }
729
730 switch (pCfg->enmDataBitCount)
731 {
732 case RTSERIALPORTDATABITS_5BITS:
733 DcbNew.ByteSize = 5;
734 break;
735 case RTSERIALPORTDATABITS_6BITS:
736 DcbNew.ByteSize = 6;
737 break;
738 case RTSERIALPORTDATABITS_7BITS:
739 DcbNew.ByteSize = 7;
740 break;
741 case RTSERIALPORTDATABITS_8BITS:
742 DcbNew.ByteSize = 8;
743 break;
744 default:
745 AssertFailedReturn(VERR_INVALID_PARAMETER);
746 }
747
748 switch (pCfg->enmStopBitCount)
749 {
750 case RTSERIALPORTSTOPBITS_ONE:
751 DcbNew.StopBits = ONESTOPBIT;
752 break;
753 case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
754 AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
755 DcbNew.StopBits = ONE5STOPBITS;
756 break;
757 case RTSERIALPORTSTOPBITS_TWO:
758 AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
759 DcbNew.StopBits = TWOSTOPBITS;
760 break;
761 default:
762 AssertFailedReturn(VERR_INVALID_PARAMETER);
763 }
764
765 int rc = VINF_SUCCESS;
766 if (!SetCommState(pThis->hDev, &DcbNew))
767 rc = RTErrConvertFromWin32(GetLastError());
768 else
769 memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew));
770
771 return rc;
772}
773
774
775RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
776 RTMSINTERVAL msTimeout)
777{
778 PRTSERIALPORTINTERNAL pThis = hSerialPort;
779 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
780 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
781 AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
782 AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
783
784 *pfEvtsRecv = 0;
785
786 int rc = VINF_SUCCESS;
787 if (fEvtMask != pThis->fEvtMask)
788 rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask);
789
790 /*
791 * EV_RXCHAR is triggered only if a byte is received after the event mask is set,
792 * not if there is already something in the input buffer. Thatswhy we check the input
793 * buffer for any stored data and the output buffer whether it is empty and return
794 * the appropriate flags.
795 */
796 if (RT_SUCCESS(rc))
797 {
798 COMSTAT ComStat; RT_ZERO(ComStat);
799 if (!ClearCommError(pThis->hDev, NULL, &ComStat))
800 return RTErrConvertFromWin32(GetLastError());
801
802 /* Check whether data is already waiting in the input buffer. */
803 if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
804 && ComStat.cbInQue > 0)
805 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
806
807 /* Check whether the output buffer is empty. */
808 if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
809 && ComStat.cbOutQue == 0)
810 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
811
812 /* Return if there is at least one event. */
813 if (*pfEvtsRecv != 0)
814 return VINF_SUCCESS;
815 }
816
817 if (RT_SUCCESS(rc))
818 {
819 DWORD dwEventMask = 0;
820 HANDLE ahWait[2];
821 ahWait[0] = pThis->hEvtDev;
822 ahWait[1] = pThis->hEvtIntr;
823
824 RT_ZERO(pThis->OverlappedEvt);
825 pThis->OverlappedEvt.hEvent = pThis->hEvtDev;
826
827 if (!WaitCommEvent(pThis->hDev, &dwEventMask, &pThis->OverlappedEvt))
828 {
829 DWORD dwRet = GetLastError();
830 if (dwRet == ERROR_IO_PENDING)
831 {
832 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout);
833 if (dwRet == WAIT_TIMEOUT)
834 rc = VERR_TIMEOUT;
835 else if (dwRet == WAIT_FAILED)
836 rc = RTErrConvertFromWin32(GetLastError());
837 else if (dwRet != WAIT_OBJECT_0)
838 rc = VERR_INTERRUPTED;
839 }
840 else
841 rc = RTErrConvertFromWin32(dwRet);
842 }
843
844 if (RT_SUCCESS(rc))
845 {
846 /* Check the event */
847 if (dwEventMask & EV_RXCHAR)
848 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
849 if (dwEventMask & EV_TXEMPTY)
850 {
851 if (pThis->fWritePending)
852 {
853 rc = rtSerialPortWriteCheckCompletion(pThis);
854 if (rc == VINF_SUCCESS)
855 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
856 else
857 rc = VINF_SUCCESS;
858 }
859 else
860 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
861 }
862 if (dwEventMask & EV_BREAK)
863 *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
864 if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD))
865 *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
866 }
867 }
868
869 return rc;
870}
871
872
873RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
874{
875 PRTSERIALPORTINTERNAL pThis = hSerialPort;
876 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
877 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
878
879 if (!SetEvent(pThis->hEvtIntr))
880 return RTErrConvertFromWin32(GetLastError());
881
882 return VINF_SUCCESS;
883}
884
885
886RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
887{
888 PRTSERIALPORTINTERNAL pThis = hSerialPort;
889 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
890 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
891
892 BOOL fSucc = FALSE;
893 if (fSet)
894 fSucc = SetCommBreak(pThis->hDev);
895 else
896 fSucc = ClearCommBreak(pThis->hDev);
897
898 int rc = VINF_SUCCESS;
899 if (!fSucc)
900 rc = RTErrConvertFromWin32(GetLastError());
901
902 return rc;
903}
904
905
906RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
907{
908 PRTSERIALPORTINTERNAL pThis = hSerialPort;
909 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
910 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
911
912 BOOL fSucc = TRUE;
913 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
914 fSucc = EscapeCommFunction(pThis->hDev, SETDTR);
915 if ( fSucc
916 && (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
917 fSucc = EscapeCommFunction(pThis->hDev, SETRTS);
918
919 if ( fSucc
920 && (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
921 fSucc = EscapeCommFunction(pThis->hDev, CLRDTR);
922 if ( fSucc
923 && (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
924 fSucc = EscapeCommFunction(pThis->hDev, CLRRTS);
925
926 int rc = VINF_SUCCESS;
927 if (!fSucc)
928 rc = RTErrConvertFromWin32(GetLastError());
929
930 return rc;
931}
932
933
934RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
935{
936 PRTSERIALPORTINTERNAL pThis = hSerialPort;
937 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
938 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
939 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
940
941 *pfStsLines = 0;
942
943 int rc = VINF_SUCCESS;
944 DWORD fStsLinesQueried = 0;
945
946 /* Get the new state */
947 if (GetCommModemStatus(pThis->hDev, &fStsLinesQueried))
948 {
949 *pfStsLines |= (fStsLinesQueried & MS_RLSD_ON) ? RTSERIALPORT_STS_LINE_DCD : 0;
950 *pfStsLines |= (fStsLinesQueried & MS_RING_ON) ? RTSERIALPORT_STS_LINE_RI : 0;
951 *pfStsLines |= (fStsLinesQueried & MS_DSR_ON) ? RTSERIALPORT_STS_LINE_DSR : 0;
952 *pfStsLines |= (fStsLinesQueried & MS_CTS_ON) ? RTSERIALPORT_STS_LINE_CTS : 0;
953 }
954 else
955 rc = RTErrConvertFromWin32(GetLastError());
956
957 return rc;
958}
959
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette