VirtualBox

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

Last change on this file since 5999 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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