VirtualBox

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

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

Continue work on the parallel port device:

  1. Removed the host device interface and replaced it with a parallel port specific one
  2. Report interrupts to the device
  3. Added code for ECP (disabled for now)
  4. Added an entry in the ACPI tables to make parallel port detection on Windows guests work
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 28.3 KB
Line 
1/* $Id: DevParallel.cpp 6222 2007-12-30 17:06:27Z 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.215389.xyz. 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
35#define PARALLEL_SAVED_STATE_VERSION 1
36
37/* defines for accessing the register bits */
38#define LPT_STATUS_BUSY 0x80
39#define LPT_STATUS_ACK 0x40
40#define LPT_STATUS_PAPER_OUT 0x20
41#define LPT_STATUS_SELECT_IN 0x10
42#define LPT_STATUS_ERROR 0x08
43#define LPT_STATUS_IRQ 0x04
44#define LPT_STATUS_BIT1 0x02 /* reserved (only for completeness) */
45#define LPT_STATUS_EPP_TIMEOUT 0x01
46
47#define LPT_CONTROL_BIT7 0x80 /* reserved (only for completeness) */
48#define LPT_CONTROL_BIT6 0x40 /* reserved (only for completeness) */
49#define LPT_CONTROL_ENABLE_BIDIRECT 0x20
50#define LPT_CONTROL_ENABLE_IRQ_VIA_ACK 0x10
51#define LPT_CONTROL_SELECT_PRINTER 0x08
52#define LPT_CONTROL_RESET 0x04
53#define LPT_CONTROL_AUTO_LINEFEED 0x02
54#define LPT_CONTROL_STROBE 0x01
55
56/** mode defines for the extended control register */
57#define LPT_ECP_ECR_CHIPMODE_MASK 0xe0
58#define LPT_ECP_ECR_CHIPMODE_GET_BITS(reg) ((reg) >> 5)
59#define LPT_ECP_ECR_CHIPMODE_SET_BITS(val) ((val) << 5)
60#define LPT_ECP_ECR_CHIPMODE_CONFIGURATION 0x07
61#define LPT_ECP_ECR_CHIPMODE_FIFO_TEST 0x06
62#define LPT_ECP_ECR_CHIPMODE_RESERVED 0x05
63#define LPT_ECP_ECR_CHIPMODE_EPP 0x04
64#define LPT_ECP_ECR_CHIPMODE_ECP_FIFO 0x03
65#define LPT_ECP_ECR_CHIPMODE_PP_FIFO 0x02
66#define LPT_ECP_ECR_CHIPMODE_BYTE 0x01
67#define LPT_ECP_ECR_CHIPMODE_COMPAT 0x00
68
69/** FIFO status bits in extended control register */
70#define LPT_ECP_ECR_FIFO_MASK 0x03
71#define LPT_ECP_ECR_FIFO_SOME_DATA 0x00
72#define LPT_ECP_ECR_FIFO_FULL 0x02
73#define LPT_ECP_ECR_FIFO_EMPTY 0x01
74
75#define LPT_ECP_CONFIGA_FIFO_WITDH_MASK 0x70
76#define LPT_ECP_CONFIGA_FIFO_WIDTH_GET_BITS(reg) ((reg) >> 4)
77#define LPT_ECP_CONFIGA_FIFO_WIDTH_SET_BITS(val) ((val) << 4)
78#define LPT_ECP_CONFIGA_FIFO_WIDTH_16 0x00
79#define LPT_ECP_CONFIGA_FIFO_WIDTH_32 0x20
80#define LPT_ECP_CONFIGA_FIFO_WIDTH_8 0x10
81
82#define LPT_ECP_FIFO_DEPTH 2
83
84
85typedef struct ParallelState
86{
87 /** Access critical section. */
88 PDMCRITSECT CritSect;
89
90 /** Pointer to the device instance. */
91 R3R0PTRTYPE(PPDMDEVINS) pDevInsHC;
92 /** Pointer to the device instance. */
93 GCPTRTYPE(PPDMDEVINS) pDevInsGC;
94#if HC_ARCH_BITS == 64 && GC_ARCH_BITS != 64
95 RTGCPTR Alignment0;
96#endif
97 /** The base interface. */
98 R3PTRTYPE(PDMIBASE) IBase;
99 /** The host device port interface. */
100 R3PTRTYPE(PDMIHOSTPARALLELPORT) IHostParallelPort;
101 /** Pointer to the attached base driver. */
102 R3PTRTYPE(PPDMIBASE) pDrvBase;
103 /** Pointer to the attached host device. */
104 R3PTRTYPE(PPDMIHOSTPARALLELCONNECTOR) pDrvHostParallelConnector;
105
106 uint8_t reg_data;
107 uint8_t reg_status;
108 uint8_t reg_control;
109 uint8_t reg_epp_addr;
110 uint8_t reg_epp_data;
111 uint8_t reg_ecp_ecr;
112 uint8_t reg_ecp_base_plus_400h; /* has different meanings */
113 uint8_t reg_ecp_config_b;
114
115 /** The ECP FIFO implementation*/
116 uint8_t ecp_fifo[LPT_ECP_FIFO_DEPTH];
117 int act_fifo_pos_write;
118 int act_fifo_pos_read;
119
120 int irq;
121 uint8_t epp_timeout;
122
123 bool fGCEnabled;
124 bool fR0Enabled;
125 bool afAlignment[6];
126
127 RTSEMEVENT ReceiveSem;
128 uint32_t base;
129
130} DEVPARALLELSTATE, *PDEVPARALLELSTATE;
131typedef DEVPARALLELSTATE ParallelState;
132
133#ifndef VBOX_DEVICE_STRUCT_TESTCASE
134
135#define PDMIHOSTPARALLELPORT_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IHostParallelPort)) )
136#define PDMIHOSTDEVICEPORT_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IHostDevicePort)) )
137#define PDMIBASE_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IBase)) )
138
139__BEGIN_DECLS
140PDMBOTHCBDECL(int) parallelIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
141PDMBOTHCBDECL(int) parallelIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
142#if 0
143PDMBOTHCBDECL(int) parallelIOPortReadECP(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
144PDMBOTHCBDECL(int) parallelIOPortWriteECP(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
145#endif
146__END_DECLS
147
148static void parallel_set_irq(ParallelState *s)
149{
150 if (s->reg_control & LPT_CONTROL_ENABLE_IRQ_VIA_ACK)
151 {
152 Log(("parallel_update_irq %d 1\n", s->irq));
153 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 1);
154 }
155}
156
157static void parallel_clear_irq(ParallelState *s)
158{
159 Log(("parallel_update_irq %d 0\n", s->irq));
160 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 0);
161}
162
163static int parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val)
164{
165 ParallelState *s = (ParallelState *)opaque;
166 unsigned char ch;
167
168 addr &= 7;
169 LogFlow(("parallel: write addr=0x%02x val=0x%02x\n", addr, val));
170 ch = val;
171
172 switch(addr) {
173 default:
174 case 0:
175#ifndef IN_RING3
176 NOREF(ch);
177 return VINF_IOM_HC_IOPORT_WRITE;
178#else
179 s->reg_data = ch;
180 if (RT_LIKELY(s->pDrvHostParallelConnector))
181 {
182 Log(("parallel_io_port_write: write 0x%X\n", ch));
183 size_t cbWrite = 1;
184 int rc = s->pDrvHostParallelConnector->pfnWrite(s->pDrvHostParallelConnector, &ch, &cbWrite);
185 AssertRC(rc);
186 }
187#endif
188 break;
189 case 1:
190 break;
191 case 2:
192 /** Set the reserved bits to one */
193 ch |= (LPT_CONTROL_BIT6 | LPT_CONTROL_BIT7);
194 if (ch != s->reg_control) {
195#ifndef IN_RING3
196 NOREF(ch);
197 return VINF_IOM_HC_IOPORT_WRITE;
198#else
199 int rc = s->pDrvHostParallelConnector->pfnWriteControl(s->pDrvHostParallelConnector, ch);
200 AssertRC(rc);
201 s->reg_control = val;
202#endif
203 }
204 break;
205 case 3:
206 s->reg_epp_addr = val;
207 break;
208 case 4:
209 s->reg_epp_data = val;
210 break;
211 case 5:
212 break;
213 case 6:
214 break;
215 case 7:
216 break;
217 }
218 return VINF_SUCCESS;
219}
220
221static uint32_t parallel_ioport_read(void *opaque, uint32_t addr, int *pRC)
222{
223 ParallelState *s = (ParallelState *)opaque;
224 uint32_t ret = ~0U;
225
226 *pRC = VINF_SUCCESS;
227
228 addr &= 7;
229 switch(addr) {
230 default:
231 case 0:
232 if (!(s->reg_control & LPT_CONTROL_ENABLE_BIDIRECT))
233 ret = s->reg_data;
234 else
235 {
236#ifndef IN_RING3
237 *pRC = VINF_IOM_HC_IOPORT_READ;
238#else
239 if (RT_LIKELY(s->pDrvHostParallelConnector))
240 {
241 size_t cbRead;
242 int rc = s->pDrvHostParallelConnector->pfnRead(s->pDrvHostParallelConnector, &s->reg_data, &cbRead);
243 Log(("parallel_io_port_read: read 0x%X\n", s->reg_data));
244 AssertRC(rc);
245 }
246 ret = s->reg_data;
247#endif
248 }
249 break;
250 case 1:
251#ifndef IN_RING3
252 *pRC = VINF_IOM_HC_IOPORT_READ;
253#else
254 if (RT_LIKELY(s->pDrvHostParallelConnector))
255 {
256 int rc = s->pDrvHostParallelConnector->pfnReadStatus(s->pDrvHostParallelConnector, &s->reg_status);
257 AssertRC(rc);
258 }
259 ret = s->reg_status;
260 parallel_clear_irq(s);
261#endif
262 break;
263 case 2:
264 ret = s->reg_control;
265 break;
266 case 3:
267 ret = s->reg_epp_addr;
268 break;
269 case 4:
270 ret = s->reg_epp_data;
271 break;
272 case 5:
273 break;
274 case 6:
275 break;
276 case 7:
277 break;
278 }
279 LogFlow(("parallel: read addr=0x%02x val=0x%02x\n", addr, ret));
280 return ret;
281}
282
283#if 0
284static int parallel_ioport_write_ecp(void *opaque, uint32_t addr, uint32_t val)
285{
286 ParallelState *s = (ParallelState *)opaque;
287 unsigned char ch;
288
289 addr &= 7;
290 LogFlow(("parallel: write ecp addr=0x%02x val=0x%02x\n", addr, val));
291 ch = val;
292 switch(addr) {
293 default:
294 case 0:
295 if (LPT_ECP_ECR_CHIPMODE_GET_BITS(s->reg_ecp_ecr) == LPT_ECP_ECR_CHIPMODE_FIFO_TEST) {
296 s->ecp_fifo[s->act_fifo_pos_write] = ch;
297 s->act_fifo_pos_write++;
298 if (s->act_fifo_pos_write < LPT_ECP_FIFO_DEPTH) {
299 /** FIFO has some data (clear both FIFO bits) */
300 s->reg_ecp_ecr &= ~(LPT_ECP_ECR_FIFO_EMPTY | LPT_ECP_ECR_FIFO_FULL);
301 } else {
302 /** FIFO is full */
303 /** Clear FIFO empty bit */
304 s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_EMPTY;
305 /** Set FIFO full bit */
306 s->reg_ecp_ecr |= LPT_ECP_ECR_FIFO_FULL;
307 s->act_fifo_pos_write = 0;
308 }
309 } else {
310 s->reg_ecp_base_plus_400h = ch;
311 }
312 break;
313 case 1:
314 s->reg_ecp_config_b = ch;
315 break;
316 case 2:
317 /** If we change the mode clear FIFO */
318 if ((ch & LPT_ECP_ECR_CHIPMODE_MASK) != (s->reg_ecp_ecr & LPT_ECP_ECR_CHIPMODE_MASK)) {
319 /** reset the fifo */
320 s->act_fifo_pos_write = 0;
321 s->act_fifo_pos_read = 0;
322 /** Set FIFO empty bit */
323 s->reg_ecp_ecr |= LPT_ECP_ECR_FIFO_EMPTY;
324 /** Clear FIFO full bit */
325 s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_FULL;
326 }
327 /** Set new mode */
328 s->reg_ecp_ecr |= LPT_ECP_ECR_CHIPMODE_SET_BITS(LPT_ECP_ECR_CHIPMODE_GET_BITS(ch));
329 break;
330 case 3:
331 break;
332 case 4:
333 break;
334 case 5:
335 break;
336 case 6:
337 break;
338 case 7:
339 break;
340 }
341 return VINF_SUCCESS;
342}
343
344static uint32_t parallel_ioport_read_ecp(void *opaque, uint32_t addr, int *pRC)
345{
346 ParallelState *s = (ParallelState *)opaque;
347 uint32_t ret = ~0U;
348
349 *pRC = VINF_SUCCESS;
350
351 addr &= 7;
352 switch(addr) {
353 default:
354 case 0:
355 if (LPT_ECP_ECR_CHIPMODE_GET_BITS(s->reg_ecp_ecr) == LPT_ECP_ECR_CHIPMODE_FIFO_TEST) {
356 ret = s->ecp_fifo[s->act_fifo_pos_read];
357 s->act_fifo_pos_read++;
358 if (s->act_fifo_pos_read == LPT_ECP_FIFO_DEPTH)
359 s->act_fifo_pos_read = 0; /** end of FIFO, start at beginning */
360 if (s->act_fifo_pos_read == s->act_fifo_pos_write) {
361 /** FIFO is empty */
362 /** Set FIFO empty bit */
363 s->reg_ecp_ecr |= LPT_ECP_ECR_FIFO_EMPTY;
364 /** Clear FIFO full bit */
365 s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_FULL;
366 } else {
367 /** FIFO has some data (clear all FIFO bits) */
368 s->reg_ecp_ecr &= ~(LPT_ECP_ECR_FIFO_EMPTY | LPT_ECP_ECR_FIFO_FULL);
369 }
370 } else {
371 ret = s->reg_ecp_base_plus_400h;
372 }
373 break;
374 case 1:
375 ret = s->reg_ecp_config_b;
376 break;
377 case 2:
378 ret = s->reg_ecp_ecr;
379 break;
380 case 3:
381 break;
382 case 4:
383 break;
384 case 5:
385 break;
386 case 6:
387 break;
388 case 7:
389 break;
390 }
391 LogFlow(("parallel: read ecp addr=0x%02x val=0x%02x\n", addr, ret));
392 return ret;
393}
394#endif
395
396#ifdef IN_RING3
397static DECLCALLBACK(int) parallelNotifyInterrupt(PPDMIHOSTPARALLELPORT pInterface)
398{
399 ParallelState *pData = PDMIHOSTPARALLELPORT_2_PARALLELSTATE(pInterface);
400
401 PDMCritSectEnter(&pData->CritSect, VINF_SUCCESS);
402 parallel_set_irq(pData);
403 PDMCritSectLeave(&pData->CritSect);
404
405 return VINF_SUCCESS;
406}
407#endif /* IN_RING3 */
408
409/**
410 * Port I/O Handler for OUT operations.
411 *
412 * @returns VBox status code.
413 *
414 * @param pDevIns The device instance.
415 * @param pvUser User argument.
416 * @param Port Port number used for the IN operation.
417 * @param u32 The value to output.
418 * @param cb The value size in bytes.
419 */
420PDMBOTHCBDECL(int) parallelIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
421 RTIOPORT Port, uint32_t u32, unsigned cb)
422{
423 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
424 int rc = VINF_SUCCESS;
425
426 if (cb == 1)
427 {
428 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
429 if (rc == VINF_SUCCESS)
430 {
431 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
432 rc = parallel_ioport_write (pData, Port, u32);
433 PDMCritSectLeave(&pData->CritSect);
434 }
435 }
436 else
437 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
438
439 return rc;
440}
441
442/**
443 * Port I/O Handler for IN operations.
444 *
445 * @returns VBox status code.
446 *
447 * @param pDevIns The device instance.
448 * @param pvUser User argument.
449 * @param Port Port number used for the IN operation.
450 * @param u32 The value to output.
451 * @param cb The value size in bytes.
452 */
453PDMBOTHCBDECL(int) parallelIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
454 RTIOPORT Port, uint32_t *pu32, unsigned cb)
455{
456 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
457 int rc = VINF_SUCCESS;
458
459 if (cb == 1)
460 {
461 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
462 if (rc == VINF_SUCCESS)
463 {
464 *pu32 = parallel_ioport_read (pData, Port, &rc);
465 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
466 PDMCritSectLeave(&pData->CritSect);
467 }
468 }
469 else
470 rc = VERR_IOM_IOPORT_UNUSED;
471
472 return rc;
473}
474
475#if 0
476/**
477 * Port I/O Handler for OUT operations on ECP registers.
478 *
479 * @returns VBox status code.
480 *
481 * @param pDevIns The device instance.
482 * @param pvUser User argument.
483 * @param Port Port number used for the IN operation.
484 * @param u32 The value to output.
485 * @param cb The value size in bytes.
486 */
487PDMBOTHCBDECL(int) parallelIOPortWriteECP(PPDMDEVINS pDevIns, void *pvUser,
488 RTIOPORT Port, uint32_t u32, unsigned cb)
489{
490 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
491 int rc = VINF_SUCCESS;
492
493 if (cb == 1)
494 {
495 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
496 if (rc == VINF_SUCCESS)
497 {
498 Log2(("%s: ecp port %#06x val %#04x\n", __FUNCTION__, Port, u32));
499 rc = parallel_ioport_write_ecp (pData, Port, u32);
500 PDMCritSectLeave(&pData->CritSect);
501 }
502 }
503 else
504 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
505
506 return rc;
507}
508
509/**
510 * Port I/O Handler for IN operations on ECP registers.
511 *
512 * @returns VBox status code.
513 *
514 * @param pDevIns The device instance.
515 * @param pvUser User argument.
516 * @param Port Port number used for the IN operation.
517 * @param u32 The value to output.
518 * @param cb The value size in bytes.
519 */
520PDMBOTHCBDECL(int) parallelIOPortReadECP(PPDMDEVINS pDevIns, void *pvUser,
521 RTIOPORT Port, uint32_t *pu32, unsigned cb)
522{
523 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
524 int rc = VINF_SUCCESS;
525
526 if (cb == 1)
527 {
528 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
529 if (rc == VINF_SUCCESS)
530 {
531 *pu32 = parallel_ioport_read_ecp (pData, Port, &rc);
532 Log2(("%s: ecp port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
533 PDMCritSectLeave(&pData->CritSect);
534 }
535 }
536 else
537 rc = VERR_IOM_IOPORT_UNUSED;
538
539 return rc;
540}
541#endif
542
543#ifdef IN_RING3
544/**
545 * Saves a state of the serial port device.
546 *
547 * @returns VBox status code.
548 * @param pDevIns The device instance.
549 * @param pSSMHandle The handle to save the state to.
550 */
551static DECLCALLBACK(int) parallelSaveExec(PPDMDEVINS pDevIns,
552 PSSMHANDLE pSSMHandle)
553{
554 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
555
556 SSMR3PutU8(pSSMHandle, pData->reg_data);
557 SSMR3PutU8(pSSMHandle, pData->reg_status);
558 SSMR3PutU8(pSSMHandle, pData->reg_control);
559 SSMR3PutS32(pSSMHandle, pData->irq);
560 SSMR3PutU32(pSSMHandle, pData->base);
561
562 return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
563}
564
565/**
566 * Loads a saved serial port device state.
567 *
568 * @returns VBox status code.
569 * @param pDevIns The device instance.
570 * @param pSSMHandle The handle to the saved state.
571 * @param u32Version The data unit version number.
572 */
573static DECLCALLBACK(int) parallelLoadExec(PPDMDEVINS pDevIns,
574 PSSMHANDLE pSSMHandle,
575 uint32_t u32Version)
576{
577 int rc;
578 uint32_t u32;
579 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
580
581 if (u32Version != PARALLEL_SAVED_STATE_VERSION)
582 {
583 AssertMsgFailed(("u32Version=%d\n", u32Version));
584 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
585 }
586
587 SSMR3GetU8(pSSMHandle, &pData->reg_data);
588 SSMR3GetU8(pSSMHandle, &pData->reg_status);
589 SSMR3GetU8(pSSMHandle, &pData->reg_control);
590 SSMR3GetS32(pSSMHandle, &pData->irq);
591 SSMR3GetU32(pSSMHandle, &pData->base);
592
593 rc = SSMR3GetU32(pSSMHandle, &u32);
594 if (VBOX_FAILURE(rc))
595 return rc;
596
597 if (u32 != ~0U)
598 {
599 AssertMsgFailed(("u32=%#x expected ~0\n", u32));
600 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
601 }
602
603 pData->pDevInsHC = pDevIns;
604 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
605 return VINF_SUCCESS;
606}
607
608
609/**
610 * @copydoc FNPDMDEVRELOCATE
611 */
612static DECLCALLBACK(void) parallelRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
613{
614 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
615 pData->pDevInsGC += offDelta;
616}
617
618/** @copyfrom PIBASE::pfnqueryInterface */
619static DECLCALLBACK(void *) parallelQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
620{
621 ParallelState *pData = PDMIBASE_2_PARALLELSTATE(pInterface);
622 switch (enmInterface)
623 {
624 case PDMINTERFACE_BASE:
625 return &pData->IBase;
626 case PDMINTERFACE_HOST_PARALLEL_PORT:
627 return &pData->IHostParallelPort;
628 default:
629 return NULL;
630 }
631}
632
633/**
634 * Destruct a device instance.
635 *
636 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
637 * resources can be freed correctly.
638 *
639 * @returns VBox status.
640 * @param pDevIns The device instance data.
641 */
642static DECLCALLBACK(int) parallelDestruct(PPDMDEVINS pDevIns)
643{
644 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState *);
645
646 PDMR3CritSectDelete(&pData->CritSect);
647
648 return VINF_SUCCESS;
649}
650
651
652/**
653 * Construct a device instance for a VM.
654 *
655 * @returns VBox status.
656 * @param pDevIns The device instance data.
657 * If the registration structure is needed, pDevIns->pDevReg points to it.
658 * @param iInstance Instance number. Use this to figure out which registers and such to use.
659 * The device number is also found in pDevIns->iInstance, but since it's
660 * likely to be freqently used PDM passes it as parameter.
661 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
662 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
663 * iInstance it's expected to be used a bit in this function.
664 */
665static DECLCALLBACK(int) parallelConstruct(PPDMDEVINS pDevIns,
666 int iInstance,
667 PCFGMNODE pCfgHandle)
668{
669 int rc;
670 ParallelState *pData = PDMINS2DATA(pDevIns, ParallelState*);
671 uint16_t io_base;
672 uint8_t irq_lvl;
673
674 Assert(iInstance < 4);
675
676 pData->pDevInsHC = pDevIns;
677 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
678
679 /*
680 * Validate configuration.
681 */
682 if (!CFGMR3AreValuesValid(pCfgHandle, "IRQ\0IOBase\0"))
683 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
684 N_("Configuration error: Unknown config key"));
685
686 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &pData->fGCEnabled);
687 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
688 pData->fGCEnabled = true;
689 else if (VBOX_FAILURE(rc))
690 return PDMDEV_SET_ERROR(pDevIns, rc,
691 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
692
693 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &pData->fR0Enabled);
694 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
695 pData->fR0Enabled = true;
696 else if (VBOX_FAILURE(rc))
697 return PDMDEV_SET_ERROR(pDevIns, rc,
698 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
699
700 /* IBase */
701 pData->IBase.pfnQueryInterface = parallelQueryInterface;
702
703 /* IHostParallelPort */
704 pData->IHostParallelPort.pfnNotifyInterrupt = parallelNotifyInterrupt;
705
706 rc = RTSemEventCreate(&pData->ReceiveSem);
707 AssertRC(rc);
708
709 /*
710 * Initialize critical section.
711 * This must of course be done before attaching drivers or anything else which can call us back..
712 */
713 char szName[24];
714 RTStrPrintf(szName, sizeof(szName), "Parallel#%d", iInstance);
715 rc = PDMDevHlpCritSectInit(pDevIns, &pData->CritSect, szName);
716 if (VBOX_FAILURE(rc))
717 return rc;
718
719 rc = CFGMR3QueryU8(pCfgHandle, "IRQ", &irq_lvl);
720 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
721 irq_lvl = 7;
722 else if (VBOX_FAILURE(rc))
723 return PDMDEV_SET_ERROR(pDevIns, rc,
724 N_("Configuration error: Failed to get the \"IRQ\" value"));
725
726 rc = CFGMR3QueryU16(pCfgHandle, "IOBase", &io_base);
727 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
728 io_base = 0x378;
729 else if (VBOX_FAILURE(rc))
730 return PDMDEV_SET_ERROR(pDevIns, rc,
731 N_("Configuration error: Failed to get the \"IOBase\" value"));
732
733 Log(("parallelConstruct instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
734
735 pData->irq = irq_lvl;
736 pData->base = io_base;
737
738 /* Init parallel state */
739 pData->reg_data = 0;
740 pData->reg_ecp_ecr = LPT_ECP_ECR_CHIPMODE_COMPAT | LPT_ECP_ECR_FIFO_EMPTY;
741 pData->act_fifo_pos_read = 0;
742 pData->act_fifo_pos_write = 0;
743
744 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
745 parallelIOPortWrite, parallelIOPortRead,
746 NULL, NULL, "PARALLEL");
747 if (VBOX_FAILURE(rc))
748 return rc;
749
750#if 0
751 /* register ecp registers */
752 rc = PDMDevHlpIOPortRegister(pDevIns, io_base+0x400, 8, 0,
753 parallelIOPortWriteECP, parallelIOPortReadECP,
754 NULL, NULL, "PARALLEL ECP");
755 if (VBOX_FAILURE(rc))
756 return rc;
757#endif
758
759 if (pData->fGCEnabled)
760 {
761 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base, 8, 0, "parallelIOPortWrite",
762 "parallelIOPortRead", NULL, NULL, "Parallel");
763 if (VBOX_FAILURE(rc))
764 return rc;
765
766#if 0
767 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base+0x400, 8, 0, "parallelIOPortWriteECP",
768 "parallelIOPortReadECP", NULL, NULL, "Parallel Ecp");
769 if (VBOX_FAILURE(rc))
770 return rc;
771#endif
772 }
773
774 if (pData->fR0Enabled)
775 {
776 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "parallelIOPortWrite",
777 "parallelIOPortRead", NULL, NULL, "Parallel");
778 if (VBOX_FAILURE(rc))
779 return rc;
780
781#if 0
782 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base+0x400, 8, 0, "parallelIOPortWriteECP",
783 "parallelIOPortReadECP", NULL, NULL, "Parallel Ecp");
784 if (VBOX_FAILURE(rc))
785 return rc;
786#endif
787 }
788
789 /* Attach the parallel port driver and get the interfaces. For now no run-time
790 * changes are supported. */
791 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pData->IBase, &pData->pDrvBase, "Parallel Host");
792 if (VBOX_SUCCESS(rc))
793 {
794 pData->pDrvHostParallelConnector = (PDMIHOSTPARALLELCONNECTOR *)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase,
795 PDMINTERFACE_HOST_PARALLEL_CONNECTOR);
796 if (!pData->pDrvHostParallelConnector)
797 {
798 AssertMsgFailed(("Configuration error: instance %d has no host parallel interface!\n", iInstance));
799 return VERR_PDM_MISSING_INTERFACE;
800 }
801 /** @todo provide read notification interface!!!! */
802 }
803 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
804 {
805 pData->pDrvBase = NULL;
806 pData->pDrvHostParallelConnector = NULL;
807 LogRel(("Parallel%d: no unit\n", iInstance));
808 }
809 else
810 {
811 AssertMsgFailed(("Parallel%d: Failed to attach to host driver. rc=%Vrc\n", iInstance, rc));
812 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
813 N_("Parallel device %d cannot attach to host driver\n"), iInstance);
814 }
815
816 /** Set compatibility mode */
817 pData->pDrvHostParallelConnector->pfnSetMode(pData->pDrvHostParallelConnector, PDM_PARALLEL_PORT_MODE_COMPAT);
818 /** Get status of control register */
819 pData->pDrvHostParallelConnector->pfnReadControl(pData->pDrvHostParallelConnector, &pData->reg_control);
820
821 rc = PDMDevHlpSSMRegister(
822 pDevIns, /* pDevIns */
823 pDevIns->pDevReg->szDeviceName, /* pszName */
824 iInstance, /* u32Instance */
825 PARALLEL_SAVED_STATE_VERSION, /* u32Version */
826 sizeof (*pData), /* cbGuess */
827 NULL, /* pfnSavePrep */
828 parallelSaveExec, /* pfnSaveExec */
829 NULL, /* pfnSaveDone */
830 NULL, /* pfnLoadPrep */
831 parallelLoadExec, /* pfnLoadExec */
832 NULL /* pfnLoadDone */
833 );
834 if (VBOX_FAILURE(rc))
835 return rc;
836
837 return VINF_SUCCESS;
838}
839
840/**
841 * The device registration structure.
842 */
843const PDMDEVREG g_DeviceParallelPort =
844{
845 /* u32Version */
846 PDM_DEVREG_VERSION,
847 /* szDeviceName */
848 "parallel",
849 /* szGCMod */
850 "VBoxDDGC.gc",
851 /* szR0Mod */
852 "VBoxDDR0.r0",
853 /* pszDescription */
854 "Parallel Communication Port",
855 /* fFlags */
856 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
857 /* fClass */
858 PDM_DEVREG_CLASS_PARALLEL,
859 /* cMaxInstances */
860 1,
861 /* cbInstance */
862 sizeof(ParallelState),
863 /* pfnConstruct */
864 parallelConstruct,
865 /* pfnDestruct */
866 parallelDestruct,
867 /* pfnRelocate */
868 parallelRelocate,
869 /* pfnIOCtl */
870 NULL,
871 /* pfnPowerOn */
872 NULL,
873 /* pfnReset */
874 NULL,
875 /* pfnSuspend */
876 NULL,
877 /* pfnResume */
878 NULL,
879 /* pfnAttach */
880 NULL,
881 /* pfnDetach */
882 NULL,
883 /* pfnQueryInterface. */
884 NULL
885};
886#endif /* IN_RING3 */
887
888
889#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