VirtualBox

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

Last change on this file since 55747 was 50876, checked in by vboxsync, 11 years ago

DevSerial.cpp: Added missing bits of state info to the saved state. Was causing windbg to be horribly slow when attaching after restoring a snapshot. (guest w8)

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