VirtualBox

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

Last change on this file since 48935 was 45025, checked in by vboxsync, 12 years ago

Update PDMDEVREG initialization comment so they refer to pfnMemSetup instead of pfnIOCtl.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.3 KB
Line 
1/* $Id: DevSerial.cpp 45025 2013-03-13 16:45:15Z vboxsync $ */
2/** @file
3 * DevSerial - 16550A UART emulation.
4 * (taken from hw/serial.c 2010/05/15 with modifications)
5 */
6
7/*
8 * Copyright (C) 2006-2013 Oracle Corporation
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 (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*
20 * This code is based on:
21 *
22 * QEMU 16550A UART emulation
23 *
24 * Copyright (c) 2003-2004 Fabrice Bellard
25 * Copyright (c) 2008 Citrix Systems, Inc.
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to deal
29 * in the Software without restriction, including without limitation the rights
30 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31 * copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
40 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 * THE SOFTWARE.
44 */
45
46/*******************************************************************************
47* Header Files *
48*******************************************************************************/
49#define LOG_GROUP LOG_GROUP_DEV_SERIAL
50#include <VBox/vmm/pdmdev.h>
51#include <iprt/assert.h>
52#include <iprt/uuid.h>
53#include <iprt/string.h>
54#include <iprt/semaphore.h>
55#include <iprt/critsect.h>
56
57#include "VBoxDD.h"
58
59#undef VBOX_SERIAL_PCI /* The PCI variant has lots of problems: wrong IRQ line and wrong IO base assigned. */
60
61#ifdef VBOX_SERIAL_PCI
62# include <VBox/pci.h>
63#endif /* VBOX_SERIAL_PCI */
64
65
66/*******************************************************************************
67* Defined Constants And Macros *
68*******************************************************************************/
69#define SERIAL_SAVED_STATE_VERSION_16450 3
70#define SERIAL_SAVED_STATE_VERSION 4
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#define UART_IIR_CTI 0x0C /* Character Timeout Indication */
87
88#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functioning */
89#define UART_IIR_FE 0xC0 /* Fifo enabled */
90
91/*
92 * These are the definitions for the Modem Control Register
93 */
94#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
95#define UART_MCR_OUT2 0x08 /* Out2 complement */
96#define UART_MCR_OUT1 0x04 /* Out1 complement */
97#define UART_MCR_RTS 0x02 /* RTS complement */
98#define UART_MCR_DTR 0x01 /* DTR complement */
99
100/*
101 * These are the definitions for the Modem Status Register
102 */
103#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
104#define UART_MSR_RI 0x40 /* Ring Indicator */
105#define UART_MSR_DSR 0x20 /* Data Set Ready */
106#define UART_MSR_CTS 0x10 /* Clear to Send */
107#define UART_MSR_DDCD 0x08 /* Delta DCD */
108#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
109#define UART_MSR_DDSR 0x02 /* Delta DSR */
110#define UART_MSR_DCTS 0x01 /* Delta CTS */
111#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
112
113#define UART_LSR_TEMT 0x40 /* Transmitter empty */
114#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
115#define UART_LSR_BI 0x10 /* Break interrupt indicator */
116#define UART_LSR_FE 0x08 /* Frame error indicator */
117#define UART_LSR_PE 0x04 /* Parity error indicator */
118#define UART_LSR_OE 0x02 /* Overrun error indicator */
119#define UART_LSR_DR 0x01 /* Receiver data ready */
120#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */
121
122/*
123 * Interrupt trigger levels.
124 * The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher.
125 */
126#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */
127#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */
128#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */
129#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */
130
131#define UART_FCR_DMS 0x08 /* DMA Mode Select */
132#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */
133#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */
134#define UART_FCR_FE 0x01 /* FIFO Enable */
135
136#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */
137
138#define XMIT_FIFO 0
139#define RECV_FIFO 1
140#define MIN_XMIT_RETRY 16
141#define MAX_XMIT_RETRY_TIME 1 /* max time (in seconds) for retrying the character xmit before dropping it */
142
143/*******************************************************************************
144* Structures and Typedefs *
145*******************************************************************************/
146
147struct SerialFifo
148{
149 uint8_t data[UART_FIFO_LENGTH];
150 uint8_t count;
151 uint8_t itl;
152 uint8_t tail;
153 uint8_t head;
154};
155
156/**
157 * Serial device.
158 *
159 * @implements PDMIBASE
160 * @implements PDMICHARPORT
161 */
162typedef struct SerialState
163{
164 /** Access critical section. */
165 PDMCRITSECT CritSect;
166 /** Pointer to the device instance - R3 Ptr. */
167 PPDMDEVINSR3 pDevInsR3;
168 /** Pointer to the device instance - R0 Ptr. */
169 PPDMDEVINSR0 pDevInsR0;
170 /** Pointer to the device instance - RC Ptr. */
171 PPDMDEVINSRC pDevInsRC;
172 /** Alignment. */
173 RTRCPTR Alignment0;
174 /** LUN\#0: The base interface. */
175 PDMIBASE IBase;
176 /** LUN\#0: The character port interface. */
177 PDMICHARPORT ICharPort;
178 /** Pointer to the attached base driver. */
179 R3PTRTYPE(PPDMIBASE) pDrvBase;
180 /** Pointer to the attached character driver. */
181 R3PTRTYPE(PPDMICHARCONNECTOR) pDrvChar;
182
183 RTSEMEVENT ReceiveSem;
184 PTMTIMERR3 fifo_timeout_timer;
185 PTMTIMERR3 transmit_timerR3;
186 PTMTIMERR0 transmit_timerR0; /* currently not used */
187 PTMTIMERRC transmit_timerRC; /* currently not used */
188 RTRCPTR Alignment1;
189 SerialFifo recv_fifo;
190 SerialFifo xmit_fifo;
191
192 uint32_t base;
193 uint16_t divider;
194 uint16_t Alignment2[1];
195 uint8_t rbr; /**< receive register */
196 uint8_t thr; /**< transmit holding register */
197 uint8_t tsr; /**< transmit shift register */
198 uint8_t ier; /**< interrupt enable register */
199 uint8_t iir; /**< interrupt identification register, R/O */
200 uint8_t lcr; /**< line control register */
201 uint8_t mcr; /**< modem control register */
202 uint8_t lsr; /**< line status register, R/O */
203 uint8_t msr; /**< modem status register, R/O */
204 uint8_t scr; /**< scratch register */
205 uint8_t fcr; /**< fifo control register */
206 uint8_t fcr_vmstate;
207 /* NOTE: this hidden state is necessary for tx irq generation as
208 it can be reset while reading iir */
209 int thr_ipending;
210 int timeout_ipending;
211 int irq;
212 int last_break_enable;
213 /** Counter for retrying xmit */
214 int tsr_retry;
215 int tsr_retry_bound; /**< number of retries before dropping a character */
216 int tsr_retry_bound_max; /**< maximum possible tsr_retry_bound value that can be set while dynamic bound adjustment */
217 int tsr_retry_bound_min; /**< minimum possible tsr_retry_bound value that can be set while dynamic bound adjustment */
218 bool msr_changed;
219 bool fGCEnabled;
220 bool fR0Enabled;
221 bool fYieldOnLSRRead;
222 bool volatile fRecvWaiting;
223 bool f16550AEnabled;
224 bool Alignment3[6];
225 /** Time it takes to transmit a character */
226 uint64_t char_transmit_time;
227
228#ifdef VBOX_SERIAL_PCI
229 PCIDEVICE PciDev;
230#endif /* VBOX_SERIAL_PCI */
231} DEVSERIAL;
232/** Pointer to the serial device state. */
233typedef DEVSERIAL *PDEVSERIAL;
234
235#ifndef VBOX_DEVICE_STRUCT_TESTCASE
236
237#ifdef IN_RING3
238
239static int serial_can_receive(PDEVSERIAL pThis);
240static void serial_receive(PDEVSERIAL pThis, const uint8_t *buf, int size);
241
242static void fifo_clear(PDEVSERIAL pThis, int fifo)
243{
244 SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo;
245 memset(f->data, 0, UART_FIFO_LENGTH);
246 f->count = 0;
247 f->head = 0;
248 f->tail = 0;
249}
250
251static int fifo_put(PDEVSERIAL pThis, int fifo, uint8_t chr)
252{
253 SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo;
254
255 /* Receive overruns do not overwrite FIFO contents. */
256 if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH)
257 {
258 f->data[f->head++] = chr;
259 if (f->head == UART_FIFO_LENGTH)
260 f->head = 0;
261 }
262
263 if (f->count < UART_FIFO_LENGTH)
264 f->count++;
265 else if (fifo == XMIT_FIFO) /* need to at least adjust tail to maintain pipe state consistency */
266 ++f->tail;
267 else if (fifo == RECV_FIFO)
268 pThis->lsr |= UART_LSR_OE;
269
270 return 1;
271}
272
273static uint8_t fifo_get(PDEVSERIAL pThis, int fifo)
274{
275 SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo;
276 uint8_t c;
277
278 if (f->count == 0)
279 return 0;
280
281 c = f->data[f->tail++];
282 if (f->tail == UART_FIFO_LENGTH)
283 f->tail = 0;
284 f->count--;
285
286 return c;
287}
288
289static void serial_update_irq(PDEVSERIAL pThis)
290{
291 uint8_t tmp_iir = UART_IIR_NO_INT;
292
293 if ( (pThis->ier & UART_IER_RLSI)
294 && (pThis->lsr & UART_LSR_INT_ANY)) {
295 tmp_iir = UART_IIR_RLSI;
296 } else if ((pThis->ier & UART_IER_RDI) && pThis->timeout_ipending) {
297 /* Note that(pThis->ier & UART_IER_RDI) can mask this interrupt,
298 * this is not in the specification but is observed on existing
299 * hardware. */
300 tmp_iir = UART_IIR_CTI;
301 } else if ( (pThis->ier & UART_IER_RDI)
302 && (pThis->lsr & UART_LSR_DR)
303 && ( !(pThis->fcr & UART_FCR_FE)
304 || pThis->recv_fifo.count >= pThis->recv_fifo.itl)) {
305 tmp_iir = UART_IIR_RDI;
306 } else if ( (pThis->ier & UART_IER_THRI)
307 && pThis->thr_ipending) {
308 tmp_iir = UART_IIR_THRI;
309 } else if ( (pThis->ier & UART_IER_MSI)
310 && (pThis->msr & UART_MSR_ANY_DELTA)) {
311 tmp_iir = UART_IIR_MSI;
312 }
313 pThis->iir = tmp_iir | (pThis->iir & 0xF0);
314
315 /** XXX only call the SetIrq function if the state really changes! */
316 if (tmp_iir != UART_IIR_NO_INT) {
317 Log(("serial_update_irq %d 1\n", pThis->irq));
318# ifdef VBOX_SERIAL_PCI
319 PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 1);
320# else /* !VBOX_SERIAL_PCI */
321 PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 1);
322# endif /* !VBOX_SERIAL_PCI */
323 } else {
324 Log(("serial_update_irq %d 0\n", pThis->irq));
325# ifdef VBOX_SERIAL_PCI
326 PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 0);
327# else /* !VBOX_SERIAL_PCI */
328 PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 0);
329# endif /* !VBOX_SERIAL_PCI */
330 }
331}
332
333static void serial_tsr_retry_update_parameters(PDEVSERIAL pThis, uint64_t tf)
334{
335 pThis->tsr_retry_bound_max = RT_MAX((tf * MAX_XMIT_RETRY_TIME) / pThis->char_transmit_time, MIN_XMIT_RETRY);
336 pThis->tsr_retry_bound_min = RT_MAX(pThis->tsr_retry_bound_max / (1000 * MAX_XMIT_RETRY_TIME), MIN_XMIT_RETRY);
337 /* for simplicity just reset to max retry count */
338 pThis->tsr_retry_bound = pThis->tsr_retry_bound_max;
339}
340
341static void serial_tsr_retry_bound_reached(PDEVSERIAL pThis)
342{
343 /* this is most likely means we have some backend connection issues */
344 /* decrement the retry bound */
345 pThis->tsr_retry_bound = RT_MAX(pThis->tsr_retry_bound / (10 * MAX_XMIT_RETRY_TIME), pThis->tsr_retry_bound_min);
346}
347
348static void serial_tsr_retry_succeeded(PDEVSERIAL pThis)
349{
350 /* success means we have a backend connection working OK,
351 * set retry bound to its maximum value */
352 pThis->tsr_retry_bound = pThis->tsr_retry_bound_max;
353}
354
355static void serial_update_parameters(PDEVSERIAL pThis)
356{
357 int speed, parity, data_bits, stop_bits, frame_size;
358
359 if (pThis->divider == 0)
360 return;
361
362 frame_size = 1;
363 if (pThis->lcr & 0x08) {
364 frame_size++;
365 if (pThis->lcr & 0x10)
366 parity = 'E';
367 else
368 parity = 'O';
369 } else {
370 parity = 'N';
371 }
372 if (pThis->lcr & 0x04)
373 stop_bits = 2;
374 else
375 stop_bits = 1;
376
377 data_bits = (pThis->lcr & 0x03) + 5;
378 frame_size += data_bits + stop_bits;
379 speed = 115200 / pThis->divider;
380 uint64_t tf = TMTimerGetFreq(CTX_SUFF(pThis->transmit_timer));
381 pThis->char_transmit_time = (tf / speed) * frame_size;
382 serial_tsr_retry_update_parameters(pThis, tf);
383
384 Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
385
386 if (RT_LIKELY(pThis->pDrvChar))
387 pThis->pDrvChar->pfnSetParameters(pThis->pDrvChar, speed, parity, data_bits, stop_bits);
388}
389
390static void serial_xmit(PDEVSERIAL pThis, bool bRetryXmit)
391{
392 if (pThis->tsr_retry <= 0) {
393 if (pThis->fcr & UART_FCR_FE) {
394 pThis->tsr = fifo_get(pThis, XMIT_FIFO);
395 if (!pThis->xmit_fifo.count)
396 pThis->lsr |= UART_LSR_THRE;
397 } else {
398 pThis->tsr = pThis->thr;
399 pThis->lsr |= UART_LSR_THRE;
400 }
401 }
402
403 if (pThis->mcr & UART_MCR_LOOP) {
404 /* in loopback mode, say that we just received a char */
405 serial_receive(pThis, &pThis->tsr, 1);
406 } else if ( RT_LIKELY(pThis->pDrvChar)
407 && RT_FAILURE(pThis->pDrvChar->pfnWrite(pThis->pDrvChar, &pThis->tsr, 1))) {
408 if ((pThis->tsr_retry >= 0) && ((!bRetryXmit) || (pThis->tsr_retry <= pThis->tsr_retry_bound))) {
409 if (!pThis->tsr_retry)
410 pThis->tsr_retry = 1; /* make sure the retry state is always set */
411 else if (bRetryXmit) /* do not increase the retry count if the retry is actually caused by next char write */
412 pThis->tsr_retry++;
413
414 TMTimerSet(CTX_SUFF(pThis->transmit_timer), TMTimerGet(CTX_SUFF(pThis->transmit_timer)) + pThis->char_transmit_time * 4);
415 return;
416 } else {
417 /* drop this character. */
418 pThis->tsr_retry = 0;
419 serial_tsr_retry_bound_reached(pThis);
420 }
421 }
422 else {
423 pThis->tsr_retry = 0;
424 serial_tsr_retry_succeeded(pThis);
425 }
426
427 if (!(pThis->lsr & UART_LSR_THRE))
428 TMTimerSet(CTX_SUFF(pThis->transmit_timer),
429 TMTimerGet(CTX_SUFF(pThis->transmit_timer)) + pThis->char_transmit_time);
430
431 if (pThis->lsr & UART_LSR_THRE) {
432 pThis->lsr |= UART_LSR_TEMT;
433 pThis->thr_ipending = 1;
434 serial_update_irq(pThis);
435 }
436}
437
438#endif /* IN_RING3 */
439
440static int serial_ioport_write(PDEVSERIAL pThis, uint32_t addr, uint32_t val)
441{
442 addr &= 7;
443
444#ifndef IN_RING3
445 NOREF(pThis);
446 return VINF_IOM_R3_IOPORT_WRITE;
447#else
448 switch(addr) {
449 default:
450 case 0:
451 if (pThis->lcr & UART_LCR_DLAB) {
452 pThis->divider = (pThis->divider & 0xff00) | val;
453 serial_update_parameters(pThis);
454 } else {
455 pThis->thr = (uint8_t) val;
456 if (pThis->fcr & UART_FCR_FE) {
457 fifo_put(pThis, XMIT_FIFO, pThis->thr);
458 pThis->thr_ipending = 0;
459 pThis->lsr &= ~UART_LSR_TEMT;
460 pThis->lsr &= ~UART_LSR_THRE;
461 serial_update_irq(pThis);
462 } else {
463 pThis->thr_ipending = 0;
464 pThis->lsr &= ~UART_LSR_THRE;
465 serial_update_irq(pThis);
466 }
467 serial_xmit(pThis, false);
468 }
469 break;
470 case 1:
471 if (pThis->lcr & UART_LCR_DLAB) {
472 pThis->divider = (pThis->divider & 0x00ff) | (val << 8);
473 serial_update_parameters(pThis);
474 } else {
475 pThis->ier = val & 0x0f;
476 if (pThis->lsr & UART_LSR_THRE) {
477 pThis->thr_ipending = 1;
478 serial_update_irq(pThis);
479 }
480 }
481 break;
482 case 2:
483 if (!pThis->f16550AEnabled)
484 break;
485
486 val = val & 0xFF;
487
488 if (pThis->fcr == val)
489 break;
490
491 /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
492 if ((val ^ pThis->fcr) & UART_FCR_FE)
493 val |= UART_FCR_XFR | UART_FCR_RFR;
494
495 /* FIFO clear */
496 if (val & UART_FCR_RFR) {
497 TMTimerStop(pThis->fifo_timeout_timer);
498 pThis->timeout_ipending = 0;
499 fifo_clear(pThis, RECV_FIFO);
500 }
501 if (val & UART_FCR_XFR) {
502 fifo_clear(pThis, XMIT_FIFO);
503 }
504
505 if (val & UART_FCR_FE) {
506 pThis->iir |= UART_IIR_FE;
507 /* Set RECV_FIFO trigger Level */
508 switch (val & 0xC0) {
509 case UART_FCR_ITL_1:
510 pThis->recv_fifo.itl = 1;
511 break;
512 case UART_FCR_ITL_2:
513 pThis->recv_fifo.itl = 4;
514 break;
515 case UART_FCR_ITL_3:
516 pThis->recv_fifo.itl = 8;
517 break;
518 case UART_FCR_ITL_4:
519 pThis->recv_fifo.itl = 14;
520 break;
521 }
522 } else
523 pThis->iir &= ~UART_IIR_FE;
524
525 /* Set fcr - or at least the bits in it that are supposed to "stick" */
526 pThis->fcr = val & 0xC9;
527 serial_update_irq(pThis);
528 break;
529 case 3:
530 {
531 int break_enable;
532 pThis->lcr = val;
533 serial_update_parameters(pThis);
534 break_enable = (val >> 6) & 1;
535 if (break_enable != pThis->last_break_enable) {
536 pThis->last_break_enable = break_enable;
537 if (RT_LIKELY(pThis->pDrvChar))
538 {
539 Log(("serial_ioport_write: Set break %d\n", break_enable));
540 int rc = pThis->pDrvChar->pfnSetBreak(pThis->pDrvChar, !!break_enable);
541 AssertRC(rc);
542 }
543 }
544 }
545 break;
546 case 4:
547 pThis->mcr = val & 0x1f;
548 if (RT_LIKELY(pThis->pDrvChar))
549 {
550 int rc = pThis->pDrvChar->pfnSetModemLines(pThis->pDrvChar,
551 !!(pThis->mcr & UART_MCR_RTS),
552 !!(pThis->mcr & UART_MCR_DTR));
553 AssertRC(rc);
554 }
555 break;
556 case 5:
557 break;
558 case 6:
559 break;
560 case 7:
561 pThis->scr = val;
562 break;
563 }
564 return VINF_SUCCESS;
565#endif
566}
567
568static uint32_t serial_ioport_read(PDEVSERIAL pThis, uint32_t addr, int *pRC)
569{
570 uint32_t ret = ~0U;
571
572 *pRC = VINF_SUCCESS;
573
574 addr &= 7;
575 switch(addr) {
576 default:
577 case 0:
578 if (pThis->lcr & UART_LCR_DLAB) {
579 /* DLAB == 1: divisor latch (LS) */
580 ret = pThis->divider & 0xff;
581 } else {
582#ifndef IN_RING3
583 *pRC = VINF_IOM_R3_IOPORT_READ;
584#else
585 if (pThis->fcr & UART_FCR_FE) {
586 ret = fifo_get(pThis, RECV_FIFO);
587 if (pThis->recv_fifo.count == 0)
588 pThis->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
589 else
590 TMTimerSet(pThis->fifo_timeout_timer,
591 TMTimerGet(pThis->fifo_timeout_timer) + pThis->char_transmit_time * 4);
592 pThis->timeout_ipending = 0;
593 } else {
594 Log(("serial_io_port_read: read 0x%X\n", pThis->rbr));
595 ret = pThis->rbr;
596 pThis->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
597 }
598 serial_update_irq(pThis);
599 if (pThis->fRecvWaiting)
600 {
601 pThis->fRecvWaiting = false;
602 int rc = RTSemEventSignal(pThis->ReceiveSem);
603 AssertRC(rc);
604 }
605#endif
606 }
607 break;
608 case 1:
609 if (pThis->lcr & UART_LCR_DLAB) {
610 /* DLAB == 1: divisor latch (MS) */
611 ret = (pThis->divider >> 8) & 0xff;
612 } else {
613 ret = pThis->ier;
614 }
615 break;
616 case 2:
617#ifndef IN_RING3
618 *pRC = VINF_IOM_R3_IOPORT_READ;
619#else
620 ret = pThis->iir;
621 if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
622 pThis->thr_ipending = 0;
623 serial_update_irq(pThis);
624 }
625 /* reset msr changed bit */
626 pThis->msr_changed = false;
627#endif
628 break;
629 case 3:
630 ret = pThis->lcr;
631 break;
632 case 4:
633 ret = pThis->mcr;
634 break;
635 case 5:
636 if ((pThis->lsr & UART_LSR_DR) == 0 && pThis->fYieldOnLSRRead)
637 {
638 /* No data available and yielding is enabled, so yield in ring3. */
639#ifndef IN_RING3
640 *pRC = VINF_IOM_R3_IOPORT_READ;
641 break;
642#else
643 RTThreadYield ();
644#endif
645 }
646 ret = pThis->lsr;
647 /* Clear break and overrun interrupts */
648 if (pThis->lsr & (UART_LSR_BI|UART_LSR_OE)) {
649#ifndef IN_RING3
650 *pRC = VINF_IOM_R3_IOPORT_READ;
651#else
652 pThis->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
653 serial_update_irq(pThis);
654#endif
655 }
656 break;
657 case 6:
658 if (pThis->mcr & UART_MCR_LOOP) {
659 /* in loopback, the modem output pins are connected to the
660 inputs */
661 ret = (pThis->mcr & 0x0c) << 4;
662 ret |= (pThis->mcr & 0x02) << 3;
663 ret |= (pThis->mcr & 0x01) << 5;
664 } else {
665 ret = pThis->msr;
666 /* Clear delta bits & msr int after read, if they were set */
667 if (pThis->msr & UART_MSR_ANY_DELTA) {
668#ifndef IN_RING3
669 *pRC = VINF_IOM_R3_IOPORT_READ;
670#else
671 pThis->msr &= 0xF0;
672 serial_update_irq(pThis);
673#endif
674 }
675 }
676 break;
677 case 7:
678 ret = pThis->scr;
679 break;
680 }
681 return ret;
682}
683
684#ifdef IN_RING3
685
686static int serial_can_receive(PDEVSERIAL pThis)
687{
688 if (pThis->fcr & UART_FCR_FE) {
689 if (pThis->recv_fifo.count < UART_FIFO_LENGTH)
690 return (pThis->recv_fifo.count <= pThis->recv_fifo.itl)
691 ? pThis->recv_fifo.itl - pThis->recv_fifo.count : 1;
692 else
693 return 0;
694 } else {
695 return !(pThis->lsr & UART_LSR_DR);
696 }
697}
698
699static void serial_receive(PDEVSERIAL pThis, const uint8_t *buf, int size)
700{
701 if (pThis->fcr & UART_FCR_FE) {
702 int i;
703 for (i = 0; i < size; i++) {
704 fifo_put(pThis, RECV_FIFO, buf[i]);
705 }
706 pThis->lsr |= UART_LSR_DR;
707 /* call the timeout receive callback in 4 char transmit time */
708 TMTimerSet(pThis->fifo_timeout_timer, TMTimerGet(pThis->fifo_timeout_timer) + pThis->char_transmit_time * 4);
709 } else {
710 if (pThis->lsr & UART_LSR_DR)
711 pThis->lsr |= UART_LSR_OE;
712 pThis->rbr = buf[0];
713 pThis->lsr |= UART_LSR_DR;
714 }
715 serial_update_irq(pThis);
716}
717
718
719/**
720 * @interface_method_impl{PDMICHARPORT,pfnNotifyRead}
721 */
722static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
723{
724 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort);
725 const uint8_t *pu8Buf = (const uint8_t*)pvBuf;
726 size_t cbRead = *pcbRead;
727
728 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
729 for (; cbRead > 0; cbRead--, pu8Buf++)
730 {
731 if (!serial_can_receive(pThis))
732 {
733 /* If we cannot receive then wait for not more than 250ms. If we still
734 * cannot receive then the new character will either overwrite rbr
735 * or it will be dropped at fifo_put(). */
736 pThis->fRecvWaiting = true;
737 PDMCritSectLeave(&pThis->CritSect);
738 int rc = RTSemEventWait(pThis->ReceiveSem, 250);
739 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
740 }
741 serial_receive(pThis, &pu8Buf[0], 1);
742 }
743 PDMCritSectLeave(&pThis->CritSect);
744 return VINF_SUCCESS;
745}
746
747/**
748 * @@interface_method_impl{PDMICHARPORT,pfnNotifyStatusLinesChanged}
749 */
750static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines)
751{
752 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort);
753 uint8_t newMsr = 0;
754
755 Log(("%s: pInterface=%p newStatusLines=%u\n", __FUNCTION__, pInterface, newStatusLines));
756
757 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
758
759 /* Set new states. */
760 if (newStatusLines & PDMICHARPORT_STATUS_LINES_DCD)
761 newMsr |= UART_MSR_DCD;
762 if (newStatusLines & PDMICHARPORT_STATUS_LINES_RI)
763 newMsr |= UART_MSR_RI;
764 if (newStatusLines & PDMICHARPORT_STATUS_LINES_DSR)
765 newMsr |= UART_MSR_DSR;
766 if (newStatusLines & PDMICHARPORT_STATUS_LINES_CTS)
767 newMsr |= UART_MSR_CTS;
768
769 /* Compare the old and the new states and set the delta bits accordingly. */
770 if ((newMsr & UART_MSR_DCD) != (pThis->msr & UART_MSR_DCD))
771 newMsr |= UART_MSR_DDCD;
772 if ((newMsr & UART_MSR_RI) == 1 && (pThis->msr & UART_MSR_RI) == 0)
773 newMsr |= UART_MSR_TERI;
774 if ((newMsr & UART_MSR_DSR) != (pThis->msr & UART_MSR_DSR))
775 newMsr |= UART_MSR_DDSR;
776 if ((newMsr & UART_MSR_CTS) != (pThis->msr & UART_MSR_CTS))
777 newMsr |= UART_MSR_DCTS;
778
779 pThis->msr = newMsr;
780 pThis->msr_changed = true;
781 serial_update_irq(pThis);
782
783 PDMCritSectLeave(&pThis->CritSect);
784
785 return VINF_SUCCESS;
786}
787
788
789/**
790 * @interface_method_impl{PDMICHARPORT,pfnNotifyBufferFull}
791 */
792static DECLCALLBACK(int) serialNotifyBufferFull(PPDMICHARPORT pInterface, bool fFull)
793{
794 return VINF_SUCCESS;
795}
796
797
798/**
799 * @interface_method_impl{PDMICHARPORT,pfnNotifyBreak}
800 */
801static DECLCALLBACK(int) serialNotifyBreak(PPDMICHARPORT pInterface)
802{
803 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort);
804
805 Log(("%s: pInterface=%p\n", __FUNCTION__, pInterface));
806
807 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
808
809 pThis->lsr |= UART_LSR_BI;
810 serial_update_irq(pThis);
811
812 PDMCritSectLeave(&pThis->CritSect);
813
814 return VINF_SUCCESS;
815}
816
817
818/* -=-=-=-=-=-=-=-=- Timer callbacks -=-=-=-=-=-=-=-=- */
819
820/**
821 * @callback_method_tmpl{FNTMTIMERDEV, Fifo timer function.}
822 */
823static DECLCALLBACK(void) serialFifoTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
824{
825 PDEVSERIAL pThis = (PDEVSERIAL)pvUser;
826 Assert(PDMCritSectIsOwner(&pThis->CritSect));
827 if (pThis->recv_fifo.count)
828 {
829 pThis->timeout_ipending = 1;
830 serial_update_irq(pThis);
831 }
832}
833
834/**
835 * @callback_method_tmpl{FNTMTIMERDEV, Transmit timer function.}
836 *
837 * Just retry to transmit a character.
838 */
839static DECLCALLBACK(void) serialTransmitTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
840{
841 PDEVSERIAL pThis = (PDEVSERIAL)pvUser;
842 Assert(PDMCritSectIsOwner(&pThis->CritSect));
843 serial_xmit(pThis, true);
844}
845
846#endif /* IN_RING3 */
847
848/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
849
850/**
851 * @callback_method_impl{FNIOMIOPORTOUT}
852 */
853PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
854{
855 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
856 int rc;
857 Assert(PDMCritSectIsOwner(&pThis->CritSect));
858
859 if (cb == 1)
860 {
861 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
862 rc = serial_ioport_write(pThis, Port, u32);
863 }
864 else
865 {
866 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
867 rc = VINF_SUCCESS;
868 }
869
870 return rc;
871}
872
873
874/**
875 * @callback_method_impl{FNIOMIOPORTIN}
876 */
877PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
878{
879 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
880 int rc;
881 Assert(PDMCritSectIsOwner(&pThis->CritSect));
882
883 if (cb == 1)
884 {
885 *pu32 = serial_ioport_read(pThis, Port, &rc);
886 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
887 }
888 else
889 rc = VERR_IOM_IOPORT_UNUSED;
890
891 return rc;
892}
893
894#ifdef IN_RING3
895
896/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
897
898/**
899 * @callback_method_tmpl{FNSSMDEVLIVEEXEC}
900 */
901static DECLCALLBACK(int) serialLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
902{
903 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
904 SSMR3PutS32(pSSM, pThis->irq);
905 SSMR3PutU32(pSSM, pThis->base);
906 return VINF_SSM_DONT_CALL_AGAIN;
907}
908
909
910/**
911 * @callback_method_tmpl{FNSSMDEVSAVEEXEC}
912 */
913static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
914{
915 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
916
917 SSMR3PutU16(pSSM, pThis->divider);
918 SSMR3PutU8(pSSM, pThis->rbr);
919 SSMR3PutU8(pSSM, pThis->ier);
920 SSMR3PutU8(pSSM, pThis->lcr);
921 SSMR3PutU8(pSSM, pThis->mcr);
922 SSMR3PutU8(pSSM, pThis->lsr);
923 SSMR3PutU8(pSSM, pThis->msr);
924 SSMR3PutU8(pSSM, pThis->scr);
925 SSMR3PutU8(pSSM, pThis->fcr); /* 16550A */
926 SSMR3PutS32(pSSM, pThis->thr_ipending);
927 SSMR3PutS32(pSSM, pThis->irq);
928 SSMR3PutS32(pSSM, pThis->last_break_enable);
929 SSMR3PutU32(pSSM, pThis->base);
930 SSMR3PutBool(pSSM, pThis->msr_changed);
931
932 /* Don't store:
933 * - the content of the FIFO
934 * - tsr_retry
935 */
936
937 return SSMR3PutU32(pSSM, ~0); /* sanity/terminator */
938}
939
940
941/**
942 * @callback_method_tmpl{FNSSMDEVLOADEXEC}
943 */
944static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
945{
946 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
947
948 if (uVersion == SERIAL_SAVED_STATE_VERSION_16450)
949 {
950 pThis->f16550AEnabled = false;
951 LogRel(("Serial#%d: falling back to 16450 mode from load state\n", pDevIns->iInstance));
952 }
953 else
954 AssertMsgReturn(uVersion == SERIAL_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
955
956 if (uPass == SSM_PASS_FINAL)
957 {
958 SSMR3GetU16(pSSM, &pThis->divider);
959 SSMR3GetU8(pSSM, &pThis->rbr);
960 SSMR3GetU8(pSSM, &pThis->ier);
961 SSMR3GetU8(pSSM, &pThis->lcr);
962 SSMR3GetU8(pSSM, &pThis->mcr);
963 SSMR3GetU8(pSSM, &pThis->lsr);
964 SSMR3GetU8(pSSM, &pThis->msr);
965 SSMR3GetU8(pSSM, &pThis->scr);
966 if (uVersion > SERIAL_SAVED_STATE_VERSION_16450)
967 {
968 SSMR3GetU8(pSSM, &pThis->fcr);
969 }
970 SSMR3GetS32(pSSM, &pThis->thr_ipending);
971 }
972
973 int32_t iIrq;
974 SSMR3GetS32(pSSM, &iIrq);
975
976 if (uPass == SSM_PASS_FINAL)
977 SSMR3GetS32(pSSM, &pThis->last_break_enable);
978
979 uint32_t IOBase;
980 int rc = SSMR3GetU32(pSSM, &IOBase);
981 AssertRCReturn(rc, rc);
982
983 if ( pThis->irq != iIrq
984 || pThis->base != IOBase)
985 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
986 N_("Config mismatch - saved irq=%#x iobase=%#x; configured irq=%#x iobase=%#x"),
987 iIrq, IOBase, pThis->irq, pThis->base);
988
989 if (uPass == SSM_PASS_FINAL)
990 {
991 SSMR3GetBool(pSSM, &pThis->msr_changed);
992
993 uint32_t u32;
994 rc = SSMR3GetU32(pSSM, &u32);
995 if (RT_FAILURE(rc))
996 return rc;
997 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
998
999 if ( (pThis->lsr & UART_LSR_DR)
1000 || pThis->fRecvWaiting)
1001 {
1002 pThis->fRecvWaiting = false;
1003 rc = RTSemEventSignal(pThis->ReceiveSem);
1004 AssertRC(rc);
1005 }
1006
1007 /* this isn't strictly necessary but cannot hurt... */
1008 pThis->pDevInsR3 = pDevIns;
1009 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1010 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1011 }
1012
1013 return VINF_SUCCESS;
1014}
1015
1016
1017#ifdef VBOX_SERIAL_PCI
1018/* -=-=-=-=-=-=-=-=- PCI Device Callback(s) -=-=-=-=-=-=-=-=- */
1019
1020/**
1021 * @callback_method_impl{FNPCIIOREGIONMAP}
1022 */
1023static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress,
1024 uint32_t cb, PCIADDRESSSPACE enmType)
1025{
1026 PDEVSERIAL pThis = RT_FROM_MEMBER(pPciDev, DEVSERIAL, PciDev);
1027 int rc = VINF_SUCCESS;
1028
1029 Assert(enmType == PCI_ADDRESS_SPACE_IO);
1030 Assert(iRegion == 0);
1031 Assert(cb == 8);
1032 AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
1033
1034 pThis->base = (RTIOPORT)GCPhysAddress;
1035 LogRel(("Serial#%d: mapping I/O at %#06x\n", pThis->pDevIns->iInstance, pThis->base));
1036
1037 /*
1038 * Register our port IO handlers.
1039 */
1040 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pThis,
1041 serial_io_write, serial_io_read, NULL, NULL, "SERIAL");
1042 AssertRC(rc);
1043 return rc;
1044}
1045
1046#endif /* VBOX_SERIAL_PCI */
1047
1048
1049/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#1 -=-=-=-=-=-=-=-=- */
1050
1051/**
1052 * @interface_method_impl{PDMIBASE, pfnQueryInterface}
1053 */
1054static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1055{
1056 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, IBase);
1057 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1058 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARPORT, &pThis->ICharPort);
1059 return NULL;
1060}
1061
1062
1063/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
1064
1065/**
1066 * @interface_method_impl{PDMDEVREG, pfnRelocate}
1067 */
1068static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1069{
1070 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1071 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1072 pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3);
1073}
1074
1075
1076/**
1077 * @interface_method_impl{PDMDEVREG, pfnReset}
1078 */
1079static DECLCALLBACK(void) serialReset(PPDMDEVINS pDevIns)
1080{
1081 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1082
1083 pThis->rbr = 0;
1084 pThis->ier = 0;
1085 pThis->iir = UART_IIR_NO_INT;
1086 pThis->lcr = 0;
1087 pThis->lsr = UART_LSR_TEMT | UART_LSR_THRE;
1088 pThis->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
1089 /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
1090 pThis->divider = 0x0C;
1091 pThis->mcr = UART_MCR_OUT2;
1092 pThis->scr = 0;
1093 pThis->tsr_retry = 0;
1094 uint64_t tf = TMTimerGetFreq(CTX_SUFF(pThis->transmit_timer));
1095 pThis->char_transmit_time = (tf / 9600) * 10;
1096 serial_tsr_retry_update_parameters(pThis, tf);
1097
1098 fifo_clear(pThis, RECV_FIFO);
1099 fifo_clear(pThis, XMIT_FIFO);
1100
1101 pThis->thr_ipending = 0;
1102 pThis->last_break_enable = 0;
1103# ifdef VBOX_SERIAL_PCI
1104 PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 0);
1105# else /* !VBOX_SERIAL_PCI */
1106 PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 0);
1107# endif /* !VBOX_SERIAL_PCI */
1108}
1109
1110
1111/**
1112 * @interface_method_impl{PDMDEVREG, pfnDestruct}
1113 */
1114static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns)
1115{
1116 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1117 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1118
1119 RTSemEventDestroy(pThis->ReceiveSem);
1120 pThis->ReceiveSem = NIL_RTSEMEVENT;
1121
1122 PDMR3CritSectDelete(&pThis->CritSect);
1123 return VINF_SUCCESS;
1124}
1125
1126
1127/**
1128 * @interface_method_impl{PDMDEVREG, pfnConstruct}
1129 */
1130static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1131{
1132 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1133 int rc;
1134 uint16_t io_base;
1135 uint8_t irq_lvl;
1136
1137 Assert(iInstance < 4);
1138 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1139
1140 /*
1141 * Initialize the instance data.
1142 * (Do this early or the destructor might choke on something!)
1143 */
1144 pThis->pDevInsR3 = pDevIns;
1145 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1146 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1147 pThis->ReceiveSem = NIL_RTSEMEVENT;
1148
1149 /* IBase */
1150 pThis->IBase.pfnQueryInterface = serialQueryInterface;
1151
1152 /* ICharPort */
1153 pThis->ICharPort.pfnNotifyRead = serialNotifyRead;
1154 pThis->ICharPort.pfnNotifyStatusLinesChanged = serialNotifyStatusLinesChanged;
1155 pThis->ICharPort.pfnNotifyBufferFull = serialNotifyBufferFull;
1156 pThis->ICharPort.pfnNotifyBreak = serialNotifyBreak;
1157
1158#ifdef VBOX_SERIAL_PCI
1159 /* the PCI device */
1160 pThis->PciDev.config[0x00] = 0xee; /* Vendor: ??? */
1161 pThis->PciDev.config[0x01] = 0x80;
1162 pThis->PciDev.config[0x02] = 0x01; /* Device: ??? */
1163 pThis->PciDev.config[0x03] = 0x01;
1164 pThis->PciDev.config[0x04] = PCI_COMMAND_IOACCESS;
1165 pThis->PciDev.config[0x09] = 0x01; /* Programming interface: 16450 */
1166 pThis->PciDev.config[0x0a] = 0x00; /* Subclass: Serial controller */
1167 pThis->PciDev.config[0x0b] = 0x07; /* Class: Communication controller */
1168 pThis->PciDev.config[0x0e] = 0x00; /* Header type: standard */
1169 pThis->PciDev.config[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/
1170 pThis->PciDev.config[0x3d] = 1; /* interrupt pin 0 */
1171#endif /* VBOX_SERIAL_PCI */
1172
1173 /*
1174 * Validate and read the configuration.
1175 */
1176 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0"
1177 "IOBase\0"
1178 "GCEnabled\0"
1179 "R0Enabled\0"
1180 "YieldOnLSRRead\0"
1181 "Enable16550A\0"
1182 ))
1183 {
1184 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
1185 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1186 }
1187
1188 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
1189 if (RT_FAILURE(rc))
1190 return PDMDEV_SET_ERROR(pDevIns, rc,
1191 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
1192
1193 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
1194 if (RT_FAILURE(rc))
1195 return PDMDEV_SET_ERROR(pDevIns, rc,
1196 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
1197
1198 rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false);
1199 if (RT_FAILURE(rc))
1200 return PDMDEV_SET_ERROR(pDevIns, rc,
1201 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
1202
1203 rc = CFGMR3QueryU8(pCfg, "IRQ", &irq_lvl);
1204 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1205 {
1206 /* Provide sensible defaults. */
1207 if (iInstance == 0)
1208 irq_lvl = 4;
1209 else if (iInstance == 1)
1210 irq_lvl = 3;
1211 else
1212 AssertReleaseFailed(); /* irq_lvl is undefined. */
1213 }
1214 else if (RT_FAILURE(rc))
1215 return PDMDEV_SET_ERROR(pDevIns, rc,
1216 N_("Configuration error: Failed to get the \"IRQ\" value"));
1217
1218 rc = CFGMR3QueryU16(pCfg, "IOBase", &io_base);
1219 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1220 {
1221 if (iInstance == 0)
1222 io_base = 0x3f8;
1223 else if (iInstance == 1)
1224 io_base = 0x2f8;
1225 else
1226 AssertReleaseFailed(); /* io_base is undefined */
1227 }
1228 else if (RT_FAILURE(rc))
1229 return PDMDEV_SET_ERROR(pDevIns, rc,
1230 N_("Configuration error: Failed to get the \"IOBase\" value"));
1231
1232 Log(("DevSerial: instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
1233
1234 rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &pThis->f16550AEnabled, true);
1235 if (RT_FAILURE(rc))
1236 return PDMDEV_SET_ERROR(pDevIns, rc,
1237 N_("Configuration error: Failed to get the \"Enable16550A\" value"));
1238
1239 pThis->irq = irq_lvl;
1240#ifdef VBOX_SERIAL_PCI
1241 pThis->base = -1;
1242#else
1243 pThis->base = io_base;
1244#endif
1245
1246 LogRel(("Serial#%d: emulating %s\n", pDevIns->iInstance, pThis->f16550AEnabled ? "16550A" : "16450"));
1247
1248 /*
1249 * Initialize critical section and the semaphore. Change the default
1250 * critical section to ours so that TM and IOM will enter it before
1251 * calling us.
1252 *
1253 * Note! This must of be done BEFORE creating timers, registering I/O ports
1254 * and other things which might pick up the default CS or end up
1255 * calling back into the device.
1256 */
1257 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "Serial#%u", iInstance);
1258 AssertRCReturn(rc, rc);
1259
1260 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
1261 AssertRCReturn(rc, rc);
1262
1263 rc = RTSemEventCreate(&pThis->ReceiveSem);
1264 AssertRCReturn(rc, rc);
1265
1266 /*
1267 * Create the timers.
1268 */
1269 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialFifoTimer, pThis,
1270 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "Serial Fifo Timer",
1271 &pThis->fifo_timeout_timer);
1272 AssertRCReturn(rc, rc);
1273
1274 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialTransmitTimer, pThis,
1275 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "Serial Transmit Timer",
1276 &pThis->transmit_timerR3);
1277 AssertRCReturn(rc, rc);
1278 pThis->transmit_timerR0 = TMTimerR0Ptr(pThis->transmit_timerR3);
1279 pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3);
1280
1281 serialReset(pDevIns);
1282
1283#ifdef VBOX_SERIAL_PCI
1284 /*
1285 * Register the PCI Device and region.
1286 */
1287 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
1288 if (RT_FAILURE(rc))
1289 return rc;
1290 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap);
1291 if (RT_FAILURE(rc))
1292 return rc;
1293
1294#else /* !VBOX_SERIAL_PCI */
1295 /*
1296 * Register the I/O ports.
1297 */
1298 pThis->base = io_base;
1299 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
1300 serialIOPortWrite, serialIOPortRead,
1301 NULL, NULL, "SERIAL");
1302 if (RT_FAILURE(rc))
1303 return rc;
1304
1305 if (pThis->fGCEnabled)
1306 {
1307 rc = PDMDevHlpIOPortRegisterRC(pDevIns, io_base, 8, 0, "serialIOPortWrite",
1308 "serialIOPortRead", NULL, NULL, "Serial");
1309 if (RT_FAILURE(rc))
1310 return rc;
1311 }
1312
1313 if (pThis->fR0Enabled)
1314 {
1315 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
1316 "serialIOPortRead", NULL, NULL, "Serial");
1317 if (RT_FAILURE(rc))
1318 return rc;
1319 }
1320#endif /* !VBOX_SERIAL_PCI */
1321
1322 /*
1323 * Saved state.
1324 */
1325 rc = PDMDevHlpSSMRegister3(pDevIns, SERIAL_SAVED_STATE_VERSION, sizeof (*pThis),
1326 serialLiveExec, serialSaveExec, serialLoadExec);
1327 if (RT_FAILURE(rc))
1328 return rc;
1329
1330 /*
1331 * Attach the char driver and get the interfaces.
1332 * For now no run-time changes are supported.
1333 */
1334 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
1335 if (RT_SUCCESS(rc))
1336 {
1337 pThis->pDrvChar = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMICHARCONNECTOR);
1338 if (!pThis->pDrvChar)
1339 {
1340 AssertLogRelMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
1341 return VERR_PDM_MISSING_INTERFACE;
1342 }
1343 /** @todo provide read notification interface!!!! */
1344 }
1345 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1346 {
1347 pThis->pDrvBase = NULL;
1348 pThis->pDrvChar = NULL;
1349 LogRel(("Serial%d: no unit\n", iInstance));
1350 }
1351 else
1352 {
1353 AssertLogRelMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc));
1354 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
1355 return rc;
1356 }
1357
1358 return VINF_SUCCESS;
1359}
1360
1361
1362/**
1363 * The device registration structure.
1364 */
1365const PDMDEVREG g_DeviceSerialPort =
1366{
1367 /* u32Version */
1368 PDM_DEVREG_VERSION,
1369 /* szName */
1370 "serial",
1371 /* szRCMod */
1372 "VBoxDDGC.gc",
1373 /* szR0Mod */
1374 "VBoxDDR0.r0",
1375 /* pszDescription */
1376 "Serial Communication Port",
1377 /* fFlags */
1378 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1379 /* fClass */
1380 PDM_DEVREG_CLASS_SERIAL,
1381 /* cMaxInstances */
1382 UINT32_MAX,
1383 /* cbInstance */
1384 sizeof(DEVSERIAL),
1385 /* pfnConstruct */
1386 serialConstruct,
1387 /* pfnDestruct */
1388 serialDestruct,
1389 /* pfnRelocate */
1390 serialRelocate,
1391 /* pfnMemSetup */
1392 NULL,
1393 /* pfnPowerOn */
1394 NULL,
1395 /* pfnReset */
1396 serialReset,
1397 /* pfnSuspend */
1398 NULL,
1399 /* pfnResume */
1400 NULL,
1401 /* pfnAttach */
1402 NULL,
1403 /* pfnDetach */
1404 NULL,
1405 /* pfnQueryInterface. */
1406 NULL,
1407 /* pfnInitComplete */
1408 NULL,
1409 /* pfnPowerOff */
1410 NULL,
1411 /* pfnSoftReset */
1412 NULL,
1413 /* u32VersionEnd */
1414 PDM_DEVREG_VERSION
1415};
1416#endif /* IN_RING3 */
1417
1418#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