VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvNamedPipe.cpp@ 1959

Last change on this file since 1959 was 1803, checked in by vboxsync, 18 years ago

Mark thread NamedPipe listen thread as waitable, so that waiting for it
actually works.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 KB
Line 
1/** @file
2 *
3 * VBox stream devices:
4 * Named pipe stream
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_NAMEDPIPE
28#include <VBox/pdm.h>
29#include <VBox/cfgm.h>
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <VBox/mm.h>
34#include <iprt/assert.h>
35#include <iprt/file.h>
36#include <iprt/stream.h>
37#include <iprt/alloc.h>
38#include <iprt/string.h>
39#include <iprt/semaphore.h>
40
41#include "Builtins.h"
42
43#ifdef __WIN__
44#include <windows.h>
45#else /* !__WIN__ */
46#include <errno.h>
47#include <unistd.h>
48#include <sys/types.h>
49#include <sys/socket.h>
50#include <sys/un.h>
51#endif /* !__WIN__ */
52
53/*******************************************************************************
54* Defined Constants And Macros *
55*******************************************************************************/
56
57/** Converts a pointer to DRVNAMEDPIPE::IMedia to a PDRVNAMEDPIPE. */
58#define PDMISTREAM_2_DRVNAMEDPIPE(pInterface) ( (PDRVNAMEDPIPE)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAMEDPIPE, IStream)) )
59
60/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
61#define PDMIBASE_2_DRVINS(pInterface) ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
62
63/*******************************************************************************
64* Structures and Typedefs *
65*******************************************************************************/
66/**
67 * Named pipe driver instance data.
68 */
69typedef struct DRVNAMEDPIPE
70{
71 /** The stream interface. */
72 PDMISTREAM IStream;
73 /** Pointer to the driver instance. */
74 PPDMDRVINS pDrvIns;
75 /** Pointer to the named pipe file name. (Freed by MM) */
76 char *pszLocation;
77 /** Flag whether VirtualBox represents the server or client side. */
78 bool fIsServer;
79#ifdef __WIN__
80 /* File handle of the named pipe. */
81 RTFILE NamedPipe;
82 /* Overlapped structure for writes. */
83 OVERLAPPED OverlappedWrite;
84 /* Overlapped structure for reads. */
85 OVERLAPPED OverlappedRead;
86 /* Listen thread wakeup semaphore */
87 RTSEMEVENT ListenSem;
88#else /* !__WIN__ */
89 /** Socket handle of the local socket for server. */
90 RTSOCKET LocalSocketServer;
91 /** Socket handle of the local socket. */
92 RTSOCKET LocalSocket;
93#endif /* !__WIN__ */
94 /** Thread for listening for new connections. */
95 RTTHREAD ListenThread;
96 /** Flag to signal listening thread to shut down. */
97 bool fShutdown;
98} DRVNAMEDPIPE, *PDRVNAMEDPIPE;
99
100
101/*******************************************************************************
102* Internal Functions *
103*******************************************************************************/
104
105
106/** @copydoc PDMISTREAM::pfnRead */
107static DECLCALLBACK(int) drvNamedPipeRead(PPDMISTREAM pInterface, void *pvBuf, size_t *cbRead)
108{
109 int rc = VINF_SUCCESS;
110 PDRVNAMEDPIPE pData = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
111 LogFlow(("%s: pvBuf=%p cbRead=%#x (%s)\n", __FUNCTION__, pvBuf, cbRead, pData->pszLocation));
112
113 Assert(pvBuf);
114#ifdef __WIN__
115 if (pData->NamedPipe != NIL_RTFILE)
116 {
117 DWORD cbReallyRead;
118 pData->OverlappedRead.Offset = 0;
119 pData->OverlappedRead.OffsetHigh = 0;
120 if (!ReadFile((HANDLE)pData->NamedPipe, pvBuf, *cbRead, &cbReallyRead, &pData->OverlappedRead))
121 {
122 DWORD uError = GetLastError();
123
124 if ( uError == ERROR_PIPE_LISTENING
125 || uError == ERROR_PIPE_NOT_CONNECTED)
126 {
127 /* No connection yet/anymore */
128 cbReallyRead = 0;
129
130 /* wait a bit or else we'll be called right back. */
131 RTThreadSleep(100);
132 }
133 else
134 {
135 if (uError == ERROR_IO_PENDING)
136 {
137 uError = 0;
138
139 /* Wait for incoming bytes. */
140 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedRead, &cbReallyRead, TRUE) == FALSE)
141 uError = GetLastError();
142 }
143
144 rc = RTErrConvertFromWin32(uError);
145 Log(("drvNamedPipeRead: ReadFile returned %d (%Vrc)\n", uError, rc));
146 }
147 }
148
149 if (VBOX_FAILURE(rc))
150 {
151 Log(("drvNamedPipeRead: RTFileRead returned %Vrc fShutdown=%d\n", rc, pData->fShutdown));
152 if ( !pData->fShutdown
153 && ( rc == VERR_EOF
154 || rc == VERR_BROKEN_PIPE
155 )
156 )
157
158 {
159 RTFILE tmp = pData->NamedPipe;
160 FlushFileBuffers((HANDLE)tmp);
161 DisconnectNamedPipe((HANDLE)tmp);
162 if (!pData->fIsServer)
163 {
164 pData->NamedPipe = NIL_RTFILE;
165 RTFileClose(tmp);
166 }
167 /* pretend success */
168 rc = VINF_SUCCESS;
169 }
170 cbReallyRead = 0;
171 }
172 *cbRead = (size_t)cbReallyRead;
173 }
174#else /* !__WIN__ */
175 if (pData->LocalSocket != NIL_RTSOCKET)
176 {
177 ssize_t cbReallyRead;
178 cbReallyRead = recv(pData->LocalSocket, pvBuf, *cbRead, 0);
179 if (cbReallyRead == 0)
180 {
181 RTSOCKET tmp = pData->LocalSocket;
182 pData->LocalSocket = NIL_RTSOCKET;
183 close(tmp);
184 }
185 else if (cbReallyRead == -1)
186 {
187 cbReallyRead = 0;
188 rc = RTErrConvertFromErrno(errno);
189 }
190 *cbRead = cbReallyRead;
191 }
192#endif /* !__WIN__ */
193 else
194 {
195 RTThreadSleep(100);
196 *cbRead = 0;
197 }
198
199 LogFlow(("%s: cbRead=%d returns %Vrc\n", __FUNCTION__, *cbRead, rc));
200 return rc;
201}
202
203
204/** @copydoc PDMISTREAM::pfnWrite */
205static DECLCALLBACK(int) drvNamedPipeWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *cbWrite)
206{
207 int rc = VINF_SUCCESS;
208 PDRVNAMEDPIPE pData = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
209 LogFlow(("%s: pvBuf=%p cbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, cbWrite, pData->pszLocation));
210
211 Assert(pvBuf);
212#ifdef __WIN__
213 if (pData->NamedPipe != NIL_RTFILE)
214 {
215 unsigned cbWritten;
216 pData->OverlappedWrite.Offset = 0;
217 pData->OverlappedWrite.OffsetHigh = 0;
218 if (!WriteFile((HANDLE)pData->NamedPipe, pvBuf, *cbWrite, NULL, &pData->OverlappedWrite))
219 {
220 DWORD uError = GetLastError();
221
222 if ( uError == ERROR_PIPE_LISTENING
223 || uError == ERROR_PIPE_NOT_CONNECTED)
224 {
225 /* No connection yet/anymore; just discard the write. */
226 cbWritten = *cbWrite;
227 }
228 else
229 if (uError != ERROR_IO_PENDING)
230 {
231 rc = RTErrConvertFromWin32(uError);
232 Log(("drvNamedPipeWrite: WriteFile returned %d (%Vrc)\n", uError, rc));
233 }
234 else
235 {
236 /* Wait for the write to complete. */
237 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedWrite, (DWORD *)&cbWritten, TRUE) == FALSE)
238 uError = GetLastError();
239 }
240 }
241 else
242 cbWritten = *cbWrite;
243
244 if (VBOX_FAILURE(rc))
245 {
246 if ( rc == VERR_EOF
247 || rc == VERR_BROKEN_PIPE)
248 {
249 RTFILE tmp = pData->NamedPipe;
250 FlushFileBuffers((HANDLE)tmp);
251 DisconnectNamedPipe((HANDLE)tmp);
252 if (!pData->fIsServer)
253 {
254 pData->NamedPipe = NIL_RTFILE;
255 RTFileClose(tmp);
256 }
257 /* pretend success */
258 rc = VINF_SUCCESS;
259 }
260 cbWritten = 0;
261 }
262 *cbWrite = cbWritten;
263 }
264#else /* !__WIN__ */
265 if (pData->LocalSocket != NIL_RTSOCKET)
266 {
267 ssize_t cbWritten;
268 cbWritten = send(pData->LocalSocket, pvBuf, *cbWrite, 0);
269 if (cbWritten == 0)
270 {
271 RTSOCKET tmp = pData->LocalSocket;
272 pData->LocalSocket = NIL_RTSOCKET;
273 close(tmp);
274 }
275 else if (cbWritten == -1)
276 {
277 cbWritten = 0;
278 rc = RTErrConvertFromErrno(errno);
279 }
280 *cbWrite = cbWritten;
281 }
282#endif /* !__WIN__ */
283
284 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
285 return rc;
286}
287
288
289/**
290 * Queries an interface to the driver.
291 *
292 * @returns Pointer to interface.
293 * @returns NULL if the interface was not supported by the driver.
294 * @param pInterface Pointer to this interface structure.
295 * @param enmInterface The requested interface identification.
296 * @thread Any thread.
297 */
298static DECLCALLBACK(void *) drvNamedPipeQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
299{
300 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
301 PDRVNAMEDPIPE pDrv = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
302 switch (enmInterface)
303 {
304 case PDMINTERFACE_BASE:
305 return &pDrvIns->IBase;
306 case PDMINTERFACE_STREAM:
307 return &pDrv->IStream;
308 default:
309 return NULL;
310 }
311}
312
313
314/* -=-=-=-=- listen thread -=-=-=-=- */
315
316/**
317 * Receive thread loop.
318 *
319 * @returns 0 on success.
320 * @param ThreadSelf Thread handle to this thread.
321 * @param pvUser User argument.
322 */
323static DECLCALLBACK(int) drvNamedPipeListenLoop(RTTHREAD ThreadSelf, void *pvUser)
324{
325 PDRVNAMEDPIPE pData = (PDRVNAMEDPIPE)pvUser;
326 int rc = VINF_SUCCESS;
327#ifdef __WIN__
328 RTFILE NamedPipe = pData->NamedPipe;
329 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, 0);
330#endif
331
332 while (RT_LIKELY(!pData->fShutdown))
333 {
334#ifdef __WIN__
335 OVERLAPPED overlapped;
336
337 memset(&overlapped, 0, sizeof(overlapped));
338 overlapped.hEvent = hEvent;
339
340 BOOL fConnected = ConnectNamedPipe((HANDLE)NamedPipe, &overlapped);
341 if ( !fConnected
342 && !pData->fShutdown)
343 {
344 DWORD hrc = GetLastError();
345
346 if (hrc == ERROR_IO_PENDING)
347 {
348 DWORD dummy;
349
350 hrc = 0;
351 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &overlapped, &dummy, TRUE) == FALSE)
352 hrc = GetLastError();
353
354 }
355
356 if (pData->fShutdown)
357 break;
358
359 if (hrc == ERROR_PIPE_CONNECTED)
360 {
361 RTSemEventWait(pData->ListenSem, 250);
362 }
363 else
364 if (hrc != ERROR_SUCCESS)
365 {
366 rc = RTErrConvertFromWin32(hrc);
367 LogRel(("NamedPipe%d: ConnectNamedPipe failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
368 break;
369 }
370 }
371#else /* !__WIN__ */
372 if (listen(pData->LocalSocketServer, 0) == -1)
373 {
374 rc = RTErrConvertFromErrno(errno);
375 LogRel(("NamedPipe%d: listen failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
376 break;
377 }
378 int s = accept(pData->LocalSocketServer, NULL, NULL);
379 if (s == -1)
380 {
381 rc = RTErrConvertFromErrno(errno);
382 LogRel(("NamedPipe%d: accept failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
383 break;
384 }
385 else
386 {
387 if (pData->LocalSocket != NIL_RTSOCKET)
388 {
389 LogRel(("NamedPipe%d: only single connection supported\n", pData->pDrvIns->iInstance));
390 close(s);
391 }
392 else
393 pData->LocalSocket = s;
394 }
395#endif /* !__WIN__ */
396 }
397
398#ifdef __WIN__
399 CloseHandle(hEvent);
400#endif
401 pData->ListenThread = NIL_RTTHREAD;
402 return VINF_SUCCESS;
403}
404
405
406/**
407 * Construct a named pipe stream driver instance.
408 *
409 * @returns VBox status.
410 * @param pDrvIns The driver instance data.
411 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
412 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
413 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
414 * iInstance it's expected to be used a bit in this function.
415 */
416static DECLCALLBACK(int) drvNamedPipeConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
417{
418 int rc;
419 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
420
421 /*
422 * Init the static parts.
423 */
424 pData->pDrvIns = pDrvIns;
425 pData->pszLocation = NULL;
426 pData->fIsServer = false;
427#ifdef __WIN__
428 pData->NamedPipe = NIL_RTFILE;
429#else /* !__WIN__ */
430 pData->LocalSocketServer = NIL_RTSOCKET;
431 pData->LocalSocket = NIL_RTSOCKET;
432#endif /* !__WIN__ */
433 pData->ListenThread = NIL_RTTHREAD;
434 pData->fShutdown = false;
435 /* IBase */
436 pDrvIns->IBase.pfnQueryInterface = drvNamedPipeQueryInterface;
437 /* IStream */
438 pData->IStream.pfnRead = drvNamedPipeRead;
439 pData->IStream.pfnWrite = drvNamedPipeWrite;
440
441 /*
442 * Read the configuration.
443 */
444 if (!CFGMR3AreValuesValid(pCfgHandle, "Location\0IsServer\0"))
445 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
446
447 char *pszLocation;
448 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Location", &pszLocation);
449 if (VBOX_FAILURE(rc))
450 {
451 AssertMsgFailed(("Configuration error: query \"Location\" resulted in %Vrc.\n", rc));
452 return rc;
453 }
454 pData->pszLocation = pszLocation;
455
456 bool fIsServer;
457 rc = CFGMR3QueryBool(pCfgHandle, "IsServer", &fIsServer);
458 if (VBOX_FAILURE(rc))
459 {
460 AssertMsgFailed(("Configuration error: query \"IsServer\" resulted in %Vrc.\n", rc));
461 goto out;
462 }
463 pData->fIsServer = fIsServer;
464
465#ifdef __WIN__
466 if (fIsServer)
467 {
468 HANDLE hPipe = CreateNamedPipe(pData->pszLocation, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 32, 32, 10000, NULL);
469 if (hPipe == INVALID_HANDLE_VALUE)
470 {
471 rc = RTErrConvertFromWin32(GetLastError());
472 LogRel(("NamedPipe%d: CreateNamedPipe failed rc=%Vrc\n", pData->pDrvIns->iInstance));
473 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create named pipe %s"), pDrvIns->iInstance, pszLocation);
474 }
475 pData->NamedPipe = (HFILE)hPipe;
476
477 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "NamedPipe");
478 if VBOX_FAILURE(rc)
479 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
480
481 rc = RTSemEventCreate(&pData->ListenSem);
482 AssertRC(rc);
483 }
484 else
485 {
486 /* Connect to the named pipe. */
487 rc = RTFileOpen(&pData->NamedPipe, pszLocation, RTFILE_O_READWRITE);
488 if (VBOX_FAILURE(rc))
489 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"), pDrvIns->iInstance, pszLocation);
490 }
491
492 memset(&pData->OverlappedWrite, 0, sizeof(pData->OverlappedWrite));
493 memset(&pData->OverlappedRead, 0, sizeof(pData->OverlappedRead));
494 pData->OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
495 pData->OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
496#else /* !__WIN__ */
497 int s;
498 struct sockaddr_un addr;
499
500 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
501 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to create local socket"), pDrvIns->iInstance);
502
503 memset(&addr, 0, sizeof(addr));
504 addr.sun_family = AF_UNIX;
505 strncpy(addr.sun_path, pszLocation, sizeof(addr.sun_path)-1);
506
507 if (fIsServer)
508 {
509 /* Bind address to the local socket. */
510 RTFileDelete(pszLocation);
511 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
512 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to bind to local socket %s"), pDrvIns->iInstance, pszLocation);
513 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "NamedPipe");
514 if VBOX_FAILURE(rc)
515 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
516 pData->LocalSocketServer = s;
517 }
518 else
519 {
520 /* Connect to the local socket. */
521 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
522 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to connect to local socket %s"), pDrvIns->iInstance, pszLocation);
523 pData->LocalSocket = s;
524 }
525#endif /* !__WIN__ */
526
527out:
528 if (VBOX_FAILURE(rc))
529 {
530 if (pszLocation)
531 MMR3HeapFree(pszLocation);
532 }
533 else
534 {
535 LogFlow(("drvNamedPipeConstruct: location %s isServer %d\n", pszLocation, fIsServer));
536 LogRel(("NamedPipe: location %s, %s\n", pszLocation, fIsServer ? "server" : "client"));
537 }
538 return rc;
539}
540
541
542/**
543 * Destruct a named pipe stream driver instance.
544 *
545 * Most VM resources are freed by the VM. This callback is provided so that
546 * any non-VM resources can be freed correctly.
547 *
548 * @param pDrvIns The driver instance data.
549 */
550static DECLCALLBACK(void) drvNamedPipeDestruct(PPDMDRVINS pDrvIns)
551{
552 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
553 LogFlow(("%s: %s\n", __FUNCTION__, pData->pszLocation));
554
555 pData->fShutdown = true;
556
557#ifdef __WIN__
558 if (pData->NamedPipe != NIL_RTFILE)
559 {
560 FlushFileBuffers((HANDLE)pData->NamedPipe);
561 if (!pData->fIsServer)
562 DisconnectNamedPipe((HANDLE)pData->NamedPipe);
563
564 RTFileClose(pData->NamedPipe);
565 pData->NamedPipe = NIL_RTFILE;
566 CloseHandle(pData->OverlappedRead.hEvent);
567 CloseHandle(pData->OverlappedWrite.hEvent);
568 }
569 /* Wake up listen thread */
570 RTSemEventSignal(pData->ListenSem);
571 RTSemEventDestroy(pData->ListenSem);
572#else /* !__WIN__ */
573 if (pData->fIsServer)
574 {
575 if (pData->LocalSocketServer != NIL_RTSOCKET)
576 close(pData->LocalSocketServer);
577 if (pData->pszLocation)
578 RTFileDelete(pData->pszLocation);
579 }
580 else
581 {
582 if (pData->LocalSocket != NIL_RTSOCKET)
583 close(pData->LocalSocket);
584 }
585#endif /* !__WIN__ */
586
587 if (pData->ListenThread)
588 {
589 RTThreadWait(pData->ListenThread, 250, NULL);
590 if (pData->ListenThread != NIL_RTTHREAD)
591 LogRel(("NamedPipe%d: listen thread did not terminate\n", pDrvIns->iInstance));
592 }
593
594 if (pData->pszLocation)
595 MMR3HeapFree(pData->pszLocation);
596}
597
598
599/**
600 * Named pipe driver registration record.
601 */
602const PDMDRVREG g_DrvNamedPipe =
603{
604 /* u32Version */
605 PDM_DRVREG_VERSION,
606 /* szDriverName */
607 "NamedPipe",
608 /* pszDescription */
609 "Named Pipe stream driver.",
610 /* fFlags */
611 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
612 /* fClass. */
613 PDM_DRVREG_CLASS_STREAM,
614 /* cMaxInstances */
615 ~0,
616 /* cbInstance */
617 sizeof(DRVNAMEDPIPE),
618 /* pfnConstruct */
619 drvNamedPipeConstruct,
620 /* pfnDestruct */
621 drvNamedPipeDestruct,
622 /* pfnIOCtl */
623 NULL,
624 /* pfnPowerOn */
625 NULL,
626 /* pfnReset */
627 NULL,
628 /* pfnSuspend */
629 NULL,
630 /* pfnResume */
631 NULL,
632 /* pfnDetach */
633 NULL,
634 /* pfnPowerOff */
635 NULL
636};
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