VirtualBox

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

Last change on this file since 28185 was 28101, checked in by vboxsync, 15 years ago

serial: Let the UART device signal if we are not ready to consume more data. Should fix lost characters when working with host hardware devices.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.1 KB
Line 
1/* $Id: DevSerial.cpp 28101 2010-04-08 15:42:36Z vboxsync $ */
2/** @file
3 * DevSerial - 16450 UART emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*
23 * This code is based on:
24 *
25 * QEMU 16450 UART emulation
26 *
27 * Copyright (c) 2003-2004 Fabrice Bellard
28 *
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
35 *
36 * The above copyright notice and this permission notice shall be included in
37 * all copies or substantial portions of the Software.
38 *
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
42 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45 * THE SOFTWARE.
46 *
47 */
48
49/*******************************************************************************
50* Header Files *
51*******************************************************************************/
52#define LOG_GROUP LOG_GROUP_DEV_SERIAL
53#include <VBox/pdmdev.h>
54#include <iprt/assert.h>
55#include <iprt/uuid.h>
56#include <iprt/string.h>
57#include <iprt/semaphore.h>
58#include <iprt/critsect.h>
59
60#include "../Builtins.h"
61
62#undef VBOX_SERIAL_PCI /* The PCI variant has lots of problems: wrong IRQ line and wrong IO base assigned. */
63
64#ifdef VBOX_SERIAL_PCI
65# include <VBox/pci.h>
66#endif /* VBOX_SERIAL_PCI */
67
68
69/*******************************************************************************
70* Defined Constants And Macros *
71*******************************************************************************/
72#define SERIAL_SAVED_STATE_VERSION 3
73
74#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
75
76#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
77#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
78#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
79#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
80
81#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
82#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
83
84#define UART_IIR_MSI 0x00 /* Modem status interrupt */
85#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
86#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
87#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
88
89/*
90 * These are the definitions for the Modem Control Register
91 */
92#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
93#define UART_MCR_OUT2 0x08 /* Out2 complement */
94#define UART_MCR_OUT1 0x04 /* Out1 complement */
95#define UART_MCR_RTS 0x02 /* RTS complement */
96#define UART_MCR_DTR 0x01 /* DTR complement */
97
98/*
99 * These are the definitions for the Modem Status Register
100 */
101#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
102#define UART_MSR_RI 0x40 /* Ring Indicator */
103#define UART_MSR_DSR 0x20 /* Data Set Ready */
104#define UART_MSR_CTS 0x10 /* Clear to Send */
105#define UART_MSR_DDCD 0x08 /* Delta DCD */
106#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
107#define UART_MSR_DDSR 0x02 /* Delta DSR */
108#define UART_MSR_DCTS 0x01 /* Delta CTS */
109#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
110
111#define UART_LSR_TEMT 0x40 /* Transmitter empty */
112#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
113#define UART_LSR_BI 0x10 /* Break interrupt indicator */
114#define UART_LSR_FE 0x08 /* Frame error indicator */
115#define UART_LSR_PE 0x04 /* Parity error indicator */
116#define UART_LSR_OE 0x02 /* Overrun error indicator */
117#define UART_LSR_DR 0x01 /* Receiver data ready */
118
119
120/*******************************************************************************
121* Structures and Typedefs *
122*******************************************************************************/
123/**
124 * Serial device.
125 *
126 * @implements PDMIBASE
127 * @implements PDMICHARPORT
128 */
129struct SerialState
130{
131 /** Access critical section. */
132 PDMCRITSECT CritSect;
133
134 /** Pointer to the device instance - R3 Ptr. */
135 PPDMDEVINSR3 pDevInsR3;
136 /** Pointer to the device instance - R0 Ptr. */
137 PPDMDEVINSR0 pDevInsR0;
138 /** Pointer to the device instance - RC Ptr. */
139 PPDMDEVINSRC pDevInsRC;
140 RTRCPTR Alignment0; /**< Alignment. */
141 /** LUN\#0: The base interface. */
142 PDMIBASE IBase;
143 /** LUN\#0: The character port interface. */
144 PDMICHARPORT ICharPort;
145 /** Pointer to the attached base driver. */
146 R3PTRTYPE(PPDMIBASE) pDrvBase;
147 /** Pointer to the attached character driver. */
148 R3PTRTYPE(PPDMICHARCONNECTOR) pDrvChar;
149
150 uint16_t divider;
151 uint16_t auAlignment[3];
152 uint8_t rbr; /* receive register */
153 uint8_t ier;
154 uint8_t iir; /* read only */
155 uint8_t lcr;
156 uint8_t mcr;
157 uint8_t lsr; /* read only */
158 uint8_t msr; /* read only */
159 uint8_t scr;
160 /* NOTE: this hidden state is necessary for tx irq generation as
161 it can be reset while reading iir */
162 int thr_ipending;
163 int irq;
164 bool msr_changed;
165
166 bool fGCEnabled;
167 bool fR0Enabled;
168 bool fYieldOnLSRRead;
169 bool afAlignment[4];
170
171 RTSEMEVENT ReceiveSem;
172 int last_break_enable;
173 uint32_t base;
174
175#ifdef VBOX_SERIAL_PCI
176 PCIDEVICE dev;
177#endif /* VBOX_SERIAL_PCI */
178};
179
180#ifndef VBOX_DEVICE_STRUCT_TESTCASE
181
182
183#ifdef VBOX_SERIAL_PCI
184#define PCIDEV_2_SERIALSTATE(pPciDev) ( (SerialState *)((uintptr_t)(pPciDev) - RT_OFFSETOF(SerialState, dev)) )
185#endif /* VBOX_SERIAL_PCI */
186#define PDMIBASE_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, IBase)) )
187#define PDMICHARPORT_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, ICharPort)) )
188
189
190/*******************************************************************************
191* Internal Functions *
192*******************************************************************************/
193RT_C_DECLS_BEGIN
194PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
195PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
196RT_C_DECLS_END
197
198#ifdef IN_RING3
199
200static void serial_update_irq(SerialState *s)
201{
202 if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
203 s->iir = UART_IIR_RDI;
204 } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
205 s->iir = UART_IIR_THRI;
206 } else if (s->msr_changed && (s->ier & UART_IER_RLSI)) {
207 s->iir = UART_IIR_RLSI;
208 } else if (s->lsr & UART_LSR_BI) {
209 s->iir = 0; /* No special status bit */
210 } else {
211 s->iir = UART_IIR_NO_INT;
212 }
213 if (s->iir != UART_IIR_NO_INT) {
214 Log(("serial_update_irq %d 1\n", s->irq));
215# ifdef VBOX_SERIAL_PCI
216 PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 1);
217# else /* !VBOX_SERIAL_PCI */
218 PDMDevHlpISASetIrqNoWait(s->CTX_SUFF(pDevIns), s->irq, 1);
219# endif /* !VBOX_SERIAL_PCI */
220 } else {
221 Log(("serial_update_irq %d 0\n", s->irq));
222# ifdef VBOX_SERIAL_PCI
223 PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 0);
224# else /* !VBOX_SERIAL_PCI */
225 PDMDevHlpISASetIrqNoWait(s->CTX_SUFF(pDevIns), s->irq, 0);
226# endif /* !VBOX_SERIAL_PCI */
227 }
228}
229
230static void serial_update_parameters(SerialState *s)
231{
232 int speed, parity, data_bits, stop_bits;
233
234 if (s->lcr & 0x08) {
235 if (s->lcr & 0x10)
236 parity = 'E';
237 else
238 parity = 'O';
239 } else {
240 parity = 'N';
241 }
242 if (s->lcr & 0x04)
243 stop_bits = 2;
244 else
245 stop_bits = 1;
246 data_bits = (s->lcr & 0x03) + 5;
247 if (s->divider == 0)
248 return;
249 speed = 115200 / s->divider;
250 Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
251 if (RT_LIKELY(s->pDrvChar))
252 s->pDrvChar->pfnSetParameters(s->pDrvChar, speed, parity, data_bits, stop_bits);
253}
254
255#endif /* IN_RING3 */
256
257static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
258{
259 SerialState *s = (SerialState *)opaque;
260 unsigned char ch;
261
262 addr &= 7;
263 LogFlow(("serial: write addr=0x%02x val=0x%02x\n", addr, val));
264
265#ifndef IN_RING3
266 NOREF(ch);
267 NOREF(s);
268 return VINF_IOM_HC_IOPORT_WRITE;
269#else
270 switch(addr) {
271 default:
272 case 0:
273 if (s->lcr & UART_LCR_DLAB) {
274 s->divider = (s->divider & 0xff00) | val;
275 serial_update_parameters(s);
276 } else if (s->lsr & UART_LSR_THRE) {
277 s->thr_ipending = 0;
278 ch = val;
279 if (RT_LIKELY(s->pDrvChar))
280 {
281 Log(("serial_ioport_write: write 0x%X\n", ch));
282 int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
283 AssertRC(rc);
284 }
285 s->thr_ipending = 1;
286 serial_update_irq(s);
287 } else
288 Log(("serial: THR not EMPTY!\n"));
289 break;
290 case 1:
291 if (s->lcr & UART_LCR_DLAB) {
292 s->divider = (s->divider & 0x00ff) | (val << 8);
293 serial_update_parameters(s);
294 } else {
295 s->ier = val & 0x0f;
296 if (s->lsr & UART_LSR_THRE) {
297 s->thr_ipending = 1;
298 }
299 serial_update_irq(s);
300 }
301 break;
302 case 2:
303 break;
304 case 3:
305 {
306 int break_enable;
307 if (s->lcr != val)
308 {
309 s->lcr = val;
310 serial_update_parameters(s);
311 }
312 break_enable = (val >> 6) & 1;
313 if (break_enable != s->last_break_enable) {
314 s->last_break_enable = break_enable;
315 if (RT_LIKELY(s->pDrvChar))
316 {
317 Log(("serial_ioport_write: Set break %d\n", break_enable));
318 int rc = s->pDrvChar->pfnSetBreak(s->pDrvChar, !!break_enable);
319 AssertRC(rc);
320 }
321 }
322 }
323 break;
324 case 4:
325 s->mcr = val & 0x1f;
326 if (RT_LIKELY(s->pDrvChar))
327 {
328 int rc = s->pDrvChar->pfnSetModemLines(s->pDrvChar, !!(s->mcr & UART_MCR_RTS), !!(s->mcr & UART_MCR_DTR));
329 AssertRC(rc);
330 }
331 break;
332 case 5:
333 break;
334 case 6:
335 break;
336 case 7:
337 s->scr = val;
338 break;
339 }
340 return VINF_SUCCESS;
341#endif
342}
343
344static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
345{
346 SerialState *s = (SerialState *)opaque;
347 uint32_t ret = ~0U;
348
349 *pRC = VINF_SUCCESS;
350
351 addr &= 7;
352 switch(addr) {
353 default:
354 case 0:
355 if (s->lcr & UART_LCR_DLAB) {
356 ret = s->divider & 0xff;
357 } else {
358#ifndef IN_RING3
359 *pRC = VINF_IOM_HC_IOPORT_READ;
360#else
361 Log(("serial_io_port_read: read 0x%X\n", s->rbr));
362 ret = s->rbr;
363 s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
364 serial_update_irq(s);
365 {
366 int rc = RTSemEventSignal(s->ReceiveSem);
367 AssertRC(rc);
368 }
369#endif
370 }
371 break;
372 case 1:
373 if (s->lcr & UART_LCR_DLAB) {
374 ret = (s->divider >> 8) & 0xff;
375 } else {
376 ret = s->ier;
377 }
378 break;
379 case 2:
380#ifndef IN_RING3
381 *pRC = VINF_IOM_HC_IOPORT_READ;
382#else
383 ret = s->iir;
384 /* reset THR pending bit */
385 if ((ret & 0x7) == UART_IIR_THRI)
386 s->thr_ipending = 0;
387 /* reset msr changed bit */
388 s->msr_changed = false;
389 serial_update_irq(s);
390#endif
391 break;
392 case 3:
393 ret = s->lcr;
394 break;
395 case 4:
396 ret = s->mcr;
397 break;
398 case 5:
399 if ((s->lsr & UART_LSR_DR) == 0 && s->fYieldOnLSRRead)
400 {
401 /* No data available and yielding is enabled, so yield in ring3. */
402#ifndef IN_RING3
403 *pRC = VINF_IOM_HC_IOPORT_READ;
404 break;
405#else
406 RTThreadYield ();
407#endif
408 }
409 ret = s->lsr;
410 break;
411 case 6:
412 if (s->mcr & UART_MCR_LOOP) {
413 /* in loopback, the modem output pins are connected to the
414 inputs */
415 ret = (s->mcr & 0x0c) << 4;
416 ret |= (s->mcr & 0x02) << 3;
417 ret |= (s->mcr & 0x01) << 5;
418 } else {
419 ret = s->msr;
420 /* Reset delta bits. */
421 s->msr &= ~UART_MSR_ANY_DELTA;
422 }
423 break;
424 case 7:
425 ret = s->scr;
426 break;
427 }
428 LogFlow(("serial: read addr=0x%02x val=0x%02x\n", addr, ret));
429 return ret;
430}
431
432#ifdef IN_RING3
433
434static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
435{
436 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
437 int rc;
438
439 Assert(*pcbRead != 0);
440
441 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
442 if (pThis->lsr & UART_LSR_DR)
443 {
444 /* If a character is still in the read queue, then wait for it to be emptied. */
445 PDMCritSectLeave(&pThis->CritSect);
446 rc = RTSemEventWait(pThis->ReceiveSem, 250);
447 if (RT_FAILURE(rc))
448 return rc;
449
450 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
451 }
452
453 if (!(pThis->lsr & UART_LSR_DR))
454 {
455 pThis->rbr = *(const char *)pvBuf;
456 pThis->lsr |= UART_LSR_DR;
457 serial_update_irq(pThis);
458 *pcbRead = 1;
459 rc = VINF_SUCCESS;
460 }
461 else
462 rc = VERR_TIMEOUT;
463
464 PDMCritSectLeave(&pThis->CritSect);
465
466 return rc;
467}
468
469static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines)
470{
471 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
472 uint8_t newMsr = 0;
473
474 Log(("%s: pInterface=%p newStatusLines=%u\n", __FUNCTION__, pInterface, newStatusLines));
475
476 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
477
478 /* Set new states. */
479 if (newStatusLines & PDMICHARPORT_STATUS_LINES_DCD)
480 newMsr |= UART_MSR_DCD;
481 if (newStatusLines & PDMICHARPORT_STATUS_LINES_RI)
482 newMsr |= UART_MSR_RI;
483 if (newStatusLines & PDMICHARPORT_STATUS_LINES_DSR)
484 newMsr |= UART_MSR_DSR;
485 if (newStatusLines & PDMICHARPORT_STATUS_LINES_CTS)
486 newMsr |= UART_MSR_CTS;
487
488 /* Compare the old and the new states and set the delta bits accordingly. */
489 if ((newMsr & UART_MSR_DCD) != (pThis->msr & UART_MSR_DCD))
490 newMsr |= UART_MSR_DDCD;
491 if ((newMsr & UART_MSR_RI) == 1 && (pThis->msr & UART_MSR_RI) == 0)
492 newMsr |= UART_MSR_TERI;
493 if ((newMsr & UART_MSR_DSR) != (pThis->msr & UART_MSR_DSR))
494 newMsr |= UART_MSR_DDSR;
495 if ((newMsr & UART_MSR_CTS) != (pThis->msr & UART_MSR_CTS))
496 newMsr |= UART_MSR_DCTS;
497
498 pThis->msr = newMsr;
499 pThis->msr_changed = true;
500 serial_update_irq(pThis);
501
502 PDMCritSectLeave(&pThis->CritSect);
503
504 return VINF_SUCCESS;
505}
506
507static DECLCALLBACK(int) serialNotifyBufferFull(PPDMICHARPORT pInterface, bool fFull)
508{
509 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
510 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
511 if (fFull)
512 {
513 pThis->lsr &= ~UART_LSR_THRE;
514 }
515 else
516 {
517 pThis->thr_ipending = 1;
518 pThis->lsr |= UART_LSR_THRE;
519 pThis->lsr |= UART_LSR_TEMT;
520 }
521 serial_update_irq(pThis);
522 PDMCritSectLeave(&pThis->CritSect);
523 return VINF_SUCCESS;
524}
525
526static DECLCALLBACK(int) serialNotifyBreak(PPDMICHARPORT pInterface)
527{
528 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
529
530 Log(("%s: pInterface=%p\n", __FUNCTION__, pInterface));
531
532 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
533
534 pThis->lsr |= UART_LSR_BI;
535 serial_update_irq(pThis);
536
537 PDMCritSectLeave(&pThis->CritSect);
538
539 return VINF_SUCCESS;
540}
541
542#endif /* IN_RING3 */
543
544/**
545 * Port I/O Handler for OUT operations.
546 *
547 * @returns VBox status code.
548 *
549 * @param pDevIns The device instance.
550 * @param pvUser User argument.
551 * @param Port Port number used for the IN operation.
552 * @param u32 The value to output.
553 * @param cb The value size in bytes.
554 */
555PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
556 RTIOPORT Port, uint32_t u32, unsigned cb)
557{
558 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
559 int rc = VINF_SUCCESS;
560
561 if (cb == 1)
562 {
563 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_WRITE);
564 if (rc == VINF_SUCCESS)
565 {
566 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
567 rc = serial_ioport_write(pThis, Port, u32);
568 PDMCritSectLeave(&pThis->CritSect);
569 }
570 }
571 else
572 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
573
574 return rc;
575}
576
577/**
578 * Port I/O Handler for IN operations.
579 *
580 * @returns VBox status code.
581 *
582 * @param pDevIns The device instance.
583 * @param pvUser User argument.
584 * @param Port Port number used for the IN operation.
585 * @param u32 The value to output.
586 * @param cb The value size in bytes.
587 */
588PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
589 RTIOPORT Port, uint32_t *pu32, unsigned cb)
590{
591 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
592 int rc = VINF_SUCCESS;
593
594 if (cb == 1)
595 {
596 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_READ);
597 if (rc == VINF_SUCCESS)
598 {
599 *pu32 = serial_ioport_read(pThis, Port, &rc);
600 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
601 PDMCritSectLeave(&pThis->CritSect);
602 }
603 }
604 else
605 rc = VERR_IOM_IOPORT_UNUSED;
606
607 return rc;
608}
609
610#ifdef IN_RING3
611
612/**
613 * @copydoc FNSSMDEVLIVEEXEC
614 */
615static DECLCALLBACK(int) serialLiveExec(PPDMDEVINS pDevIns,
616 PSSMHANDLE pSSM,
617 uint32_t uPass)
618{
619 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
620 SSMR3PutS32(pSSM, pThis->irq);
621 SSMR3PutU32(pSSM, pThis->base);
622 return VINF_SSM_DONT_CALL_AGAIN;
623}
624
625/**
626 * @copydoc FNSSMDEVSAVEEXEC
627 */
628static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns,
629 PSSMHANDLE pSSM)
630{
631 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
632
633 SSMR3PutU16(pSSM, pThis->divider);
634 SSMR3PutU8(pSSM, pThis->rbr);
635 SSMR3PutU8(pSSM, pThis->ier);
636 SSMR3PutU8(pSSM, pThis->lcr);
637 SSMR3PutU8(pSSM, pThis->mcr);
638 SSMR3PutU8(pSSM, pThis->lsr);
639 SSMR3PutU8(pSSM, pThis->msr);
640 SSMR3PutU8(pSSM, pThis->scr);
641 SSMR3PutS32(pSSM, pThis->thr_ipending);
642 SSMR3PutS32(pSSM, pThis->irq);
643 SSMR3PutS32(pSSM, pThis->last_break_enable);
644 SSMR3PutU32(pSSM, pThis->base);
645 SSMR3PutBool(pSSM, pThis->msr_changed);
646 return SSMR3PutU32(pSSM, ~0); /* sanity/terminator */
647}
648
649/**
650 * @copydoc FNSSMDEVLOADEXEC
651 */
652static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
653 PSSMHANDLE pSSM,
654 uint32_t uVersion,
655 uint32_t uPass)
656{
657 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
658
659 AssertMsgReturn(uVersion == SERIAL_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
660
661 if (uPass == SSM_PASS_FINAL)
662 {
663 SSMR3GetU16(pSSM, &pThis->divider);
664 SSMR3GetU8(pSSM, &pThis->rbr);
665 SSMR3GetU8(pSSM, &pThis->ier);
666 SSMR3GetU8(pSSM, &pThis->lcr);
667 SSMR3GetU8(pSSM, &pThis->mcr);
668 SSMR3GetU8(pSSM, &pThis->lsr);
669 SSMR3GetU8(pSSM, &pThis->msr);
670 SSMR3GetU8(pSSM, &pThis->scr);
671 SSMR3GetS32(pSSM, &pThis->thr_ipending);
672 }
673
674 int32_t iIrq;
675 SSMR3GetS32(pSSM, &iIrq);
676
677 if (uPass == SSM_PASS_FINAL)
678 SSMR3GetS32(pSSM, &pThis->last_break_enable);
679
680 uint32_t IOBase;
681 int rc = SSMR3GetU32(pSSM, &IOBase);
682 AssertRCReturn(rc, rc);
683
684 if ( pThis->irq != iIrq
685 || pThis->base != IOBase)
686 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - saved irq=%#x iobase=%#x; configured irq=%#x iobase=%#x"),
687 iIrq, IOBase, pThis->irq, pThis->base);
688
689 if (uPass == SSM_PASS_FINAL)
690 {
691 SSMR3GetBool(pSSM, &pThis->msr_changed);
692
693 uint32_t u32;
694 rc = SSMR3GetU32(pSSM, &u32);
695 if (RT_FAILURE(rc))
696 return rc;
697 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
698
699 if (pThis->lsr & UART_LSR_DR)
700 {
701 rc = RTSemEventSignal(pThis->ReceiveSem);
702 AssertRC(rc);
703 }
704
705 /* this isn't strictly necessary but cannot hurt... */
706 pThis->pDevInsR3 = pDevIns;
707 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
708 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
709 }
710
711 return VINF_SUCCESS;
712}
713
714
715/**
716 * @copydoc FNPDMDEVRELOCATE
717 */
718static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
719{
720 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
721 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
722}
723
724#ifdef VBOX_SERIAL_PCI
725
726static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned */ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
727{
728 SerialState *pThis = PCIDEV_2_SERIALSTATE(pPciDev);
729 int rc = VINF_SUCCESS;
730
731 Assert(enmType == PCI_ADDRESS_SPACE_IO);
732 Assert(iRegion == 0);
733 Assert(cb == 8);
734 AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
735
736 pThis->base = (RTIOPORT)GCPhysAddress;
737 LogRel(("Serial#%d: mapping I/O at %#06x\n", pThis->pDevIns->iInstance, pThis->base));
738
739 /*
740 * Register our port IO handlers.
741 */
742 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pThis,
743 serial_io_write, serial_io_read, NULL, NULL, "SERIAL");
744 AssertRC(rc);
745 return rc;
746}
747
748#endif /* VBOX_SERIAL_PCI */
749
750/**
751 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
752 */
753static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
754{
755 SerialState *pThis = PDMIBASE_2_SERIALSTATE(pInterface);
756 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
757 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARPORT, &pThis->ICharPort);
758 return NULL;
759}
760
761/**
762 * Destruct a device instance.
763 *
764 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
765 * resources can be freed correctly.
766 *
767 * @returns VBox status.
768 * @param pDevIns The device instance data.
769 */
770static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns)
771{
772 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
773 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
774
775 RTSemEventDestroy(pThis->ReceiveSem);
776 pThis->ReceiveSem = NIL_RTSEMEVENT;
777
778 PDMR3CritSectDelete(&pThis->CritSect);
779 return VINF_SUCCESS;
780}
781
782
783/**
784 * @interface_method_impl{PDMDEVREG,pfnConstruct}
785 */
786static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
787 int iInstance,
788 PCFGMNODE pCfg)
789{
790 int rc;
791 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState*);
792 uint16_t io_base;
793 uint8_t irq_lvl;
794
795 Assert(iInstance < 4);
796 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
797
798 /*
799 * Initialize the instance data.
800 * (Do this early or the destructor might choke on something!)
801 */
802 pThis->pDevInsR3 = pDevIns;
803 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
804 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
805
806 pThis->lsr = UART_LSR_TEMT | UART_LSR_THRE;
807 pThis->iir = UART_IIR_NO_INT;
808 pThis->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
809
810 /* IBase */
811 pThis->IBase.pfnQueryInterface = serialQueryInterface;
812
813 /* ICharPort */
814 pThis->ICharPort.pfnNotifyRead = serialNotifyRead;
815 pThis->ICharPort.pfnNotifyStatusLinesChanged = serialNotifyStatusLinesChanged;
816 pThis->ICharPort.pfnNotifyBufferFull = serialNotifyBufferFull;
817 pThis->ICharPort.pfnNotifyBreak = serialNotifyBreak;
818
819#ifdef VBOX_SERIAL_PCI
820 /* the PCI device */
821 pThis->dev.config[0x00] = 0xee; /* Vendor: ??? */
822 pThis->dev.config[0x01] = 0x80;
823 pThis->dev.config[0x02] = 0x01; /* Device: ??? */
824 pThis->dev.config[0x03] = 0x01;
825 pThis->dev.config[0x04] = PCI_COMMAND_IOACCESS;
826 pThis->dev.config[0x09] = 0x01; /* Programming interface: 16450 */
827 pThis->dev.config[0x0a] = 0x00; /* Subclass: Serial controller */
828 pThis->dev.config[0x0b] = 0x07; /* Class: Communication controller */
829 pThis->dev.config[0x0e] = 0x00; /* Header type: standard */
830 pThis->dev.config[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/
831 pThis->dev.config[0x3d] = 1; /* interrupt pin 0 */
832#endif /* VBOX_SERIAL_PCI */
833
834 /*
835 * Validate and read the configuration.
836 */
837 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0" "IOBase\0" "GCEnabled\0" "R0Enabled\0" "YieldOnLSRRead\0"))
838 {
839 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
840 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
841 }
842
843 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
844 if (RT_FAILURE(rc))
845 return PDMDEV_SET_ERROR(pDevIns, rc,
846 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
847
848 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
849 if (RT_FAILURE(rc))
850 return PDMDEV_SET_ERROR(pDevIns, rc,
851 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
852
853 rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false);
854 if (RT_FAILURE(rc))
855 return PDMDEV_SET_ERROR(pDevIns, rc,
856 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
857
858 rc = CFGMR3QueryU8(pCfg, "IRQ", &irq_lvl);
859 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
860 {
861 /* Provide sensible defaults. */
862 if (iInstance == 0)
863 irq_lvl = 4;
864 else if (iInstance == 1)
865 irq_lvl = 3;
866 else
867 AssertReleaseFailed(); /* irq_lvl is undefined. */
868 }
869 else if (RT_FAILURE(rc))
870 return PDMDEV_SET_ERROR(pDevIns, rc,
871 N_("Configuration error: Failed to get the \"IRQ\" value"));
872
873 rc = CFGMR3QueryU16(pCfg, "IOBase", &io_base);
874 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
875 {
876 if (iInstance == 0)
877 io_base = 0x3f8;
878 else if (iInstance == 1)
879 io_base = 0x2f8;
880 else
881 AssertReleaseFailed(); /* io_base is undefined */
882 }
883 else if (RT_FAILURE(rc))
884 return PDMDEV_SET_ERROR(pDevIns, rc,
885 N_("Configuration error: Failed to get the \"IOBase\" value"));
886
887 Log(("DevSerial: instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
888
889 pThis->irq = irq_lvl;
890#ifdef VBOX_SERIAL_PCI
891 pThis->base = -1;
892#else
893 pThis->base = io_base;
894#endif
895
896 /*
897 * Initialize critical section and the semaphore.
898 * This must of course be done before attaching drivers or anything else which can call us back..
899 */
900 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "Serial#%d", iInstance);
901 if (RT_FAILURE(rc))
902 return rc;
903
904 rc = RTSemEventCreate(&pThis->ReceiveSem);
905 AssertRC(rc);
906
907#ifdef VBOX_SERIAL_PCI
908 /*
909 * Register the PCI Device and region.
910 */
911 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev);
912 if (RT_FAILURE(rc))
913 return rc;
914 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap);
915 if (RT_FAILURE(rc))
916 return rc;
917
918#else /* !VBOX_SERIAL_PCI */
919 /*
920 * Register the I/O ports.
921 */
922 pThis->base = io_base;
923 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
924 serialIOPortWrite, serialIOPortRead,
925 NULL, NULL, "SERIAL");
926 if (RT_FAILURE(rc))
927 return rc;
928
929 if (pThis->fGCEnabled)
930 {
931 rc = PDMDevHlpIOPortRegisterRC(pDevIns, io_base, 8, 0, "serialIOPortWrite",
932 "serialIOPortRead", NULL, NULL, "Serial");
933 if (RT_FAILURE(rc))
934 return rc;
935 }
936
937
938 if (pThis->fR0Enabled)
939 {
940 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
941 "serialIOPortRead", NULL, NULL, "Serial");
942 if (RT_FAILURE(rc))
943 return rc;
944 }
945#endif /* !VBOX_SERIAL_PCI */
946
947 /*
948 * Saved state.
949 */
950 rc = PDMDevHlpSSMRegister3(pDevIns, SERIAL_SAVED_STATE_VERSION, sizeof (*pThis),
951 serialLiveExec, serialSaveExec, serialLoadExec);
952 if (RT_FAILURE(rc))
953 return rc;
954
955 /*
956 * Attach the char driver and get the interfaces.
957 * For now no run-time changes are supported.
958 */
959 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
960 if (RT_SUCCESS(rc))
961 {
962 pThis->pDrvChar = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMICHARCONNECTOR);
963 if (!pThis->pDrvChar)
964 {
965 AssertLogRelMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
966 return VERR_PDM_MISSING_INTERFACE;
967 }
968 /** @todo provide read notification interface!!!! */
969 }
970 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
971 {
972 pThis->pDrvBase = NULL;
973 pThis->pDrvChar = NULL;
974 LogRel(("Serial%d: no unit\n", iInstance));
975 }
976 else
977 {
978 AssertLogRelMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc));
979 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
980 return rc;
981 }
982
983 return VINF_SUCCESS;
984}
985
986
987/**
988 * The device registration structure.
989 */
990const PDMDEVREG g_DeviceSerialPort =
991{
992 /* u32Version */
993 PDM_DEVREG_VERSION,
994 /* szName */
995 "serial",
996 /* szRCMod */
997 "VBoxDDGC.gc",
998 /* szR0Mod */
999 "VBoxDDR0.r0",
1000 /* pszDescription */
1001 "Serial Communication Port",
1002 /* fFlags */
1003 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1004 /* fClass */
1005 PDM_DEVREG_CLASS_SERIAL,
1006 /* cMaxInstances */
1007 UINT32_MAX,
1008 /* cbInstance */
1009 sizeof(SerialState),
1010 /* pfnConstruct */
1011 serialConstruct,
1012 /* pfnDestruct */
1013 serialDestruct,
1014 /* pfnRelocate */
1015 serialRelocate,
1016 /* pfnIOCtl */
1017 NULL,
1018 /* pfnPowerOn */
1019 NULL,
1020 /* pfnReset */
1021 NULL,
1022 /* pfnSuspend */
1023 NULL,
1024 /* pfnResume */
1025 NULL,
1026 /* pfnAttach */
1027 NULL,
1028 /* pfnDetach */
1029 NULL,
1030 /* pfnQueryInterface. */
1031 NULL,
1032 /* pfnInitComplete */
1033 NULL,
1034 /* pfnPowerOff */
1035 NULL,
1036 /* pfnSoftReset */
1037 NULL,
1038 /* u32VersionEnd */
1039 PDM_DEVREG_VERSION
1040};
1041#endif /* IN_RING3 */
1042
1043
1044#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