VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DevSerial.cpp@ 1736

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

Property cleanup (and reexport serial device).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.9 KB
Line 
1/** @file
2 *
3 * VBox serial device:
4 * Serial communication port driver
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 * This code is based on:
25 *
26 * QEMU 16450 UART emulation
27 *
28 * Copyright (c) 2003-2004 Fabrice Bellard
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a copy
31 * of this software and associated documentation files (the "Software"), to deal
32 * in the Software without restriction, including without limitation the rights
33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 * copies of the Software, and to permit persons to whom the Software is
35 * furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46 * THE SOFTWARE.
47 *
48 */
49
50
51/*******************************************************************************
52* Header Files *
53*******************************************************************************/
54#define LOG_GROUP LOG_GROUP_DEV_SERIAL
55#include <VBox/pdm.h>
56#include <VBox/err.h>
57
58#include <VBox/log.h>
59#include <iprt/assert.h>
60#include <iprt/uuid.h>
61#include <iprt/string.h>
62#include <iprt/semaphore.h>
63#include <iprt/critsect.h>
64
65#include "Builtins.h"
66
67#undef VBOX_SERIAL_PCI /* The PCI variant has lots of problems: wrong IRQ line and wrong IO base assigned. */
68
69#ifdef VBOX_SERIAL_PCI
70#include <VBox/pci.h>
71#endif /* VBOX_SERIAL_PCI */
72
73#define SERIAL_SAVED_STATE_VERSION 2
74
75#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
76
77#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
78#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
79#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
80#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
81
82#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
83#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
84
85#define UART_IIR_MSI 0x00 /* Modem status interrupt */
86#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
87#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
88#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
89
90/*
91 * These are the definitions for the Modem Control Register
92 */
93#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
94#define UART_MCR_OUT2 0x08 /* Out2 complement */
95#define UART_MCR_OUT1 0x04 /* Out1 complement */
96#define UART_MCR_RTS 0x02 /* RTS complement */
97#define UART_MCR_DTR 0x01 /* DTR complement */
98
99/*
100 * These are the definitions for the Modem Status Register
101 */
102#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
103#define UART_MSR_RI 0x40 /* Ring Indicator */
104#define UART_MSR_DSR 0x20 /* Data Set Ready */
105#define UART_MSR_CTS 0x10 /* Clear to Send */
106#define UART_MSR_DDCD 0x08 /* Delta DCD */
107#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
108#define UART_MSR_DDSR 0x02 /* Delta DSR */
109#define UART_MSR_DCTS 0x01 /* Delta CTS */
110#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
111
112#define UART_LSR_TEMT 0x40 /* Transmitter empty */
113#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
114#define UART_LSR_BI 0x10 /* Break interrupt indicator */
115#define UART_LSR_FE 0x08 /* Frame error indicator */
116#define UART_LSR_PE 0x04 /* Parity error indicator */
117#define UART_LSR_OE 0x02 /* Overrun error indicator */
118#define UART_LSR_DR 0x01 /* Receiver data ready */
119
120struct SerialState
121{
122 /** Access critical section. */
123 PDMCRITSECT CritSect;
124
125 /** Pointer to the device instance. */
126 HCPTRTYPE(PPDMDEVINS) pDevInsHC;
127 /** The base interface. */
128 HCPTRTYPE(PDMIBASE) IBase;
129 /** The character port interface. */
130 HCPTRTYPE(PDMICHARPORT) ICharPort;
131 /** Pointer to the attached base driver. */
132 HCPTRTYPE(PPDMIBASE) pDrvBase;
133 /** Pointer to the attached character driver. */
134 HCPTRTYPE(PPDMICHAR) pDrvChar;
135
136 uint16_t divider;
137 uint8_t rbr; /* receive register */
138 uint8_t ier;
139 uint8_t iir; /* read only */
140 uint8_t lcr;
141 uint8_t mcr;
142 uint8_t lsr; /* read only */
143 uint8_t msr; /* read only */
144 uint8_t scr;
145 /* NOTE: this hidden state is necessary for tx irq generation as
146 it can be reset while reading iir */
147 int thr_ipending;
148 int irq;
149
150 bool fGCEnabled;
151 bool fR0Enabled;
152
153 RTSEMEVENT ReceiveSem;
154 int last_break_enable;
155 uint32_t base;
156
157#ifdef VBOX_SERIAL_PCI
158 PCIDEVICE dev;
159#endif /* VBOX_SERIAL_PCI */
160 /** Pointer to the device instance. */
161 GCPTRTYPE(PPDMDEVINS) pDevInsGC;
162};
163
164#ifndef VBOX_DEVICE_STRUCT_TESTCASE
165
166
167#ifdef VBOX_SERIAL_PCI
168#define PCIDEV_2_SERIALSTATE(pPciDev) ( (SerialState *)((uintptr_t)(pPciDev) - RT_OFFSETOF(SerialState, dev)) )
169#endif /* VBOX_SERIAL_PCI */
170#define PDMIBASE_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, IBase)) )
171#define PDMICHARPORT_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, ICharPort)) )
172
173
174__BEGIN_DECLS
175PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
176PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
177__END_DECLS
178
179
180static void serial_update_irq(SerialState *s)
181{
182 if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
183 s->iir = UART_IIR_RDI;
184 } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
185 s->iir = UART_IIR_THRI;
186 } else {
187 s->iir = UART_IIR_NO_INT;
188 }
189 if (s->iir != UART_IIR_NO_INT) {
190#ifdef VBOX_SERIAL_PCI
191 PDMDevHlpPCISetIrqNoWait(CTXSUFF(s->pDevIns), 0, 1);
192#else /* !VBOX_SERIAL_PCI */
193 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 1);
194#endif /* !VBOX_SERIAL_PCI */
195 } else {
196#ifdef VBOX_SERIAL_PCI
197 PDMDevHlpPCISetIrqNoWait(CTXSUFF(s->pDevIns), 0, 0);
198#else /* !VBOX_SERIAL_PCI */
199 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 0);
200#endif /* !VBOX_SERIAL_PCI */
201 }
202}
203
204static void serial_update_parameters(SerialState *s)
205{
206 int speed, parity, data_bits, stop_bits;
207
208 if (s->lcr & 0x08) {
209 if (s->lcr & 0x10)
210 parity = 'E';
211 else
212 parity = 'O';
213 } else {
214 parity = 'N';
215 }
216 if (s->lcr & 0x04)
217 stop_bits = 2;
218 else
219 stop_bits = 1;
220 data_bits = (s->lcr & 0x03) + 5;
221 if (s->divider == 0)
222 return;
223 speed = 115200 / s->divider;
224 Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
225}
226
227static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
228{
229 SerialState *s = (SerialState *)opaque;
230 unsigned char ch;
231
232 addr &= 7;
233 LogFlow(("serial: write addr=0x%02x val=0x%02x\n", addr, val));
234 switch(addr) {
235 default:
236 case 0:
237 if (s->lcr & UART_LCR_DLAB) {
238 s->divider = (s->divider & 0xff00) | val;
239 serial_update_parameters(s);
240 } else {
241#ifndef IN_RING3
242 NOREF(ch);
243 return VINF_IOM_HC_IOPORT_WRITE;
244#else
245 s->thr_ipending = 0;
246 s->lsr &= ~UART_LSR_THRE;
247 serial_update_irq(s);
248 ch = val;
249 /** @todo implement backpressure for writing (don't set interrupt
250 * bits/line until the character is actually written). This way
251 * EMT wouldn't block for writes taking longer than normal. */
252 if (s->pDrvChar)
253 {
254 int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
255 AssertRC(rc);
256 }
257 s->thr_ipending = 1;
258 s->lsr |= UART_LSR_THRE;
259 s->lsr |= UART_LSR_TEMT;
260 serial_update_irq(s);
261#endif
262 }
263 break;
264 case 1:
265 if (s->lcr & UART_LCR_DLAB) {
266 s->divider = (s->divider & 0x00ff) | (val << 8);
267 serial_update_parameters(s);
268 } else {
269 s->ier = val & 0x0f;
270 if (s->lsr & UART_LSR_THRE) {
271 s->thr_ipending = 1;
272 }
273 serial_update_irq(s);
274 }
275 break;
276 case 2:
277 break;
278 case 3:
279 {
280 int break_enable;
281 s->lcr = val;
282 serial_update_parameters(s);
283 break_enable = (val >> 6) & 1;
284 if (break_enable != s->last_break_enable) {
285 s->last_break_enable = break_enable;
286 }
287 }
288 break;
289 case 4:
290 s->mcr = val & 0x1f;
291 break;
292 case 5:
293 break;
294 case 6:
295 break;
296 case 7:
297 s->scr = val;
298 break;
299 }
300 return VINF_SUCCESS;
301}
302
303static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
304{
305 SerialState *s = (SerialState *)opaque;
306 uint32_t ret = ~0U;
307
308 *pRC = VINF_SUCCESS;
309
310 addr &= 7;
311 switch(addr) {
312 default:
313 case 0:
314 if (s->lcr & UART_LCR_DLAB) {
315 ret = s->divider & 0xff;
316 } else {
317#ifndef IN_RING3
318 *pRC = VINF_IOM_HC_IOPORT_READ;
319#else
320 ret = s->rbr;
321 s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
322 serial_update_irq(s);
323 {
324 int rc = RTSemEventSignal(s->ReceiveSem);
325 AssertRC(rc);
326 }
327#endif
328 }
329 break;
330 case 1:
331 if (s->lcr & UART_LCR_DLAB) {
332 ret = (s->divider >> 8) & 0xff;
333 } else {
334 ret = s->ier;
335 }
336 break;
337 case 2:
338 ret = s->iir;
339 /* reset THR pending bit */
340 if ((ret & 0x7) == UART_IIR_THRI)
341 s->thr_ipending = 0;
342 serial_update_irq(s);
343 break;
344 case 3:
345 ret = s->lcr;
346 break;
347 case 4:
348 ret = s->mcr;
349 break;
350 case 5:
351 ret = s->lsr;
352 break;
353 case 6:
354 if (s->mcr & UART_MCR_LOOP) {
355 /* in loopback, the modem output pins are connected to the
356 inputs */
357 ret = (s->mcr & 0x0c) << 4;
358 ret |= (s->mcr & 0x02) << 3;
359 ret |= (s->mcr & 0x01) << 5;
360 } else {
361 ret = s->msr;
362 }
363 break;
364 case 7:
365 ret = s->scr;
366 break;
367 }
368 LogFlow(("serial: read addr=0x%02x val=0x%02x\n", addr, ret));
369 return ret;
370}
371
372#ifdef IN_RING3
373static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
374{
375 SerialState *pData = PDMICHARPORT_2_SERIALSTATE(pInterface);
376 int rc;
377
378 Assert(*pcbRead != 0);
379
380 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
381 if (pData->lsr & UART_LSR_DR)
382 {
383 /* If a character is still in the read queue, then wait for it to be emptied. */
384 PDMCritSectLeave(&pData->CritSect);
385 rc = RTSemEventWait(pData->ReceiveSem, 250);
386 if (VBOX_FAILURE(rc))
387 return rc;
388
389 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
390 }
391
392 Assert(!(pData->lsr & UART_LSR_DR));
393 pData->rbr = *(const char *)pvBuf;
394 pData->lsr |= UART_LSR_DR;
395 serial_update_irq(pData);
396 PDMCritSectLeave(&pData->CritSect);
397
398 *pcbRead = 1;
399 return VINF_SUCCESS;
400}
401#endif /* IN_RING3 */
402
403/**
404 * Port I/O Handler for OUT operations.
405 *
406 * @returns VBox status code.
407 *
408 * @param pDevIns The device instance.
409 * @param pvUser User argument.
410 * @param Port Port number used for the IN operation.
411 * @param u32 The value to output.
412 * @param cb The value size in bytes.
413 */
414PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
415 RTIOPORT Port, uint32_t u32, unsigned cb)
416{
417 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
418 int rc = VINF_SUCCESS;
419
420 if (cb == 1)
421 {
422 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
423 if (rc == VINF_SUCCESS)
424 {
425 Log(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
426 rc = serial_ioport_write (pData, Port, u32);
427 PDMCritSectLeave(&pData->CritSect);
428 }
429 }
430 else
431 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
432
433 return rc;
434}
435
436/**
437 * Port I/O Handler for IN operations.
438 *
439 * @returns VBox status code.
440 *
441 * @param pDevIns The device instance.
442 * @param pvUser User argument.
443 * @param Port Port number used for the IN operation.
444 * @param u32 The value to output.
445 * @param cb The value size in bytes.
446 */
447PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
448 RTIOPORT Port, uint32_t *pu32, unsigned cb)
449{
450 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
451 int rc = VINF_SUCCESS;
452
453 if (cb == 1)
454 {
455 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
456 if (rc == VINF_SUCCESS)
457 {
458 Log(("%s: port %#06x\n", __FUNCTION__, Port));
459 *pu32 = serial_ioport_read (pData, Port, &rc);
460 Log(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
461 PDMCritSectLeave(&pData->CritSect);
462 }
463 }
464 else
465 rc = VERR_IOM_IOPORT_UNUSED;
466
467 return rc;
468}
469
470#ifdef IN_RING3
471/**
472 * Saves a state of the serial port device.
473 *
474 * @returns VBox status code.
475 * @param pDevIns The device instance.
476 * @param pSSMHandle The handle to save the state to.
477 */
478static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns,
479 PSSMHANDLE pSSMHandle)
480{
481 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
482
483 SSMR3PutU16(pSSMHandle, pData->divider);
484 SSMR3PutU8(pSSMHandle, pData->rbr);
485 SSMR3PutU8(pSSMHandle, pData->ier);
486 SSMR3PutU8(pSSMHandle, pData->lcr);
487 SSMR3PutU8(pSSMHandle, pData->mcr);
488 SSMR3PutU8(pSSMHandle, pData->lsr);
489 SSMR3PutU8(pSSMHandle, pData->msr);
490 SSMR3PutU8(pSSMHandle, pData->scr);
491 SSMR3PutS32(pSSMHandle, pData->thr_ipending);
492 SSMR3PutS32(pSSMHandle, pData->irq);
493 SSMR3PutS32(pSSMHandle, pData->last_break_enable);
494 SSMR3PutU32(pSSMHandle, pData->base);
495 return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
496}
497
498/**
499 * Loads a saved serial port device state.
500 *
501 * @returns VBox status code.
502 * @param pDevIns The device instance.
503 * @param pSSMHandle The handle to the saved state.
504 * @param u32Version The data unit version number.
505 */
506static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
507 PSSMHANDLE pSSMHandle,
508 uint32_t u32Version)
509{
510 int rc;
511 uint32_t u32;
512 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
513
514 if (u32Version != SERIAL_SAVED_STATE_VERSION)
515 {
516 AssertMsgFailed(("u32Version=%d\n", u32Version));
517 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
518 }
519
520 SSMR3GetU16(pSSMHandle, &pData->divider);
521 SSMR3GetU8(pSSMHandle, &pData->rbr);
522 SSMR3GetU8(pSSMHandle, &pData->ier);
523 SSMR3GetU8(pSSMHandle, &pData->lcr);
524 SSMR3GetU8(pSSMHandle, &pData->mcr);
525 SSMR3GetU8(pSSMHandle, &pData->lsr);
526 SSMR3GetU8(pSSMHandle, &pData->msr);
527 SSMR3GetU8(pSSMHandle, &pData->scr);
528 SSMR3GetS32(pSSMHandle, &pData->thr_ipending);
529 SSMR3GetS32(pSSMHandle, &pData->irq);
530 SSMR3GetS32(pSSMHandle, &pData->last_break_enable);
531 SSMR3GetU32(pSSMHandle, &pData->base);
532
533 rc = SSMR3GetU32(pSSMHandle, &u32);
534 if (VBOX_FAILURE(rc))
535 return rc;
536
537 if (u32 != ~0U)
538 {
539 AssertMsgFailed(("u32=%#x expected ~0\n", u32));
540 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
541 }
542 /* Be careful with pointers in the structure; they are not preserved
543 * in the saved state. */
544
545 if (pData->lsr & UART_LSR_DR)
546 {
547 int rc = RTSemEventSignal(pData->ReceiveSem);
548 AssertRC(rc);
549 }
550 pData->pDevInsHC = pDevIns;
551 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
552 return VINF_SUCCESS;
553}
554
555
556/**
557 * @copydoc FNPDMDEVRELOCATE
558 */
559static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
560{
561 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
562 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
563}
564
565#ifdef VBOX_SERIAL_PCI
566
567static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned */ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
568{
569 SerialState *pData = PCIDEV_2_SERIALSTATE(pPciDev);
570 int rc = VINF_SUCCESS;
571
572 Assert(enmType == PCI_ADDRESS_SPACE_IO);
573 Assert(iRegion == 0);
574 Assert(cb == 8);
575 AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
576
577 pData->base = (RTIOPORT)GCPhysAddress;
578 LogRel(("Serial#%d: mapping I/O at %#06x\n", pData->pDevIns->iInstance, pData->base));
579
580 /*
581 * Register our port IO handlers.
582 */
583 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pData,
584 serial_io_write, serial_io_read, NULL, NULL, "SERIAL");
585 AssertRC(rc);
586 return rc;
587}
588
589#endif /* VBOX_SERIAL_PCI */
590
591
592/** @copyfrom PIBASE::pfnqueryInterface */
593static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
594{
595 SerialState *pData = PDMIBASE_2_SERIALSTATE(pInterface);
596 switch (enmInterface)
597 {
598 case PDMINTERFACE_BASE:
599 return &pData->IBase;
600 case PDMINTERFACE_CHAR_PORT:
601 return &pData->ICharPort;
602 default:
603 return NULL;
604 }
605}
606
607
608/**
609 * Construct a device instance for a VM.
610 *
611 * @returns VBox status.
612 * @param pDevIns The device instance data.
613 * If the registration structure is needed, pDevIns->pDevReg points to it.
614 * @param iInstance Instance number. Use this to figure out which registers and such to use.
615 * The device number is also found in pDevIns->iInstance, but since it's
616 * likely to be freqently used PDM passes it as parameter.
617 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
618 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
619 * iInstance it's expected to be used a bit in this function.
620 */
621static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
622 int iInstance,
623 PCFGMNODE pCfgHandle)
624{
625 int rc;
626 SerialState *pData = PDMINS2DATA(pDevIns, SerialState*);
627 uint16_t io_base;
628 uint8_t irq_lvl;
629
630 Assert(iInstance < 4);
631
632 pData->pDevInsHC = pDevIns;
633 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
634
635 /*
636 * Validate configuration.
637 */
638 if (!CFGMR3AreValuesValid(pCfgHandle, "IRQ\0IOBase\0"))
639 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
640
641 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &pData->fGCEnabled);
642 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
643 pData->fGCEnabled = true;
644 else if (VBOX_FAILURE(rc))
645 return PDMDEV_SET_ERROR(pDevIns, rc,
646 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
647
648 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &pData->fR0Enabled);
649 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
650 pData->fR0Enabled = true;
651 else if (VBOX_FAILURE(rc))
652 return PDMDEV_SET_ERROR(pDevIns, rc,
653 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
654
655 /* IBase */
656 pData->IBase.pfnQueryInterface = serialQueryInterface;
657
658 /* ICharPort */
659 pData->ICharPort.pfnNotifyRead = serialNotifyRead;
660
661 rc = RTSemEventCreate(&pData->ReceiveSem);
662 AssertRC(rc);
663
664 /*
665 * Initialize critical section.
666 * This must of course be done before attaching drivers or anything else which can call us back..
667 */
668 char szName[24];
669 RTStrPrintf(szName, sizeof(szName), "Serial#%d", iInstance);
670 rc = PDMDevHlpCritSectInit(pDevIns, &pData->CritSect, szName);
671 if (VBOX_FAILURE(rc))
672 return rc;
673
674/** @todo r=bird: Check for VERR_CFGM_VALUE_NOT_FOUND and provide sensible defaults.
675 * Also do AssertMsgFailed(("Configuration error:....)) in the failure cases of CFGMR3Query*()
676 * and CFGR3AreValuesValid() like we're doing in the other devices. */
677 rc = CFGMR3QueryU8 (pCfgHandle, "IRQ", &irq_lvl);
678 if (VBOX_FAILURE (rc))
679 return rc;
680
681 rc = CFGMR3QueryU16 (pCfgHandle, "IOBase", &io_base);
682 if (VBOX_FAILURE (rc))
683 return rc;
684
685 Log(("serialConstruct instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
686
687 pData->irq = irq_lvl;
688 pData->lsr = UART_LSR_TEMT | UART_LSR_THRE;
689 pData->iir = UART_IIR_NO_INT;
690 pData->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
691#ifdef VBOX_SERIAL_PCI
692 pData->base = -1;
693 pData->dev.config[0x00] = 0xee; /* Vendor: ??? */
694 pData->dev.config[0x01] = 0x80;
695 pData->dev.config[0x02] = 0x01; /* Device: ??? */
696 pData->dev.config[0x03] = 0x01;
697 pData->dev.config[0x04] = PCI_COMMAND_IOACCESS;
698 pData->dev.config[0x09] = 0x01; /* Programming interface: 16450 */
699 pData->dev.config[0x0a] = 0x00; /* Subclass: Serial controller */
700 pData->dev.config[0x0b] = 0x07; /* Class: Communication controller */
701 pData->dev.config[0x0e] = 0x00; /* Header type: standard */
702 pData->dev.config[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/
703 pData->dev.config[0x3d] = 1; /* interrupt pin 0 */
704 rc = PDMDevHlpPCIRegister(pDevIns, &pData->dev);
705 if (VBOX_FAILURE(rc))
706 return rc;
707 /*
708 * Register the PCI I/O ports.
709 */
710 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap);
711 if (VBOX_FAILURE(rc))
712 return rc;
713#else /* !VBOX_SERIAL_PCI */
714 pData->base = io_base;
715 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
716 serialIOPortWrite, serialIOPortRead,
717 NULL, NULL, "SERIAL");
718 if (VBOX_FAILURE (rc))
719 return rc;
720
721 if (pData->fGCEnabled)
722 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base, 8, 0, "serialIOPortWrite",
723 "serialIOPortRead", NULL, NULL, "Serial");
724
725 if (pData->fR0Enabled)
726 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
727 "serialIOPortRead", NULL, NULL, "Serial");
728
729#endif /* !VBOX_SERIAL_PCI */
730
731 /* Attach the char driver and get the interfaces. For now no run-time
732 * changes are supported. */
733 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pData->IBase, &pData->pDrvBase, "Serial Char");
734 if (VBOX_SUCCESS(rc))
735 {
736 pData->pDrvChar = (PDMICHAR *)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_CHAR);
737 if (!pData->pDrvChar)
738 {
739 AssertMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
740 return VERR_PDM_MISSING_INTERFACE;
741 }
742 /** @todo provide read notification interface!!!! */
743 }
744 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
745 {
746 pData->pDrvBase = NULL;
747 pData->pDrvChar = NULL;
748 LogRel(("Serial%d: no unit\n", iInstance));
749 }
750 else
751 {
752 AssertMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Vrc\n", iInstance, rc));
753 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
754 N_("Serial device %d cannot attach to char driver\n"), iInstance);
755 }
756
757 rc = PDMDevHlpSSMRegister (
758 pDevIns, /* pDevIns */
759 pDevIns->pDevReg->szDeviceName, /* pszName */
760 iInstance, /* u32Instance */
761 SERIAL_SAVED_STATE_VERSION, /* u32Version */
762 sizeof (*pData), /* cbGuess */
763 NULL, /* pfnSavePrep */
764 serialSaveExec, /* pfnSaveExec */
765 NULL, /* pfnSaveDone */
766 NULL, /* pfnLoadPrep */
767 serialLoadExec, /* pfnLoadExec */
768 NULL /* pfnLoadDone */
769 );
770 if (VBOX_FAILURE(rc))
771 return rc;
772
773 return VINF_SUCCESS;
774}
775
776/**
777 * The device registration structure.
778 */
779const PDMDEVREG g_DeviceSerialPort =
780{
781 /* u32Version */
782 PDM_DEVREG_VERSION,
783 /* szDeviceName */
784 "serial",
785 /* szGCMod */
786 "VBoxDDGC.gc",
787 /* szR0Mod */
788 "VBoxDDR0.r0",
789 /* pszDescription */
790 "Serial Communication Port",
791 /* fFlags */
792 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
793 /* fClass */
794 PDM_DEVREG_CLASS_SERIAL,
795 /* cMaxInstances */
796 1,
797 /* cbInstance */
798 sizeof(SerialState),
799 /* pfnConstruct */
800 serialConstruct,
801 /* pfnDestruct */
802 NULL,
803 /* pfnRelocate */
804 serialRelocate,
805 /* pfnIOCtl */
806 NULL,
807 /* pfnPowerOn */
808 NULL,
809 /* pfnReset */
810 NULL,
811 /* pfnSuspend */
812 NULL,
813 /* pfnResume */
814 NULL,
815 /* pfnAttach */
816 NULL,
817 /* pfnDetach */
818 NULL,
819 /* pfnQueryInterface. */
820 NULL
821};
822#endif /* IN_RING3 */
823
824
825#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