VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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