VirtualBox

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

Last change on this file since 73635 was 73635, checked in by vboxsync, 7 years ago

Runtime/serialport-win.cpp: Fixes for I/O

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.1 KB
Line 
1/* $Id: serialport-win.cpp 73635 2018-08-13 13:01:07Z 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 return rc;
169}
170
171
172/**
173 * Common worker for handling I/O completion.
174 *
175 * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB.
176 *
177 * @returns IPRT status code.
178 * @param pThis The pipe instance handle.
179 */
180static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis)
181{
182 int rc = VINF_SUCCESS;
183 DWORD dwRc = WaitForSingleObject(pThis->OverlappedWrite.hEvent, 0);
184 if (dwRc == WAIT_OBJECT_0)
185 {
186 DWORD cbWritten = 0;
187 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE))
188 {
189 for (;;)
190 {
191 if (cbWritten >= pThis->cbBounceBufUsed)
192 {
193 pThis->fWritePending = false;
194 rc = VINF_SUCCESS;
195 break;
196 }
197
198 /* resubmit the remainder of the buffer - can this actually happen? */
199 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
200 rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
201 if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
202 &cbWritten, &pThis->OverlappedWrite))
203 {
204 if (GetLastError() == ERROR_IO_PENDING)
205 rc = VINF_TRY_AGAIN;
206 else
207 {
208 pThis->fWritePending = false;
209 rc = RTErrConvertFromWin32(GetLastError());
210 }
211 break;
212 }
213 Assert(cbWritten > 0);
214 }
215 }
216 else
217 {
218 pThis->fWritePending = false;
219 rc = RTErrConvertFromWin32(GetLastError());
220 }
221 }
222 else if (dwRc == WAIT_TIMEOUT)
223 rc = VINF_TRY_AGAIN;
224 else
225 {
226 pThis->fWritePending = false;
227 if (dwRc == WAIT_ABANDONED)
228 rc = VERR_INVALID_HANDLE;
229 else
230 rc = RTErrConvertFromWin32(GetLastError());
231 }
232 return rc;
233}
234
235
236RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
237{
238 AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
239 AssertReturn(VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
240 AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
241 AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
242 VERR_INVALID_PARAMETER);
243
244 int rc = VINF_SUCCESS;
245 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
246 if (pThis)
247 {
248 pThis->u32Magic = RTSERIALPORT_MAGIC;
249 pThis->fOpenFlags = fFlags;
250 pThis->fEvtMask = 0;
251 pThis->fWritePending = false;
252 pThis->pbBounceBuf = NULL;
253 pThis->cbBounceBufUsed = 0;
254 pThis->cbBounceBufAlloc = 0;
255 RT_ZERO(pThis->OverlappedEvt);
256 RT_ZERO(pThis->OverlappedWrite);
257 RT_ZERO(pThis->OverlappedRead);
258 pThis->hEvtDev = CreateEvent(NULL, TRUE, FALSE, NULL);
259 if (pThis->hEvtDev)
260 {
261 pThis->OverlappedEvt.hEvent = pThis->hEvtDev,
262 pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL);
263 if (pThis->hEvtIntr)
264 {
265 pThis->hEvtWrite = CreateEvent(NULL, TRUE, TRUE, NULL);
266 if (pThis->hEvtWrite)
267 {
268 pThis->OverlappedWrite.hEvent = pThis->hEvtWrite;
269 pThis->hEvtRead = CreateEvent(NULL, TRUE, TRUE, NULL);
270 if (pThis->hEvtRead)
271 {
272 pThis->OverlappedRead.hEvent = pThis->hEvtRead;
273 DWORD fWinFlags = 0;
274
275 if (fFlags & RTSERIALPORT_OPEN_F_WRITE)
276 fWinFlags |= GENERIC_WRITE;
277 if (fFlags & RTSERIALPORT_OPEN_F_READ)
278 fWinFlags |= GENERIC_READ;
279
280 pThis->hDev = CreateFile(pszPortAddress,
281 fWinFlags,
282 0, /* Must be opened with exclusive access. */
283 NULL, /* No SECURITY_ATTRIBUTES structure. */
284 OPEN_EXISTING, /* Must use OPEN_EXISTING. */
285 FILE_FLAG_OVERLAPPED, /* Overlapped I/O. */
286 NULL);
287 if (pThis->hDev)
288 {
289 rc = rtSerialPortSetDefaultCfg(pThis);
290 if (RT_SUCCESS(rc))
291 {
292 *phSerialPort = pThis;
293 return rc;
294 }
295 }
296 else
297 rc = RTErrConvertFromWin32(GetLastError());
298
299 CloseHandle(pThis->hEvtRead);
300 }
301
302 CloseHandle(pThis->hEvtWrite);
303 }
304
305 CloseHandle(pThis->hEvtIntr);
306 }
307 else
308 rc = RTErrConvertFromWin32(GetLastError());
309
310 CloseHandle(pThis->hEvtDev);
311 }
312 else
313 rc = RTErrConvertFromWin32(GetLastError());
314
315 RTMemFree(pThis);
316 }
317 else
318 rc = VERR_NO_MEMORY;
319
320 return rc;
321}
322
323
324RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
325{
326 PRTSERIALPORTINTERNAL pThis = hSerialPort;
327 if (pThis == NIL_RTSERIALPORT)
328 return VINF_SUCCESS;
329 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
330 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
331
332 /*
333 * Do the cleanup.
334 */
335 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
336
337 if (pThis->fWritePending)
338 rtSerialPortWriteCheckCompletion(pThis);
339
340 CloseHandle(pThis->hDev);
341 CloseHandle(pThis->hEvtDev);
342 CloseHandle(pThis->hEvtWrite);
343 CloseHandle(pThis->hEvtRead);
344 CloseHandle(pThis->hEvtIntr);
345 pThis->hDev = NULL;
346 pThis->hEvtDev = NULL;
347 pThis->hEvtWrite = NULL;
348 pThis->hEvtRead = NULL;
349 pThis->hEvtIntr = NULL;
350 RTMemFree(pThis);
351 return VINF_SUCCESS;
352}
353
354
355RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
356{
357 PRTSERIALPORTINTERNAL pThis = hSerialPort;
358 AssertPtrReturn(pThis, -1);
359 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
360
361 return (RTHCINTPTR)pThis->hDev;
362}
363
364
365RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
366{
367 PRTSERIALPORTINTERNAL pThis = hSerialPort;
368 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
369 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
370 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
371 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
372
373 /*
374 * Kick of an overlapped read.
375 */
376 int rc = VINF_SUCCESS;
377 uint8_t *pbBuf = (uint8_t *)pvBuf;
378
379 while ( cbToRead > 0
380 && RT_SUCCESS(rc))
381 {
382 BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
383 DWORD cbRead = 0;
384 if (ReadFile(pThis->hDev, pbBuf,
385 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
386 &cbRead, &pThis->OverlappedRead))
387 {
388 if (pcbRead)
389 {
390 *pcbRead = cbRead;
391 break;
392 }
393 rc = VINF_SUCCESS;
394 }
395 else if (GetLastError() == ERROR_IO_PENDING)
396 {
397 DWORD dwWait = WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE);
398 if (dwWait == WAIT_OBJECT_0)
399 {
400 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
401 {
402 if (pcbRead)
403 {
404 *pcbRead = cbRead;
405 break;
406 }
407 rc = VINF_SUCCESS;
408 }
409 else
410 rc = RTErrConvertFromWin32(GetLastError());
411 }
412 else
413 {
414 Assert(dwWait == WAIT_FAILED);
415 rc = RTErrConvertFromWin32(GetLastError());
416 }
417 }
418 else
419 rc = RTErrConvertFromWin32(GetLastError());
420
421 if (RT_SUCCESS(rc))
422 {
423 cbToRead -= cbRead;
424 pbBuf += cbRead;
425 }
426 }
427
428 return rc;
429}
430
431
432RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
433{
434 PRTSERIALPORTINTERNAL pThis = hSerialPort;
435 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
436 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
437 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
438 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
439 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
440
441 *pcbRead = 0;
442
443 /*
444 * Kick of an overlapped read. It should return immediately if
445 * there is bytes in the buffer. If not, we'll cancel it and see
446 * what we get back.
447 */
448 int rc = VINF_SUCCESS;
449 BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
450 DWORD cbRead = 0;
451 if ( cbToRead == 0
452 || ReadFile(pThis->hDev, pvBuf,
453 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
454 &cbRead, &pThis->OverlappedRead))
455 {
456 *pcbRead = cbRead;
457 rc = VINF_SUCCESS;
458 }
459 else if (GetLastError() == ERROR_IO_PENDING)
460 {
461 if (!CancelIo(pThis->hDev))
462 WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE);
463 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
464 {
465 *pcbRead = cbRead;
466 rc = VINF_SUCCESS;
467 }
468 else if (GetLastError() == ERROR_OPERATION_ABORTED)
469 {
470 *pcbRead = 0;
471 rc = VINF_TRY_AGAIN;
472 }
473 else
474 rc = RTErrConvertFromWin32(GetLastError());
475 }
476 else
477 rc = RTErrConvertFromWin32(GetLastError());
478
479 return rc;
480}
481
482
483RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
484{
485 PRTSERIALPORTINTERNAL pThis = hSerialPort;
486 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
487 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
488 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
489 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
490
491 /* If I/O is pending, check if it has completed. */
492 int rc = VINF_SUCCESS;
493 if (pThis->fWritePending)
494 rc = rtSerialPortWriteCheckCompletion(pThis);
495 if (rc == VINF_SUCCESS)
496 {
497 const uint8_t *pbBuf = (const uint8_t *)pvBuf;
498
499 while ( cbToWrite > 0
500 && RT_SUCCESS(rc))
501 {
502 BOOL fSucc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
503 DWORD cbWritten = 0;
504 if (WriteFile(pThis->hDev, pbBuf,
505 cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0,
506 &cbWritten, &pThis->OverlappedWrite))
507 {
508 if (pcbWritten)
509 {
510 *pcbWritten = cbWritten;
511 break;
512 }
513 rc = VINF_SUCCESS;
514 }
515 else if (GetLastError() == ERROR_IO_PENDING)
516 {
517 DWORD dwWait = WaitForSingleObject(pThis->OverlappedWrite.hEvent, INFINITE);
518 if (dwWait == WAIT_OBJECT_0)
519 {
520 if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE /*fWait*/))
521 {
522 if (pcbWritten)
523 {
524 *pcbWritten = cbWritten;
525 break;
526 }
527 rc = VINF_SUCCESS;
528 }
529 else
530 rc = RTErrConvertFromWin32(GetLastError());
531 }
532 else
533 {
534 Assert(dwWait == WAIT_FAILED);
535 rc = RTErrConvertFromWin32(GetLastError());
536 }
537 }
538 else
539 rc = RTErrConvertFromWin32(GetLastError());
540
541 if (RT_SUCCESS(rc))
542 {
543 cbToWrite -= cbWritten;
544 pbBuf += cbWritten;
545 }
546 }
547 }
548
549 return rc;
550}
551
552
553RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
554{
555 PRTSERIALPORTINTERNAL pThis = hSerialPort;
556 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
557 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
558 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
559 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
560 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
561
562 /* If I/O is pending, check if it has completed. */
563 int rc = VINF_SUCCESS;
564 if (pThis->fWritePending)
565 rc = rtSerialPortWriteCheckCompletion(pThis);
566 if (rc == VINF_SUCCESS)
567 {
568 Assert(!pThis->fWritePending);
569
570 /* Do the bounce buffering. */
571 if ( pThis->cbBounceBufAlloc < cbToWrite
572 && pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE)
573 {
574 if (cbToWrite > RTSERIALPORT_NT_SIZE)
575 cbToWrite = RTSERIALPORT_NT_SIZE;
576 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
577 if (pv)
578 {
579 pThis->pbBounceBuf = (uint8_t *)pv;
580 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
581 }
582 else
583 rc = VERR_NO_MEMORY;
584 }
585 else if (cbToWrite > RTSERIALPORT_NT_SIZE)
586 cbToWrite = RTSERIALPORT_NT_SIZE;
587 if (RT_SUCCESS(rc) && cbToWrite)
588 {
589 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
590 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
591
592 /* Submit the write. */
593 rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
594 DWORD cbWritten = 0;
595 if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
596 &cbWritten, &pThis->OverlappedWrite))
597 {
598 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
599 rc = VINF_SUCCESS;
600 }
601 else if (GetLastError() == ERROR_IO_PENDING)
602 {
603 *pcbWritten = cbToWrite;
604 pThis->fWritePending = true;
605 rc = VINF_SUCCESS;
606 }
607 else
608 rc = RTErrConvertFromWin32(GetLastError());
609 }
610 else if (RT_SUCCESS(rc))
611 *pcbWritten = 0;
612 }
613 else if (RT_SUCCESS(rc))
614 *pcbWritten = 0;
615
616 return rc;
617}
618
619
620RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
621{
622 PRTSERIALPORTINTERNAL pThis = hSerialPort;
623 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
624 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
625
626 pCfg->uBaudRate = pThis->PortCfg.BaudRate;
627 switch (pThis->PortCfg.Parity)
628 {
629 case NOPARITY:
630 pCfg->enmParity = RTSERIALPORTPARITY_NONE;
631 break;
632 case EVENPARITY:
633 pCfg->enmParity = RTSERIALPORTPARITY_EVEN;
634 break;
635 case ODDPARITY:
636 pCfg->enmParity = RTSERIALPORTPARITY_ODD;
637 break;
638 case MARKPARITY:
639 pCfg->enmParity = RTSERIALPORTPARITY_MARK;
640 break;
641 case SPACEPARITY:
642 pCfg->enmParity = RTSERIALPORTPARITY_SPACE;
643 break;
644 default:
645 AssertFailed();
646 return VERR_INTERNAL_ERROR;
647 }
648
649 switch (pThis->PortCfg.ByteSize)
650 {
651 case 5:
652 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
653 break;
654 case 6:
655 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
656 break;
657 case 7:
658 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
659 break;
660 case 8:
661 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
662 break;
663 default:
664 AssertFailed();
665 return VERR_INTERNAL_ERROR;
666 }
667
668 switch (pThis->PortCfg.StopBits)
669 {
670 case ONESTOPBIT:
671 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
672 break;
673 case ONE5STOPBITS:
674 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
675 break;
676 case TWOSTOPBITS:
677 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
678 break;
679 default:
680 AssertFailed();
681 return VERR_INTERNAL_ERROR;
682 }
683
684 return VINF_SUCCESS;
685}
686
687
688RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
689{
690 PRTSERIALPORTINTERNAL pThis = hSerialPort;
691 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
692 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
693
694 RT_NOREF(pErrInfo);
695
696 DCB DcbNew;
697 memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew));
698 DcbNew.BaudRate = pCfg->uBaudRate;
699
700 switch (pCfg->enmParity)
701 {
702 case RTSERIALPORTPARITY_NONE:
703 DcbNew.Parity = NOPARITY;
704 break;
705 case RTSERIALPORTPARITY_EVEN:
706 DcbNew.Parity = EVENPARITY;
707 break;
708 case RTSERIALPORTPARITY_ODD:
709 DcbNew.Parity = ODDPARITY;
710 break;
711 case RTSERIALPORTPARITY_MARK:
712 DcbNew.Parity = MARKPARITY;
713 break;
714 case RTSERIALPORTPARITY_SPACE:
715 DcbNew.Parity = SPACEPARITY;
716 break;
717 default:
718 AssertFailedReturn(VERR_INVALID_PARAMETER);
719 }
720
721 switch (pCfg->enmDataBitCount)
722 {
723 case RTSERIALPORTDATABITS_5BITS:
724 DcbNew.ByteSize = 5;
725 break;
726 case RTSERIALPORTDATABITS_6BITS:
727 DcbNew.ByteSize = 6;
728 break;
729 case RTSERIALPORTDATABITS_7BITS:
730 DcbNew.ByteSize = 7;
731 break;
732 case RTSERIALPORTDATABITS_8BITS:
733 DcbNew.ByteSize = 8;
734 break;
735 default:
736 AssertFailedReturn(VERR_INVALID_PARAMETER);
737 }
738
739 switch (pCfg->enmStopBitCount)
740 {
741 case RTSERIALPORTSTOPBITS_ONE:
742 DcbNew.StopBits = ONESTOPBIT;
743 break;
744 case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
745 AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
746 DcbNew.StopBits = ONE5STOPBITS;
747 break;
748 case RTSERIALPORTSTOPBITS_TWO:
749 AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
750 DcbNew.StopBits = TWOSTOPBITS;
751 break;
752 default:
753 AssertFailedReturn(VERR_INVALID_PARAMETER);
754 }
755
756 int rc = VINF_SUCCESS;
757 if (!SetCommState(pThis->hDev, &DcbNew))
758 rc = RTErrConvertFromWin32(GetLastError());
759 else
760 memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew));
761
762 return rc;
763}
764
765
766RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
767 RTMSINTERVAL msTimeout)
768{
769 PRTSERIALPORTINTERNAL pThis = hSerialPort;
770 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
771 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
772 AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
773 AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
774
775 *pfEvtsRecv = 0;
776
777 int rc = VINF_SUCCESS;
778 if (fEvtMask != pThis->fEvtMask)
779 rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask);
780
781 /*
782 * EV_RXCHAR is triggered only if a byte is received after the event mask is set,
783 * not if there is already something in the input buffer. Thatswhy we check the input
784 * buffer for any stored data and the output buffer whether it is empty and return
785 * the appropriate flags.
786 */
787 if (RT_SUCCESS(rc))
788 {
789 COMSTAT ComStat; RT_ZERO(ComStat);
790 if (!ClearCommError(pThis->hDev, NULL, &ComStat))
791 return RTErrConvertFromWin32(GetLastError());
792
793 /* Check whether data is already waiting in the input buffer. */
794 if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
795 && ComStat.cbInQue > 0)
796 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
797
798 /* Check whether the output buffer is empty. */
799 if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
800 && ComStat.cbOutQue == 0)
801 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
802
803 /* Return if there is at least one event. */
804 if (*pfEvtsRecv != 0)
805 return VINF_SUCCESS;
806 }
807
808 if (RT_SUCCESS(rc))
809 {
810 DWORD dwEventMask = 0;
811 HANDLE ahWait[2];
812 ahWait[0] = pThis->hEvtDev;
813 ahWait[1] = pThis->hEvtIntr;
814
815 RT_ZERO(pThis->OverlappedEvt);
816 pThis->OverlappedEvt.hEvent = pThis->hEvtDev;
817
818 if (!WaitCommEvent(pThis->hDev, &dwEventMask, &pThis->OverlappedEvt))
819 {
820 DWORD dwRet = GetLastError();
821 if (dwRet == ERROR_IO_PENDING)
822 {
823 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout);
824 if (dwRet == WAIT_TIMEOUT)
825 rc = VERR_TIMEOUT;
826 else if (dwRet == WAIT_FAILED)
827 rc = RTErrConvertFromWin32(GetLastError());
828 else if (dwRet != WAIT_OBJECT_0)
829 rc = VERR_INTERRUPTED;
830 }
831 else
832 rc = RTErrConvertFromWin32(dwRet);
833 }
834
835 if (RT_SUCCESS(rc))
836 {
837 /* Check the event */
838 if (dwEventMask & EV_RXCHAR)
839 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
840 if (dwEventMask & EV_TXEMPTY)
841 {
842 if (pThis->fWritePending)
843 {
844 rc = rtSerialPortWriteCheckCompletion(pThis);
845 if (rc == VINF_SUCCESS)
846 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
847 else
848 rc = VINF_SUCCESS;
849 }
850 else
851 *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
852 }
853 if (dwEventMask & EV_BREAK)
854 *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
855 if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD))
856 *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
857 }
858 }
859
860 return rc;
861}
862
863
864RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
865{
866 PRTSERIALPORTINTERNAL pThis = hSerialPort;
867 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
868 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
869
870 if (!SetEvent(pThis->hEvtIntr))
871 return RTErrConvertFromWin32(GetLastError());
872
873 return VINF_SUCCESS;
874}
875
876
877RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
878{
879 PRTSERIALPORTINTERNAL pThis = hSerialPort;
880 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
881 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
882
883 BOOL fSucc = FALSE;
884 if (fSet)
885 fSucc = SetCommBreak(pThis->hDev);
886 else
887 fSucc = ClearCommBreak(pThis->hDev);
888
889 int rc = VINF_SUCCESS;
890 if (!fSucc)
891 rc = RTErrConvertFromWin32(GetLastError());
892
893 return rc;
894}
895
896
897RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
898{
899 PRTSERIALPORTINTERNAL pThis = hSerialPort;
900 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
901 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
902
903 BOOL fSucc = TRUE;
904 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
905 fSucc = EscapeCommFunction(pThis->hDev, SETDTR);
906 if ( fSucc
907 && (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
908 fSucc = EscapeCommFunction(pThis->hDev, SETRTS);
909
910 if ( fSucc
911 && (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
912 fSucc = EscapeCommFunction(pThis->hDev, CLRDTR);
913 if ( fSucc
914 && (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
915 fSucc = EscapeCommFunction(pThis->hDev, CLRRTS);
916
917 int rc = VINF_SUCCESS;
918 if (!fSucc)
919 rc = RTErrConvertFromWin32(GetLastError());
920
921 return rc;
922}
923
924
925RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
926{
927 PRTSERIALPORTINTERNAL pThis = hSerialPort;
928 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
929 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
930 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
931
932 *pfStsLines = 0;
933
934 int rc = VINF_SUCCESS;
935 DWORD fStsLinesQueried = 0;
936
937 /* Get the new state */
938 if (GetCommModemStatus(pThis->hDev, &fStsLinesQueried))
939 {
940 *pfStsLines |= (fStsLinesQueried & MS_RLSD_ON) ? RTSERIALPORT_STS_LINE_DCD : 0;
941 *pfStsLines |= (fStsLinesQueried & MS_RING_ON) ? RTSERIALPORT_STS_LINE_RI : 0;
942 *pfStsLines |= (fStsLinesQueried & MS_DSR_ON) ? RTSERIALPORT_STS_LINE_DSR : 0;
943 *pfStsLines |= (fStsLinesQueried & MS_CTS_ON) ? RTSERIALPORT_STS_LINE_CTS : 0;
944 }
945 else
946 rc = RTErrConvertFromWin32(GetLastError());
947
948 return rc;
949}
950
Note: See TracBrowser for help on using the repository browser.

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