VirtualBox

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

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

alignment

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 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 /** Pointer to the device instance. */
128 GCPTRTYPE(PPDMDEVINS) pDevInsGC;
129#if HC_ARCH_BITS == 64 && GC_ARCH_BITS != 64
130 RTGCPTR Alignment0;
131#endif
132 /** The base interface. */
133 HCPTRTYPE(PDMIBASE) IBase;
134 /** The character port interface. */
135 HCPTRTYPE(PDMICHARPORT) ICharPort;
136 /** Pointer to the attached base driver. */
137 HCPTRTYPE(PPDMIBASE) pDrvBase;
138 /** Pointer to the attached character driver. */
139 HCPTRTYPE(PPDMICHAR) pDrvChar;
140
141 uint16_t divider;
142 uint16_t afAlignment[3];
143 uint8_t rbr; /* receive register */
144 uint8_t ier;
145 uint8_t iir; /* read only */
146 uint8_t lcr;
147 uint8_t mcr;
148 uint8_t lsr; /* read only */
149 uint8_t msr; /* read only */
150 uint8_t scr;
151 /* NOTE: this hidden state is necessary for tx irq generation as
152 it can be reset while reading iir */
153 int thr_ipending;
154 int irq;
155
156 bool fGCEnabled;
157 bool fR0Enabled;
158 bool afAlignment[6];
159
160 RTSEMEVENT ReceiveSem;
161 int last_break_enable;
162 uint32_t base;
163
164#ifdef VBOX_SERIAL_PCI
165 PCIDEVICE dev;
166#endif /* VBOX_SERIAL_PCI */
167};
168
169#ifndef VBOX_DEVICE_STRUCT_TESTCASE
170
171
172#ifdef VBOX_SERIAL_PCI
173#define PCIDEV_2_SERIALSTATE(pPciDev) ( (SerialState *)((uintptr_t)(pPciDev) - RT_OFFSETOF(SerialState, dev)) )
174#endif /* VBOX_SERIAL_PCI */
175#define PDMIBASE_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, IBase)) )
176#define PDMICHARPORT_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, ICharPort)) )
177
178
179__BEGIN_DECLS
180PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
181PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
182__END_DECLS
183
184
185static void serial_update_irq(SerialState *s)
186{
187 if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
188 s->iir = UART_IIR_RDI;
189 } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
190 s->iir = UART_IIR_THRI;
191 } else {
192 s->iir = UART_IIR_NO_INT;
193 }
194 if (s->iir != UART_IIR_NO_INT) {
195#ifdef VBOX_SERIAL_PCI
196 PDMDevHlpPCISetIrqNoWait(CTXSUFF(s->pDevIns), 0, 1);
197#else /* !VBOX_SERIAL_PCI */
198 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 1);
199#endif /* !VBOX_SERIAL_PCI */
200 } else {
201#ifdef VBOX_SERIAL_PCI
202 PDMDevHlpPCISetIrqNoWait(CTXSUFF(s->pDevIns), 0, 0);
203#else /* !VBOX_SERIAL_PCI */
204 PDMDevHlpISASetIrqNoWait(CTXSUFF(s->pDevIns), s->irq, 0);
205#endif /* !VBOX_SERIAL_PCI */
206 }
207}
208
209static void serial_update_parameters(SerialState *s)
210{
211 int speed, parity, data_bits, stop_bits;
212
213 if (s->lcr & 0x08) {
214 if (s->lcr & 0x10)
215 parity = 'E';
216 else
217 parity = 'O';
218 } else {
219 parity = 'N';
220 }
221 if (s->lcr & 0x04)
222 stop_bits = 2;
223 else
224 stop_bits = 1;
225 data_bits = (s->lcr & 0x03) + 5;
226 if (s->divider == 0)
227 return;
228 speed = 115200 / s->divider;
229 Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
230}
231
232static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
233{
234 SerialState *s = (SerialState *)opaque;
235 unsigned char ch;
236
237 addr &= 7;
238 LogFlow(("serial: write addr=0x%02x val=0x%02x\n", addr, val));
239 switch(addr) {
240 default:
241 case 0:
242 if (s->lcr & UART_LCR_DLAB) {
243 s->divider = (s->divider & 0xff00) | val;
244 serial_update_parameters(s);
245 } else {
246#ifndef IN_RING3
247 NOREF(ch);
248 return VINF_IOM_HC_IOPORT_WRITE;
249#else
250 s->thr_ipending = 0;
251 s->lsr &= ~UART_LSR_THRE;
252 serial_update_irq(s);
253 ch = val;
254 /** @todo implement backpressure for writing (don't set interrupt
255 * bits/line until the character is actually written). This way
256 * EMT wouldn't block for writes taking longer than normal. */
257 if (s->pDrvChar)
258 {
259 int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
260 AssertRC(rc);
261 }
262 s->thr_ipending = 1;
263 s->lsr |= UART_LSR_THRE;
264 s->lsr |= UART_LSR_TEMT;
265 serial_update_irq(s);
266#endif
267 }
268 break;
269 case 1:
270 if (s->lcr & UART_LCR_DLAB) {
271 s->divider = (s->divider & 0x00ff) | (val << 8);
272 serial_update_parameters(s);
273 } else {
274 s->ier = val & 0x0f;
275 if (s->lsr & UART_LSR_THRE) {
276 s->thr_ipending = 1;
277 }
278 serial_update_irq(s);
279 }
280 break;
281 case 2:
282 break;
283 case 3:
284 {
285 int break_enable;
286 s->lcr = val;
287 serial_update_parameters(s);
288 break_enable = (val >> 6) & 1;
289 if (break_enable != s->last_break_enable) {
290 s->last_break_enable = break_enable;
291 }
292 }
293 break;
294 case 4:
295 s->mcr = val & 0x1f;
296 break;
297 case 5:
298 break;
299 case 6:
300 break;
301 case 7:
302 s->scr = val;
303 break;
304 }
305 return VINF_SUCCESS;
306}
307
308static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
309{
310 SerialState *s = (SerialState *)opaque;
311 uint32_t ret = ~0U;
312
313 *pRC = VINF_SUCCESS;
314
315 addr &= 7;
316 switch(addr) {
317 default:
318 case 0:
319 if (s->lcr & UART_LCR_DLAB) {
320 ret = s->divider & 0xff;
321 } else {
322#ifndef IN_RING3
323 *pRC = VINF_IOM_HC_IOPORT_READ;
324#else
325 ret = s->rbr;
326 s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
327 serial_update_irq(s);
328 {
329 int rc = RTSemEventSignal(s->ReceiveSem);
330 AssertRC(rc);
331 }
332#endif
333 }
334 break;
335 case 1:
336 if (s->lcr & UART_LCR_DLAB) {
337 ret = (s->divider >> 8) & 0xff;
338 } else {
339 ret = s->ier;
340 }
341 break;
342 case 2:
343 ret = s->iir;
344 /* reset THR pending bit */
345 if ((ret & 0x7) == UART_IIR_THRI)
346 s->thr_ipending = 0;
347 serial_update_irq(s);
348 break;
349 case 3:
350 ret = s->lcr;
351 break;
352 case 4:
353 ret = s->mcr;
354 break;
355 case 5:
356 ret = s->lsr;
357 break;
358 case 6:
359 if (s->mcr & UART_MCR_LOOP) {
360 /* in loopback, the modem output pins are connected to the
361 inputs */
362 ret = (s->mcr & 0x0c) << 4;
363 ret |= (s->mcr & 0x02) << 3;
364 ret |= (s->mcr & 0x01) << 5;
365 } else {
366 ret = s->msr;
367 }
368 break;
369 case 7:
370 ret = s->scr;
371 break;
372 }
373 LogFlow(("serial: read addr=0x%02x val=0x%02x\n", addr, ret));
374 return ret;
375}
376
377#ifdef IN_RING3
378static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
379{
380 SerialState *pData = PDMICHARPORT_2_SERIALSTATE(pInterface);
381 int rc;
382
383 Assert(*pcbRead != 0);
384
385 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
386 if (pData->lsr & UART_LSR_DR)
387 {
388 /* If a character is still in the read queue, then wait for it to be emptied. */
389 PDMCritSectLeave(&pData->CritSect);
390 rc = RTSemEventWait(pData->ReceiveSem, 250);
391 if (VBOX_FAILURE(rc))
392 return rc;
393
394 PDMCritSectEnter(&pData->CritSect, VERR_PERMISSION_DENIED);
395 }
396
397 Assert(!(pData->lsr & UART_LSR_DR));
398 pData->rbr = *(const char *)pvBuf;
399 pData->lsr |= UART_LSR_DR;
400 serial_update_irq(pData);
401 PDMCritSectLeave(&pData->CritSect);
402
403 *pcbRead = 1;
404 return VINF_SUCCESS;
405}
406#endif /* IN_RING3 */
407
408/**
409 * Port I/O Handler for OUT operations.
410 *
411 * @returns VBox status code.
412 *
413 * @param pDevIns The device instance.
414 * @param pvUser User argument.
415 * @param Port Port number used for the IN operation.
416 * @param u32 The value to output.
417 * @param cb The value size in bytes.
418 */
419PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
420 RTIOPORT Port, uint32_t u32, unsigned cb)
421{
422 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
423 int rc = VINF_SUCCESS;
424
425 if (cb == 1)
426 {
427 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_WRITE);
428 if (rc == VINF_SUCCESS)
429 {
430 Log(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
431 rc = serial_ioport_write (pData, Port, u32);
432 PDMCritSectLeave(&pData->CritSect);
433 }
434 }
435 else
436 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
437
438 return rc;
439}
440
441/**
442 * Port I/O Handler for IN operations.
443 *
444 * @returns VBox status code.
445 *
446 * @param pDevIns The device instance.
447 * @param pvUser User argument.
448 * @param Port Port number used for the IN operation.
449 * @param u32 The value to output.
450 * @param cb The value size in bytes.
451 */
452PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
453 RTIOPORT Port, uint32_t *pu32, unsigned cb)
454{
455 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
456 int rc = VINF_SUCCESS;
457
458 if (cb == 1)
459 {
460 rc = PDMCritSectEnter(&pData->CritSect, VINF_IOM_HC_IOPORT_READ);
461 if (rc == VINF_SUCCESS)
462 {
463 Log(("%s: port %#06x\n", __FUNCTION__, Port));
464 *pu32 = serial_ioport_read (pData, Port, &rc);
465 Log(("%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#ifdef IN_RING3
476/**
477 * Saves a state of the serial port device.
478 *
479 * @returns VBox status code.
480 * @param pDevIns The device instance.
481 * @param pSSMHandle The handle to save the state to.
482 */
483static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns,
484 PSSMHANDLE pSSMHandle)
485{
486 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
487
488 SSMR3PutU16(pSSMHandle, pData->divider);
489 SSMR3PutU8(pSSMHandle, pData->rbr);
490 SSMR3PutU8(pSSMHandle, pData->ier);
491 SSMR3PutU8(pSSMHandle, pData->lcr);
492 SSMR3PutU8(pSSMHandle, pData->mcr);
493 SSMR3PutU8(pSSMHandle, pData->lsr);
494 SSMR3PutU8(pSSMHandle, pData->msr);
495 SSMR3PutU8(pSSMHandle, pData->scr);
496 SSMR3PutS32(pSSMHandle, pData->thr_ipending);
497 SSMR3PutS32(pSSMHandle, pData->irq);
498 SSMR3PutS32(pSSMHandle, pData->last_break_enable);
499 SSMR3PutU32(pSSMHandle, pData->base);
500 return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
501}
502
503/**
504 * Loads a saved serial port device state.
505 *
506 * @returns VBox status code.
507 * @param pDevIns The device instance.
508 * @param pSSMHandle The handle to the saved state.
509 * @param u32Version The data unit version number.
510 */
511static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
512 PSSMHANDLE pSSMHandle,
513 uint32_t u32Version)
514{
515 int rc;
516 uint32_t u32;
517 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
518
519 if (u32Version != SERIAL_SAVED_STATE_VERSION)
520 {
521 AssertMsgFailed(("u32Version=%d\n", u32Version));
522 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
523 }
524
525 SSMR3GetU16(pSSMHandle, &pData->divider);
526 SSMR3GetU8(pSSMHandle, &pData->rbr);
527 SSMR3GetU8(pSSMHandle, &pData->ier);
528 SSMR3GetU8(pSSMHandle, &pData->lcr);
529 SSMR3GetU8(pSSMHandle, &pData->mcr);
530 SSMR3GetU8(pSSMHandle, &pData->lsr);
531 SSMR3GetU8(pSSMHandle, &pData->msr);
532 SSMR3GetU8(pSSMHandle, &pData->scr);
533 SSMR3GetS32(pSSMHandle, &pData->thr_ipending);
534 SSMR3GetS32(pSSMHandle, &pData->irq);
535 SSMR3GetS32(pSSMHandle, &pData->last_break_enable);
536 SSMR3GetU32(pSSMHandle, &pData->base);
537
538 rc = SSMR3GetU32(pSSMHandle, &u32);
539 if (VBOX_FAILURE(rc))
540 return rc;
541
542 if (u32 != ~0U)
543 {
544 AssertMsgFailed(("u32=%#x expected ~0\n", u32));
545 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
546 }
547 /* Be careful with pointers in the structure; they are not preserved
548 * in the saved state. */
549
550 if (pData->lsr & UART_LSR_DR)
551 {
552 int rc = RTSemEventSignal(pData->ReceiveSem);
553 AssertRC(rc);
554 }
555 pData->pDevInsHC = pDevIns;
556 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
557 return VINF_SUCCESS;
558}
559
560
561/**
562 * @copydoc FNPDMDEVRELOCATE
563 */
564static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
565{
566 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
567 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
568}
569
570#ifdef VBOX_SERIAL_PCI
571
572static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned */ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
573{
574 SerialState *pData = PCIDEV_2_SERIALSTATE(pPciDev);
575 int rc = VINF_SUCCESS;
576
577 Assert(enmType == PCI_ADDRESS_SPACE_IO);
578 Assert(iRegion == 0);
579 Assert(cb == 8);
580 AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
581
582 pData->base = (RTIOPORT)GCPhysAddress;
583 LogRel(("Serial#%d: mapping I/O at %#06x\n", pData->pDevIns->iInstance, pData->base));
584
585 /*
586 * Register our port IO handlers.
587 */
588 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pData,
589 serial_io_write, serial_io_read, NULL, NULL, "SERIAL");
590 AssertRC(rc);
591 return rc;
592}
593
594#endif /* VBOX_SERIAL_PCI */
595
596
597/** @copyfrom PIBASE::pfnqueryInterface */
598static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
599{
600 SerialState *pData = PDMIBASE_2_SERIALSTATE(pInterface);
601 switch (enmInterface)
602 {
603 case PDMINTERFACE_BASE:
604 return &pData->IBase;
605 case PDMINTERFACE_CHAR_PORT:
606 return &pData->ICharPort;
607 default:
608 return NULL;
609 }
610}
611
612/**
613 * Destruct a device instance.
614 *
615 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
616 * resources can be freed correctly.
617 *
618 * @returns VBox status.
619 * @param pDevIns The device instance data.
620 */
621static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns)
622{
623 SerialState *pData = PDMINS2DATA(pDevIns, SerialState *);
624
625 RTSemEventDestroy(pData->ReceiveSem);
626 pData->ReceiveSem = NIL_RTSEMEVENT;
627
628 PDMR3CritSectDelete(&pData->CritSect);
629 return VINF_SUCCESS;
630}
631
632
633/**
634 * Construct a device instance for a VM.
635 *
636 * @returns VBox status.
637 * @param pDevIns The device instance data.
638 * If the registration structure is needed, pDevIns->pDevReg points to it.
639 * @param iInstance Instance number. Use this to figure out which registers and such to use.
640 * The device number is also found in pDevIns->iInstance, but since it's
641 * likely to be freqently used PDM passes it as parameter.
642 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
643 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
644 * iInstance it's expected to be used a bit in this function.
645 */
646static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
647 int iInstance,
648 PCFGMNODE pCfgHandle)
649{
650 int rc;
651 SerialState *pData = PDMINS2DATA(pDevIns, SerialState*);
652 uint16_t io_base;
653 uint8_t irq_lvl;
654
655 Assert(iInstance < 4);
656
657 pData->pDevInsHC = pDevIns;
658 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
659
660 /*
661 * Validate configuration.
662 */
663 if (!CFGMR3AreValuesValid(pCfgHandle, "IRQ\0IOBase\0"))
664 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
665
666 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &pData->fGCEnabled);
667 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
668 pData->fGCEnabled = true;
669 else if (VBOX_FAILURE(rc))
670 return PDMDEV_SET_ERROR(pDevIns, rc,
671 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
672
673 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &pData->fR0Enabled);
674 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
675 pData->fR0Enabled = true;
676 else if (VBOX_FAILURE(rc))
677 return PDMDEV_SET_ERROR(pDevIns, rc,
678 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
679
680 /* IBase */
681 pData->IBase.pfnQueryInterface = serialQueryInterface;
682
683 /* ICharPort */
684 pData->ICharPort.pfnNotifyRead = serialNotifyRead;
685
686 rc = RTSemEventCreate(&pData->ReceiveSem);
687 AssertRC(rc);
688
689 /*
690 * Initialize critical section.
691 * This must of course be done before attaching drivers or anything else which can call us back..
692 */
693 char szName[24];
694 RTStrPrintf(szName, sizeof(szName), "Serial#%d", iInstance);
695 rc = PDMDevHlpCritSectInit(pDevIns, &pData->CritSect, szName);
696 if (VBOX_FAILURE(rc))
697 return rc;
698
699/** @todo r=bird: Check for VERR_CFGM_VALUE_NOT_FOUND and provide sensible defaults.
700 * Also do AssertMsgFailed(("Configuration error:....)) in the failure cases of CFGMR3Query*()
701 * and CFGR3AreValuesValid() like we're doing in the other devices. */
702 rc = CFGMR3QueryU8 (pCfgHandle, "IRQ", &irq_lvl);
703 if (VBOX_FAILURE (rc))
704 return rc;
705
706 rc = CFGMR3QueryU16 (pCfgHandle, "IOBase", &io_base);
707 if (VBOX_FAILURE (rc))
708 return rc;
709
710 Log(("serialConstruct instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
711
712 pData->irq = irq_lvl;
713 pData->lsr = UART_LSR_TEMT | UART_LSR_THRE;
714 pData->iir = UART_IIR_NO_INT;
715 pData->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
716#ifdef VBOX_SERIAL_PCI
717 pData->base = -1;
718 pData->dev.config[0x00] = 0xee; /* Vendor: ??? */
719 pData->dev.config[0x01] = 0x80;
720 pData->dev.config[0x02] = 0x01; /* Device: ??? */
721 pData->dev.config[0x03] = 0x01;
722 pData->dev.config[0x04] = PCI_COMMAND_IOACCESS;
723 pData->dev.config[0x09] = 0x01; /* Programming interface: 16450 */
724 pData->dev.config[0x0a] = 0x00; /* Subclass: Serial controller */
725 pData->dev.config[0x0b] = 0x07; /* Class: Communication controller */
726 pData->dev.config[0x0e] = 0x00; /* Header type: standard */
727 pData->dev.config[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/
728 pData->dev.config[0x3d] = 1; /* interrupt pin 0 */
729 rc = PDMDevHlpPCIRegister(pDevIns, &pData->dev);
730 if (VBOX_FAILURE(rc))
731 return rc;
732 /*
733 * Register the PCI I/O ports.
734 */
735 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap);
736 if (VBOX_FAILURE(rc))
737 return rc;
738#else /* !VBOX_SERIAL_PCI */
739 pData->base = io_base;
740 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
741 serialIOPortWrite, serialIOPortRead,
742 NULL, NULL, "SERIAL");
743 if (VBOX_FAILURE (rc))
744 return rc;
745
746 if (pData->fGCEnabled)
747 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base, 8, 0, "serialIOPortWrite",
748 "serialIOPortRead", NULL, NULL, "Serial");
749
750 if (pData->fR0Enabled)
751 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
752 "serialIOPortRead", NULL, NULL, "Serial");
753
754#endif /* !VBOX_SERIAL_PCI */
755
756 /* Attach the char driver and get the interfaces. For now no run-time
757 * changes are supported. */
758 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pData->IBase, &pData->pDrvBase, "Serial Char");
759 if (VBOX_SUCCESS(rc))
760 {
761 pData->pDrvChar = (PDMICHAR *)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_CHAR);
762 if (!pData->pDrvChar)
763 {
764 AssertMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
765 return VERR_PDM_MISSING_INTERFACE;
766 }
767 /** @todo provide read notification interface!!!! */
768 }
769 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
770 {
771 pData->pDrvBase = NULL;
772 pData->pDrvChar = NULL;
773 LogRel(("Serial%d: no unit\n", iInstance));
774 }
775 else
776 {
777 AssertMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Vrc\n", iInstance, rc));
778 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
779 N_("Serial device %d cannot attach to char driver\n"), iInstance);
780 }
781
782 rc = PDMDevHlpSSMRegister (
783 pDevIns, /* pDevIns */
784 pDevIns->pDevReg->szDeviceName, /* pszName */
785 iInstance, /* u32Instance */
786 SERIAL_SAVED_STATE_VERSION, /* u32Version */
787 sizeof (*pData), /* cbGuess */
788 NULL, /* pfnSavePrep */
789 serialSaveExec, /* pfnSaveExec */
790 NULL, /* pfnSaveDone */
791 NULL, /* pfnLoadPrep */
792 serialLoadExec, /* pfnLoadExec */
793 NULL /* pfnLoadDone */
794 );
795 if (VBOX_FAILURE(rc))
796 return rc;
797
798 return VINF_SUCCESS;
799}
800
801/**
802 * The device registration structure.
803 */
804const PDMDEVREG g_DeviceSerialPort =
805{
806 /* u32Version */
807 PDM_DEVREG_VERSION,
808 /* szDeviceName */
809 "serial",
810 /* szGCMod */
811 "VBoxDDGC.gc",
812 /* szR0Mod */
813 "VBoxDDR0.r0",
814 /* pszDescription */
815 "Serial Communication Port",
816 /* fFlags */
817 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
818 /* fClass */
819 PDM_DEVREG_CLASS_SERIAL,
820 /* cMaxInstances */
821 1,
822 /* cbInstance */
823 sizeof(SerialState),
824 /* pfnConstruct */
825 serialConstruct,
826 /* pfnDestruct */
827 serialDestruct,
828 /* pfnRelocate */
829 serialRelocate,
830 /* pfnIOCtl */
831 NULL,
832 /* pfnPowerOn */
833 NULL,
834 /* pfnReset */
835 NULL,
836 /* pfnSuspend */
837 NULL,
838 /* pfnResume */
839 NULL,
840 /* pfnAttach */
841 NULL,
842 /* pfnDetach */
843 NULL,
844 /* pfnQueryInterface. */
845 NULL
846};
847#endif /* IN_RING3 */
848
849
850#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