VirtualBox

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

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

Use pdmdrv.h and pdmdev.h where appropirate.

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