VirtualBox

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

Last change on this file since 103795 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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