VirtualBox

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

Last change on this file since 81106 was 77323, checked in by vboxsync, 6 years ago

Runtime/serialport-win.cpp: Don't keep dwEventMask on the stack for overlapped I/O as this may get written to after the method returned corrupting stack memory. Fixes crashes on Windows

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