VirtualBox

source: vbox/trunk/src/VBox/Devices/Parallel/DevParallel.cpp@ 3649

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

Added parallel device emulation and host driver. Contributed by: Alexander Eichner

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.4 KB
Line 
1/* $Id: DevParallel.cpp 3649 2007-07-16 15:41:16Z vboxsync $ */
2/** @file
3 * VirtualBox Parallel Device Emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/* based on DevSerial.cpp */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DEV_PARALLEL
28#include <VBox/pdm.h>
29#include <VBox/err.h>
30
31#include <VBox/log.h>
32#include <iprt/assert.h>
33#include <iprt/uuid.h>
34#include <iprt/string.h>
35#include <iprt/semaphore.h>
36#include <iprt/critsect.h>
37
38#include "Builtins.h"
39#include "ParallelIOCtlCmd.h"
40
41#define PARALLEL_SAVED_STATE_VERSION 1
42
43/* defines for accessing the register bits */
44#define LPT_STATUS_BUSY 0x80
45#define LPT_STATUS_ACK 0x40
46#define LPT_STATUS_PAPER_OUT 0x20
47#define LPT_STATUS_SELECT_IN 0x10
48#define LPT_STATUS_ERROR 0x08
49#define LPT_STATUS_IRQ 0x04
50#define LPT_STATUS_BIT1 0x02 /* reserved (only for completeness) */
51#define LPT_STATUS_BIT0 0x01 /* reserved (only for completeness) */
52
53#define LPT_CONTROL_BIT7 0x80 /* reserved (only for completeness) */
54#define LPT_CONTROL_BIT6 0x40 /* reserved (only for completeness) */
55#define LPT_CONTROL_ENABLE_BIDIRECT 0x20
56#define LPT_CONTROL_ENABLE_IRQ_VIA_ACK 0x10
57#define LPT_CONTROL_SELECT_PRINTER 0x08
58#define LPT_CONTROL_RESET 0x04
59#define LPT_CONTROL_AUTO_LINEFEED 0x02
60#define LPT_CONTROL_STROBE 0x01
61
62typedef struct ParallelState
63{
64 /** Access critical section. */
65 PDMCRITSECT CritSect;
66
67 /** Pointer to the device instance. */
68 HCPTRTYPE(PPDMDEVINS) pDevInsHC;
69 /** Pointer to the device instance. */
70 GCPTRTYPE(PPDMDEVINS) pDevInsGC;
71#if HC_ARCH_BITS == 64 && GC_ARCH_BITS != 64
72 RTGCPTR Alignment0;
73#endif
74 /** The base interface. */
75 HCPTRTYPE(PDMIBASE) IBase;
76 /** The host device port interface. */
77 HCPTRTYPE(PDMIHOSTDEVICEPORT) IHostDevicePort;
78 /** Pointer to the attached base driver. */
79 HCPTRTYPE(PPDMIBASE) pDrvBase;
80 /** Pointer to the attached host device. */
81 HCPTRTYPE(PPDMIHOSTDEVICECONNECTOR) pDrvHostDeviceConnector;
82
83 uint8_t reg_data;
84 uint8_t reg_status;
85 uint8_t reg_control;
86
87 int irq;
88
89 bool fGCEnabled;
90 bool fR0Enabled;
91 bool afAlignment[6];
92
93 RTSEMEVENT ReceiveSem;
94 uint32_t base;
95
96} DEVPARALLELSTATE, *PDEVPARALLELSTATE;
97typedef DEVPARALLELSTATE ParallelState;
98
99#ifndef VBOX_DEVICE_STRUCT_TESTCASE
100
101#define PDMIBASE_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IBase)) )
102#define PDMIHOSTDEVICEPORT_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IHostDevicePort)) )
103
104
105__BEGIN_DECLS
106PDMBOTHCBDECL(int) parallelIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
107PDMBOTHCBDECL(int) parallelIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
108__END_DECLS
109
110
111static void parallel_update_irq(ParallelState *s)
112{
113 if (s->reg_control & LPT_CONTROL_ENABLE_IRQ_VIA_ACK) {
114 Log(("parallel_update_irq %d 1\n", s->irq));
115 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 1);
116 } else {
117 Log(("parallel_update_irq %d 0\n", s->irq));
118 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 0);
119 }
120}
121
122static int parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val)
123{
124 ParallelState *s = (ParallelState *)opaque;
125 unsigned char ch;
126
127 addr &= 7;
128 LogFlow(("parallel: write addr=0x%02x val=0x%02x\n", addr, val));
129 switch(addr) {
130 default:
131 case 0:
132#ifndef IN_RING3
133 NOREF(ch);
134 return VINF_IOM_HC_IOPORT_WRITE;
135#else
136 ch = val;
137 s->reg_data = ch;
138 if (RT_LIKELY(s->pDrvHostDeviceConnector))
139 {
140 Log(("parallel_io_port_write: write 0x%X\n", ch));
141 size_t cbWrite = 1;
142 int rc = s->pDrvHostDeviceConnector->pfnWrite(s->pDrvHostDeviceConnector, &ch, &cbWrite);
143 AssertRC(rc);
144 }
145#endif
146 break;
147 case 1:
148 break;
149 case 2:
150 s->reg_control = val;
151 parallel_update_irq(s);
152 break;
153 case 3:
154 break;
155 case 4:
156 break;
157 case 5:
158 break;
159 case 6:
160 break;
161 case 7:
162 break;
163 }
164 return VINF_SUCCESS;
165}
166
167static uint32_t parallel_ioport_read(void *opaque, uint32_t addr, int *pRC)
168{
169 ParallelState *s = (ParallelState *)opaque;
170 uint32_t ret = ~0U;
171
172 *pRC = VINF_SUCCESS;
173
174 addr &= 7;
175 switch(addr) {
176 default:
177 case 0:
178#ifndef IN_RING3
179 *pRC = VINF_IOM_HC_IOPORT_READ;
180#else
181 if (RT_LIKELY(s->pDrvHostDeviceConnector))
182 {
183 size_t cbRead;
184 int rc = s->pDrvHostDeviceConnector->pfnRead(s->pDrvHostDeviceConnector, &s->reg_data, &cbRead);
185 Log(("parallel_io_port_read: read 0x%X\n", s->reg_data));
186 AssertRC(rc);
187 }
188 ret = s->reg_data;
189#endif
190 break;
191 case 1:
192 ret = s->reg_status;
193 break;
194 case 2:
195 ret = s->reg_control;
196 break;
197 case 3:
198 break;
199 case 4:
200 break;
201 case 5:
202 break;
203 case 6:
204 break;
205 case 7:
206 break;
207 }
208 LogFlow(("parallel: read addr=0x%02x val=0x%02x\n", addr, ret));
209 return ret;
210}
211
212#ifdef IN_RING3
213static DECLCALLBACK(int) parallelNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
214{
215 ParallelState *pData = PDMIHOSTDEVICEPORT_2_PARALLELSTATE(pInterface);
216 int rc;
217
218 NOREF(pvBuf); NOREF(pcbRead); NOREF(pData); NOREF(rc);
219 return VINF_SUCCESS;
220#if 0
221 Assert(*pcbRead != 0);
222
223 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
224 if (pData->lsr & UART_LSR_DR)
225 {
226 /* If a character is still in the read queue, then wait for it to be emptied. */
227 PDMCritSectLeave(&pData->CritSect);
228 rc = RTSemEventWait(pData->ReceiveSem, 250);
229 if (VBOX_FAILURE(rc))
230 return rc;
231
232 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
233 }
234
235 if (!(pData->lsr & UART_LSR_DR))
236 {
237 pData->rbr = *(const char *)pvBuf;
238 pData->lsr |= UART_LSR_DR;
239 serial_update_irq(pData);
240 *pcbRead = 1;
241 rc = VINF_SUCCESS;
242 }
243 else
244 rc = VERR_TIMEOUT;
245
246 PDMCritSectLeave(&pData->CritSect);
247
248 return rc;
249#endif
250}
251#endif /* IN_RING3 */
252
253/**
254 * Port I/O Handler for OUT operations.
255 *
256 * @returns VBox status code.
257 *
258 * @param pDevIns The device instance.
259 * @param pvUser User argument.
260 * @param Port Port number used for the IN operation.
261 * @param u32 The value to output.
262 * @param cb The value size in bytes.
263 */
264PDMBOTHCBDECL(int) parallelIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
265 RTIOPORT Port, uint32_t u32, unsigned cb)
266{
267 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
268 int rc = VINF_SUCCESS;
269
270 if (cb == 1)
271 {
272 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
273 if (rc == VINF_SUCCESS)
274 {
275 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
276 rc = parallel_ioport_write (pData, Port, u32);
277 PDMCritSectLeave(&pData->CritSect);
278 }
279 }
280 else
281 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
282
283 return rc;
284}
285
286/**
287 * Port I/O Handler for IN operations.
288 *
289 * @returns VBox status code.
290 *
291 * @param pDevIns The device instance.
292 * @param pvUser User argument.
293 * @param Port Port number used for the IN operation.
294 * @param u32 The value to output.
295 * @param cb The value size in bytes.
296 */
297PDMBOTHCBDECL(int) parallelIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
298 RTIOPORT Port, uint32_t *pu32, unsigned cb)
299{
300 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
301 int rc = VINF_SUCCESS;
302
303 if (cb == 1)
304 {
305 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
306 if (rc == VINF_SUCCESS)
307 {
308 *pu32 = parallel_ioport_read (pData, Port, &rc);
309 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
310 PDMCritSectLeave(&pData->CritSect);
311 }
312 }
313 else
314 rc = VERR_IOM_IOPORT_UNUSED;
315
316 return rc;
317}
318
319#ifdef IN_RING3
320/**
321 * Saves a state of the serial port device.
322 *
323 * @returns VBox status code.
324 * @param pDevIns The device instance.
325 * @param pSSMHandle The handle to save the state to.
326 */
327static DECLCALLBACK(int) parallelSaveExec(PPDMDEVINS pDevIns,
328 PSSMHANDLE pSSMHandle)
329{
330 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
331
332 SSMR3PutU8(pSSMHandle, pData->reg_data);
333 SSMR3PutU8(pSSMHandle, pData->reg_status);
334 SSMR3PutU8(pSSMHandle, pData->reg_control);
335 SSMR3PutS32(pSSMHandle, pData->irq);
336 SSMR3PutU32(pSSMHandle, pData->base);
337
338 return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
339}
340
341/**
342 * Loads a saved serial port device state.
343 *
344 * @returns VBox status code.
345 * @param pDevIns The device instance.
346 * @param pSSMHandle The handle to the saved state.
347 * @param u32Version The data unit version number.
348 */
349static DECLCALLBACK(int) parallelLoadExec(PPDMDEVINS pDevIns,
350 PSSMHANDLE pSSMHandle,
351 uint32_t u32Version)
352{
353 int rc;
354 uint32_t u32;
355 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
356
357 if (u32Version != PARALLEL_SAVED_STATE_VERSION)
358 {
359 AssertMsgFailed(("u32Version=%d\n", u32Version));
360 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
361 }
362
363 SSMR3GetU8(pSSMHandle, &pData->reg_data);
364 SSMR3GetU8(pSSMHandle, &pData->reg_status);
365 SSMR3GetU8(pSSMHandle, &pData->reg_control);
366 SSMR3GetS32(pSSMHandle, &pData->irq);
367 SSMR3GetU32(pSSMHandle, &pData->base);
368
369 rc = SSMR3GetU32(pSSMHandle, &u32);
370 if (VBOX_FAILURE(rc))
371 return rc;
372
373 if (u32 != ~0U)
374 {
375 AssertMsgFailed(("u32=%#x expected ~0\n", u32));
376 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
377 }
378
379 pData->pDevInsHC = pDevIns;
380 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
381 return VINF_SUCCESS;
382}
383
384
385/**
386 * @copydoc FNPDMDEVRELOCATE
387 */
388static DECLCALLBACK(void) parallelRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
389{
390 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
391 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
392}
393
394/** @copyfrom PIBASE::pfnqueryInterface */
395static DECLCALLBACK(void *) parallelQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
396{
397 ParallelState *pData = PDMIBASE_2_PARALLELSTATE(pInterface);
398 switch (enmInterface)
399 {
400 case PDMINTERFACE_BASE:
401 return &pData->IBase;
402 case PDMINTERFACE_HOST_DEVICE_PORT:
403 return &pData->IHostDevicePort;
404 default:
405 return NULL;
406 }
407}
408
409/**
410 * Destruct a device instance.
411 *
412 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
413 * resources can be freed correctly.
414 *
415 * @returns VBox status.
416 * @param pDevIns The device instance data.
417 */
418static DECLCALLBACK(int) parallelDestruct(PPDMDEVINS pDevIns)
419{
420 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
421
422 RTSemEventDestroy(pData->ReceiveSem);
423 pData->ReceiveSem = NIL_RTSEMEVENT;
424
425 PDMR3CritSectDelete(&pData->CritSect);
426 return VINF_SUCCESS;
427}
428
429
430/**
431 * Construct a device instance for a VM.
432 *
433 * @returns VBox status.
434 * @param pDevIns The device instance data.
435 * If the registration structure is needed, pDevIns->pDevReg points to it.
436 * @param iInstance Instance number. Use this to figure out which registers and such to use.
437 * The device number is also found in pDevIns->iInstance, but since it's
438 * likely to be freqently used PDM passes it as parameter.
439 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
440 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
441 * iInstance it's expected to be used a bit in this function.
442 */
443static DECLCALLBACK(int) parallelConstruct(PPDMDEVINS pDevIns,
444 int iInstance,
445 PCFGMNODE pCfgHandle)
446{
447 int rc;
448 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState*);
449 uint16_t io_base;
450 uint8_t irq_lvl;
451
452 Assert(iInstance < 4);
453
454 pData->pDevInsHC = pDevIns;
455 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
456
457 /*
458 * Validate configuration.
459 */
460 if (!CFGMR3AreValuesValid(pCfgHandle, "IRQ\0IOBase\0"))
461 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
462
463 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &pData->fGCEnabled);
464 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
465 pData->fGCEnabled = true;
466 else if (VBOX_FAILURE(rc))
467 return PDMDEV_SET_ERROR(pDevIns, rc,
468 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
469
470 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &pData->fR0Enabled);
471 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
472 pData->fR0Enabled = true;
473 else if (VBOX_FAILURE(rc))
474 return PDMDEV_SET_ERROR(pDevIns, rc,
475 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
476
477 /* IBase */
478 pData->IBase.pfnQueryInterface = parallelQueryInterface;
479
480 /* ICharPort */
481 /* pData->ICharPort.pfnNotifyRead = parallelNotifyRead; */
482
483 rc = RTSemEventCreate(&pData->ReceiveSem);
484 AssertRC(rc);
485
486 /*
487 * Initialize critical section.
488 * This must of course be done before attaching drivers or anything else which can call us back..
489 */
490 char szName[24];
491 RTStrPrintf(szName, sizeof(szName), "Parallel#%d", iInstance);
492 rc = PDMDevHlpCritSectInit(pDevIns, &pData->CritSect, szName);
493 if (VBOX_FAILURE(rc))
494 return rc;
495
496/** @todo r=bird: Check for VERR_CFGM_VALUE_NOT_FOUND and provide sensible defaults.
497 * Also do AssertMsgFailed(("Configuration error:....)) in the failure cases of CFGMR3Query*()
498 * and CFGR3AreValuesValid() like we're doing in the other devices. */
499 rc = CFGMR3QueryU8(pCfgHandle, "IRQ", &irq_lvl);
500 if (VBOX_FAILURE(rc))
501 return rc;
502
503 rc = CFGMR3QueryU16(pCfgHandle, "IOBase", &io_base);
504 if (VBOX_FAILURE(rc))
505 return rc;
506
507 Log(("parallelConstruct instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
508
509 pData->irq = irq_lvl;
510 pData->reg_status = LPT_STATUS_BUSY | LPT_STATUS_IRQ;
511 pData->reg_control = LPT_CONTROL_STROBE | LPT_CONTROL_AUTO_LINEFEED | LPT_CONTROL_SELECT_PRINTER;
512 pData->base = io_base;
513 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
514 parallelIOPortWrite, parallelIOPortRead,
515 NULL, NULL, "PARALLEL");
516 if (VBOX_FAILURE (rc))
517 return rc;
518
519 if (pData->fGCEnabled)
520 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base, 8, 0, "parallelIOPortWrite",
521 "parallelIOPortRead", NULL, NULL, "Parallel");
522
523 if (pData->fR0Enabled)
524 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "parallelIOPortWrite",
525 "parallelIOPortRead", NULL, NULL, "Parallel");
526
527 /* Attach the char driver and get the interfaces. For now no run-time
528 * changes are supported. */
529 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pData->IBase, &pData->pDrvBase, "Parallel Host");
530 if (VBOX_SUCCESS(rc))
531 {
532 pData->pDrvHostDeviceConnector = (PDMIHOSTDEVICECONNECTOR *)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_HOST_DEVICE_CONNECTOR);
533 if (!pData->pDrvHostDeviceConnector)
534 {
535 AssertMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
536 return VERR_PDM_MISSING_INTERFACE;
537 }
538 /** @todo provide read notification interface!!!! */
539 }
540 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
541 {
542 pData->pDrvBase = NULL;
543 pData->pDrvHostDeviceConnector = NULL;
544 LogRel(("Parallel%d: no unit\n", iInstance));
545 }
546 else
547 {
548 AssertMsgFailed(("Parallel%d: Failed to attach to host driver. rc=%Vrc\n", iInstance, rc));
549 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
550 N_("Parallel device %d cannot attach to host driver\n"), iInstance);
551 }
552
553 rc = PDMDevHlpSSMRegister(
554 pDevIns, /* pDevIns */
555 pDevIns->pDevReg->szDeviceName, /* pszName */
556 iInstance, /* u32Instance */
557 PARALLEL_SAVED_STATE_VERSION, /* u32Version */
558 sizeof (*pData), /* cbGuess */
559 NULL, /* pfnSavePrep */
560 parallelSaveExec, /* pfnSaveExec */
561 NULL, /* pfnSaveDone */
562 NULL, /* pfnLoadPrep */
563 parallelLoadExec, /* pfnLoadExec */
564 NULL /* pfnLoadDone */
565 );
566 if (VBOX_FAILURE(rc))
567 return rc;
568
569 return VINF_SUCCESS;
570}
571
572/**
573 * The device registration structure.
574 */
575const PDMDEVREG g_DeviceParallelPort =
576{
577 /* u32Version */
578 PDM_DEVREG_VERSION,
579 /* szDeviceName */
580 "parallel",
581 /* szGCMod */
582 "VBoxDDGC.gc",
583 /* szR0Mod */
584 "VBoxDDR0.r0",
585 /* pszDescription */
586 "Parallel Communication Port",
587 /* fFlags */
588 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
589 /* fClass */
590 PDM_DEVREG_CLASS_PARALLEL,
591 /* cMaxInstances */
592 1,
593 /* cbInstance */
594 sizeof(ParallelState),
595 /* pfnConstruct */
596 parallelConstruct,
597 /* pfnDestruct */
598 parallelDestruct,
599 /* pfnRelocate */
600 parallelRelocate,
601 /* pfnIOCtl */
602 NULL,
603 /* pfnPowerOn */
604 NULL,
605 /* pfnReset */
606 NULL,
607 /* pfnSuspend */
608 NULL,
609 /* pfnResume */
610 NULL,
611 /* pfnAttach */
612 NULL,
613 /* pfnDetach */
614 NULL,
615 /* pfnQueryInterface. */
616 NULL
617};
618#endif /* IN_RING3 */
619
620
621#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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