VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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