VirtualBox

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

Last change on this file since 17995 was 16583, checked in by vboxsync, 16 years ago

CFGM option YieldOnLSRRead for the virtual COM device, helps with Windows guest kernel debugger (xTracker #3612).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/* $Id: DevSerial.cpp 16583 2009-02-09 13:01:35Z 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*******************************************************************************/
123struct SerialState
124{
125 /** Access critical section. */
126 PDMCRITSECT CritSect;
127
128 /** Pointer to the device instance - R3 Ptr. */
129 PPDMDEVINSR3 pDevInsR3;
130 /** Pointer to the device instance - R0 Ptr. */
131 PPDMDEVINSR0 pDevInsR0;
132 /** Pointer to the device instance - RC Ptr. */
133 PPDMDEVINSRC pDevInsRC;
134 RTRCPTR Alignment0; /**< Alignment. */
135 /** The base interface. */
136 PDMIBASE IBase;
137 /** The character port interface. */
138 PDMICHARPORT ICharPort;
139 /** Pointer to the attached base driver. */
140 R3PTRTYPE(PPDMIBASE) pDrvBase;
141 /** Pointer to the attached character driver. */
142 R3PTRTYPE(PPDMICHAR) pDrvChar;
143
144 uint16_t divider;
145 uint16_t auAlignment[3];
146 uint8_t rbr; /* receive register */
147 uint8_t ier;
148 uint8_t iir; /* read only */
149 uint8_t lcr;
150 uint8_t mcr;
151 uint8_t lsr; /* read only */
152 uint8_t msr; /* read only */
153 uint8_t scr;
154 /* NOTE: this hidden state is necessary for tx irq generation as
155 it can be reset while reading iir */
156 int thr_ipending;
157 int irq;
158 bool msr_changed;
159
160 bool fGCEnabled;
161 bool fR0Enabled;
162 bool fYieldOnLSRRead;
163 bool afAlignment[4];
164
165 RTSEMEVENT ReceiveSem;
166 int last_break_enable;
167 uint32_t base;
168
169#ifdef VBOX_SERIAL_PCI
170 PCIDEVICE dev;
171#endif /* VBOX_SERIAL_PCI */
172};
173
174#ifndef VBOX_DEVICE_STRUCT_TESTCASE
175
176
177#ifdef VBOX_SERIAL_PCI
178#define PCIDEV_2_SERIALSTATE(pPciDev) ( (SerialState *)((uintptr_t)(pPciDev) - RT_OFFSETOF(SerialState, dev)) )
179#endif /* VBOX_SERIAL_PCI */
180#define PDMIBASE_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, IBase)) )
181#define PDMICHARPORT_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, ICharPort)) )
182
183
184/*******************************************************************************
185* Internal Functions *
186*******************************************************************************/
187__BEGIN_DECLS
188PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
189PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
190__END_DECLS
191
192#ifdef IN_RING3
193
194static void serial_update_irq(SerialState *s)
195{
196 if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
197 s->iir = UART_IIR_RDI;
198 } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
199 s->iir = UART_IIR_THRI;
200 } else if (s->msr_changed && (s->ier & UART_IER_RLSI)) {
201 s->iir = UART_IIR_RLSI;
202 } else {
203 s->iir = UART_IIR_NO_INT;
204 }
205 if (s->iir != UART_IIR_NO_INT) {
206 Log(("serial_update_irq %d 1\n", s->irq));
207# ifdef VBOX_SERIAL_PCI
208 PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 1);
209# else /* !VBOX_SERIAL_PCI */
210 PDMDevHlpISASetIrqNoWait(s->CTX_SUFF(pDevIns), s->irq, 1);
211# endif /* !VBOX_SERIAL_PCI */
212 } else {
213 Log(("serial_update_irq %d 0\n", s->irq));
214# ifdef VBOX_SERIAL_PCI
215 PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 0);
216# else /* !VBOX_SERIAL_PCI */
217 PDMDevHlpISASetIrqNoWait(s->CTX_SUFF(pDevIns), s->irq, 0);
218# endif /* !VBOX_SERIAL_PCI */
219 }
220}
221
222static void serial_update_parameters(SerialState *s)
223{
224 int speed, parity, data_bits, stop_bits;
225
226 if (s->lcr & 0x08) {
227 if (s->lcr & 0x10)
228 parity = 'E';
229 else
230 parity = 'O';
231 } else {
232 parity = 'N';
233 }
234 if (s->lcr & 0x04)
235 stop_bits = 2;
236 else
237 stop_bits = 1;
238 data_bits = (s->lcr & 0x03) + 5;
239 if (s->divider == 0)
240 return;
241 speed = 115200 / s->divider;
242 Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
243 if (RT_LIKELY(s->pDrvChar))
244 s->pDrvChar->pfnSetParameters(s->pDrvChar, speed, parity, data_bits, stop_bits);
245}
246
247#endif /* IN_RING3 */
248
249static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
250{
251 SerialState *s = (SerialState *)opaque;
252 unsigned char ch;
253
254 addr &= 7;
255 LogFlow(("serial: write addr=0x%02x val=0x%02x\n", addr, val));
256
257#ifndef IN_RING3
258 NOREF(ch);
259 NOREF(s);
260 return VINF_IOM_HC_IOPORT_WRITE;
261#else
262 switch(addr) {
263 default:
264 case 0:
265 if (s->lcr & UART_LCR_DLAB) {
266 s->divider = (s->divider & 0xff00) | val;
267 serial_update_parameters(s);
268 } else {
269 s->thr_ipending = 0;
270 s->lsr &= ~UART_LSR_THRE;
271 serial_update_irq(s);
272 ch = val;
273 if (RT_LIKELY(s->pDrvChar))
274 {
275 Log(("serial_io_port_write: write 0x%X\n", ch));
276 int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
277 AssertRC(rc);
278 }
279 s->thr_ipending = 1;
280 s->lsr |= UART_LSR_THRE;
281 s->lsr |= UART_LSR_TEMT;
282 serial_update_irq(s);
283 }
284 break;
285 case 1:
286 if (s->lcr & UART_LCR_DLAB) {
287 s->divider = (s->divider & 0x00ff) | (val << 8);
288 serial_update_parameters(s);
289 } else {
290 s->ier = val & 0x0f;
291 if (s->lsr & UART_LSR_THRE) {
292 s->thr_ipending = 1;
293 }
294 serial_update_irq(s);
295 }
296 break;
297 case 2:
298 break;
299 case 3:
300 {
301 int break_enable;
302 if (s->lcr != val)
303 {
304 s->lcr = val;
305 serial_update_parameters(s);
306 }
307 break_enable = (val >> 6) & 1;
308 if (break_enable != s->last_break_enable) {
309 s->last_break_enable = break_enable;
310 }
311 }
312 break;
313 case 4:
314 s->mcr = val & 0x1f;
315 if (RT_LIKELY(s->pDrvChar))
316 {
317 int rc = s->pDrvChar->pfnSetModemLines(s->pDrvChar, !!(s->mcr & UART_MCR_RTS), !!(s->mcr & UART_MCR_DTR));
318 AssertRC(rc);
319 }
320 break;
321 case 5:
322 break;
323 case 6:
324 break;
325 case 7:
326 s->scr = val;
327 break;
328 }
329 return VINF_SUCCESS;
330#endif
331}
332
333static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
334{
335 SerialState *s = (SerialState *)opaque;
336 uint32_t ret = ~0U;
337
338 *pRC = VINF_SUCCESS;
339
340 addr &= 7;
341 switch(addr) {
342 default:
343 case 0:
344 if (s->lcr & UART_LCR_DLAB) {
345 ret = s->divider & 0xff;
346 } else {
347#ifndef IN_RING3
348 *pRC = VINF_IOM_HC_IOPORT_READ;
349#else
350 Log(("serial_io_port_read: read 0x%X\n", s->rbr));
351 ret = s->rbr;
352 s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
353 serial_update_irq(s);
354 {
355 int rc = RTSemEventSignal(s->ReceiveSem);
356 AssertRC(rc);
357 }
358#endif
359 }
360 break;
361 case 1:
362 if (s->lcr & UART_LCR_DLAB) {
363 ret = (s->divider >> 8) & 0xff;
364 } else {
365 ret = s->ier;
366 }
367 break;
368 case 2:
369#ifndef IN_RING3
370 *pRC = VINF_IOM_HC_IOPORT_READ;
371#else
372 ret = s->iir;
373 /* reset THR pending bit */
374 if ((ret & 0x7) == UART_IIR_THRI)
375 s->thr_ipending = 0;
376 /* reset msr changed bit */
377 s->msr_changed = false;
378 serial_update_irq(s);
379#endif
380 break;
381 case 3:
382 ret = s->lcr;
383 break;
384 case 4:
385 ret = s->mcr;
386 break;
387 case 5:
388 if ((s->lsr & UART_LSR_DR) == 0 && s->fYieldOnLSRRead)
389 {
390 /* No data available and yielding is enabled, so yield in ring3. */
391#ifndef IN_RING3
392 *pRC = VINF_IOM_HC_IOPORT_READ;
393 break;
394#else
395 RTThreadYield ();
396#endif
397 }
398 ret = s->lsr;
399 break;
400 case 6:
401 if (s->mcr & UART_MCR_LOOP) {
402 /* in loopback, the modem output pins are connected to the
403 inputs */
404 ret = (s->mcr & 0x0c) << 4;
405 ret |= (s->mcr & 0x02) << 3;
406 ret |= (s->mcr & 0x01) << 5;
407 } else {
408 ret = s->msr;
409 /* Reset delta bits. */
410 s->msr &= ~UART_MSR_ANY_DELTA;
411 }
412 break;
413 case 7:
414 ret = s->scr;
415 break;
416 }
417 LogFlow(("serial: read addr=0x%02x val=0x%02x\n", addr, ret));
418 return ret;
419}
420
421#ifdef IN_RING3
422
423static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
424{
425 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
426 int rc;
427
428 Assert(*pcbRead != 0);
429
430 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
431 if (pThis->lsr & UART_LSR_DR)
432 {
433 /* If a character is still in the read queue, then wait for it to be emptied. */
434 PDMCritSectLeave(&pThis->CritSect);
435 rc = RTSemEventWait(pThis->ReceiveSem, 250);
436 if (RT_FAILURE(rc))
437 return rc;
438
439 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
440 }
441
442 if (!(pThis->lsr & UART_LSR_DR))
443 {
444 pThis->rbr = *(const char *)pvBuf;
445 pThis->lsr |= UART_LSR_DR;
446 serial_update_irq(pThis);
447 *pcbRead = 1;
448 rc = VINF_SUCCESS;
449 }
450 else
451 rc = VERR_TIMEOUT;
452
453 PDMCritSectLeave(&pThis->CritSect);
454
455 return rc;
456}
457
458static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines)
459{
460 SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
461 uint8_t newMsr = 0;
462
463 Log(("%s: pInterface=%p newStatusLines=%u\n", __FUNCTION__, pInterface, newStatusLines));
464
465 PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
466
467 /* Set new states. */
468 if (newStatusLines & PDM_ICHAR_STATUS_LINES_DCD)
469 newMsr |= UART_MSR_DCD;
470 if (newStatusLines & PDM_ICHAR_STATUS_LINES_RI)
471 newMsr |= UART_MSR_RI;
472 if (newStatusLines & PDM_ICHAR_STATUS_LINES_DSR)
473 newMsr |= UART_MSR_DSR;
474 if (newStatusLines & PDM_ICHAR_STATUS_LINES_CTS)
475 newMsr |= UART_MSR_CTS;
476
477 /* Compare the old and the new states and set the delta bits accordingly. */
478 if ((newMsr & UART_MSR_DCD) != (pThis->msr & UART_MSR_DCD))
479 newMsr |= UART_MSR_DDCD;
480 if ((newMsr & UART_MSR_RI) == 1 && (pThis->msr & UART_MSR_RI) == 0)
481 newMsr |= UART_MSR_TERI;
482 if ((newMsr & UART_MSR_DSR) != (pThis->msr & UART_MSR_DSR))
483 newMsr |= UART_MSR_DDSR;
484 if ((newMsr & UART_MSR_CTS) != (pThis->msr & UART_MSR_CTS))
485 newMsr |= UART_MSR_DCTS;
486
487 pThis->msr = newMsr;
488 pThis->msr_changed = true;
489 serial_update_irq(pThis);
490
491 PDMCritSectLeave(&pThis->CritSect);
492
493 return VINF_SUCCESS;
494}
495
496#endif /* IN_RING3 */
497
498/**
499 * Port I/O Handler for OUT operations.
500 *
501 * @returns VBox status code.
502 *
503 * @param pDevIns The device instance.
504 * @param pvUser User argument.
505 * @param Port Port number used for the IN operation.
506 * @param u32 The value to output.
507 * @param cb The value size in bytes.
508 */
509PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
510 RTIOPORT Port, uint32_t u32, unsigned cb)
511{
512 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
513 int rc = VINF_SUCCESS;
514
515 if (cb == 1)
516 {
517 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_WRITE);
518 if (rc == VINF_SUCCESS)
519 {
520 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, u32));
521 rc = serial_ioport_write(pThis, Port, u32);
522 PDMCritSectLeave(&pThis->CritSect);
523 }
524 }
525 else
526 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
527
528 return rc;
529}
530
531/**
532 * Port I/O Handler for IN operations.
533 *
534 * @returns VBox status code.
535 *
536 * @param pDevIns The device instance.
537 * @param pvUser User argument.
538 * @param Port Port number used for the IN operation.
539 * @param u32 The value to output.
540 * @param cb The value size in bytes.
541 */
542PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
543 RTIOPORT Port, uint32_t *pu32, unsigned cb)
544{
545 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
546 int rc = VINF_SUCCESS;
547
548 if (cb == 1)
549 {
550 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_READ);
551 if (rc == VINF_SUCCESS)
552 {
553 *pu32 = serial_ioport_read(pThis, Port, &rc);
554 Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, Port, *pu32));
555 PDMCritSectLeave(&pThis->CritSect);
556 }
557 }
558 else
559 rc = VERR_IOM_IOPORT_UNUSED;
560
561 return rc;
562}
563
564#ifdef IN_RING3
565
566/**
567 * Saves a state of the serial port device.
568 *
569 * @returns VBox status code.
570 * @param pDevIns The device instance.
571 * @param pSSMHandle The handle to save the state to.
572 */
573static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns,
574 PSSMHANDLE pSSMHandle)
575{
576 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
577
578 SSMR3PutU16(pSSMHandle, pThis->divider);
579 SSMR3PutU8(pSSMHandle, pThis->rbr);
580 SSMR3PutU8(pSSMHandle, pThis->ier);
581 SSMR3PutU8(pSSMHandle, pThis->lcr);
582 SSMR3PutU8(pSSMHandle, pThis->mcr);
583 SSMR3PutU8(pSSMHandle, pThis->lsr);
584 SSMR3PutU8(pSSMHandle, pThis->msr);
585 SSMR3PutU8(pSSMHandle, pThis->scr);
586 SSMR3PutS32(pSSMHandle, pThis->thr_ipending);
587 SSMR3PutS32(pSSMHandle, pThis->irq);
588 SSMR3PutS32(pSSMHandle, pThis->last_break_enable);
589 SSMR3PutU32(pSSMHandle, pThis->base);
590 SSMR3PutBool(pSSMHandle, pThis->msr_changed);
591 return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
592}
593
594/**
595 * Loads a saved serial port device state.
596 *
597 * @returns VBox status code.
598 * @param pDevIns The device instance.
599 * @param pSSMHandle The handle to the saved state.
600 * @param u32Version The data unit version number.
601 */
602static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
603 PSSMHANDLE pSSMHandle,
604 uint32_t u32Version)
605{
606 int rc;
607 uint32_t u32;
608 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
609
610 if (u32Version != SERIAL_SAVED_STATE_VERSION)
611 {
612 AssertMsgFailed(("u32Version=%d\n", u32Version));
613 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
614 }
615
616 SSMR3GetU16(pSSMHandle, &pThis->divider);
617 SSMR3GetU8(pSSMHandle, &pThis->rbr);
618 SSMR3GetU8(pSSMHandle, &pThis->ier);
619 SSMR3GetU8(pSSMHandle, &pThis->lcr);
620 SSMR3GetU8(pSSMHandle, &pThis->mcr);
621 SSMR3GetU8(pSSMHandle, &pThis->lsr);
622 SSMR3GetU8(pSSMHandle, &pThis->msr);
623 SSMR3GetU8(pSSMHandle, &pThis->scr);
624 SSMR3GetS32(pSSMHandle, &pThis->thr_ipending);
625 SSMR3GetS32(pSSMHandle, &pThis->irq);
626 SSMR3GetS32(pSSMHandle, &pThis->last_break_enable);
627 SSMR3GetU32(pSSMHandle, &pThis->base);
628 SSMR3GetBool(pSSMHandle, &pThis->msr_changed);
629
630 rc = SSMR3GetU32(pSSMHandle, &u32);
631 if (RT_FAILURE(rc))
632 return rc;
633
634 if (u32 != ~0U)
635 {
636 AssertLogRelMsgFailed(("u32=%#x expected ~0\n", u32));
637 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
638 }
639 /* Be careful with pointers in the structure; they are not preserved
640 * in the saved state. */
641
642 if (pThis->lsr & UART_LSR_DR)
643 {
644 int rc = RTSemEventSignal(pThis->ReceiveSem);
645 AssertRC(rc);
646 }
647
648 /* this isn't strictly necessary but cannot hurt... */
649 pThis->pDevInsR3 = pDevIns;
650 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
651 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
652 return VINF_SUCCESS;
653}
654
655
656/**
657 * @copydoc FNPDMDEVRELOCATE
658 */
659static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
660{
661 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
662 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
663}
664
665#ifdef VBOX_SERIAL_PCI
666
667static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned */ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
668{
669 SerialState *pThis = PCIDEV_2_SERIALSTATE(pPciDev);
670 int rc = VINF_SUCCESS;
671
672 Assert(enmType == PCI_ADDRESS_SPACE_IO);
673 Assert(iRegion == 0);
674 Assert(cb == 8);
675 AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
676
677 pThis->base = (RTIOPORT)GCPhysAddress;
678 LogRel(("Serial#%d: mapping I/O at %#06x\n", pThis->pDevIns->iInstance, pThis->base));
679
680 /*
681 * Register our port IO handlers.
682 */
683 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pThis,
684 serial_io_write, serial_io_read, NULL, NULL, "SERIAL");
685 AssertRC(rc);
686 return rc;
687}
688
689#endif /* VBOX_SERIAL_PCI */
690
691
692/** @copyfrom PIBASE::pfnqueryInterface */
693static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
694{
695 SerialState *pThis = PDMIBASE_2_SERIALSTATE(pInterface);
696 switch (enmInterface)
697 {
698 case PDMINTERFACE_BASE:
699 return &pThis->IBase;
700 case PDMINTERFACE_CHAR_PORT:
701 return &pThis->ICharPort;
702 default:
703 return NULL;
704 }
705}
706
707/**
708 * Destruct a device instance.
709 *
710 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
711 * resources can be freed correctly.
712 *
713 * @returns VBox status.
714 * @param pDevIns The device instance data.
715 */
716static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns)
717{
718 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
719
720 RTSemEventDestroy(pThis->ReceiveSem);
721 pThis->ReceiveSem = NIL_RTSEMEVENT;
722
723 PDMR3CritSectDelete(&pThis->CritSect);
724 return VINF_SUCCESS;
725}
726
727
728/**
729 * Construct a device instance for a VM.
730 *
731 * @returns VBox status.
732 * @param pDevIns The device instance data.
733 * If the registration structure is needed, pDevIns->pDevReg points to it.
734 * @param iInstance Instance number. Use this to figure out which registers and such to use.
735 * The device number is also found in pDevIns->iInstance, but since it's
736 * likely to be freqently used PDM passes it as parameter.
737 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
738 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
739 * iInstance it's expected to be used a bit in this function.
740 */
741static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
742 int iInstance,
743 PCFGMNODE pCfgHandle)
744{
745 int rc;
746 SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState*);
747 uint16_t io_base;
748 uint8_t irq_lvl;
749
750 Assert(iInstance < 4);
751
752 /*
753 * Initialize the instance data.
754 * (Do this early or the destructor might choke on something!)
755 */
756 pThis->pDevInsR3 = pDevIns;
757 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
758 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
759
760 pThis->lsr = UART_LSR_TEMT | UART_LSR_THRE;
761 pThis->iir = UART_IIR_NO_INT;
762 pThis->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
763
764 /* IBase */
765 pThis->IBase.pfnQueryInterface = serialQueryInterface;
766
767 /* ICharPort */
768 pThis->ICharPort.pfnNotifyRead = serialNotifyRead;
769 pThis->ICharPort.pfnNotifyStatusLinesChanged = serialNotifyStatusLinesChanged;
770
771#ifdef VBOX_SERIAL_PCI
772 /* the PCI device */
773 pThis->dev.config[0x00] = 0xee; /* Vendor: ??? */
774 pThis->dev.config[0x01] = 0x80;
775 pThis->dev.config[0x02] = 0x01; /* Device: ??? */
776 pThis->dev.config[0x03] = 0x01;
777 pThis->dev.config[0x04] = PCI_COMMAND_IOACCESS;
778 pThis->dev.config[0x09] = 0x01; /* Programming interface: 16450 */
779 pThis->dev.config[0x0a] = 0x00; /* Subclass: Serial controller */
780 pThis->dev.config[0x0b] = 0x07; /* Class: Communication controller */
781 pThis->dev.config[0x0e] = 0x00; /* Header type: standard */
782 pThis->dev.config[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/
783 pThis->dev.config[0x3d] = 1; /* interrupt pin 0 */
784#endif /* VBOX_SERIAL_PCI */
785
786 /*
787 * Validate and read the configuration.
788 */
789 if (!CFGMR3AreValuesValid(pCfgHandle, "IRQ\0" "IOBase\0" "GCEnabled\0" "R0Enabled\0" "YieldOnLSRRead\0"))
790 {
791 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
792 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
793 }
794
795 rc = CFGMR3QueryBoolDef(pCfgHandle, "GCEnabled", &pThis->fGCEnabled, true);
796 if (RT_FAILURE(rc))
797 return PDMDEV_SET_ERROR(pDevIns, rc,
798 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
799
800 rc = CFGMR3QueryBoolDef(pCfgHandle, "R0Enabled", &pThis->fR0Enabled, true);
801 if (RT_FAILURE(rc))
802 return PDMDEV_SET_ERROR(pDevIns, rc,
803 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
804
805 rc = CFGMR3QueryBoolDef(pCfgHandle, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false);
806 if (RT_FAILURE(rc))
807 return PDMDEV_SET_ERROR(pDevIns, rc,
808 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
809
810 rc = CFGMR3QueryU8(pCfgHandle, "IRQ", &irq_lvl);
811 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
812 {
813 /* Provide sensible defaults. */
814 if (iInstance == 0)
815 irq_lvl = 4;
816 else if (iInstance == 1)
817 irq_lvl = 3;
818 else
819 AssertReleaseFailed(); /* irq_lvl is undefined. */
820 }
821 else if (RT_FAILURE(rc))
822 return PDMDEV_SET_ERROR(pDevIns, rc,
823 N_("Configuration error: Failed to get the \"IRQ\" value"));
824
825 rc = CFGMR3QueryU16(pCfgHandle, "IOBase", &io_base);
826 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
827 {
828 if (iInstance == 0)
829 io_base = 0x3f8;
830 else if (iInstance == 1)
831 io_base = 0x2f8;
832 else
833 AssertReleaseFailed(); /* io_base is undefined */
834 }
835 else if (RT_FAILURE(rc))
836 return PDMDEV_SET_ERROR(pDevIns, rc,
837 N_("Configuration error: Failed to get the \"IOBase\" value"));
838
839 Log(("DevSerial: instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
840
841 pThis->irq = irq_lvl;
842#ifdef VBOX_SERIAL_PCI
843 pThis->base = -1;
844#else
845 pThis->base = io_base;
846#endif
847
848 /*
849 * Initialize critical section and the semaphore.
850 * This must of course be done before attaching drivers or anything else which can call us back..
851 */
852 char szName[24];
853 RTStrPrintf(szName, sizeof(szName), "Serial#%d", iInstance);
854 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, szName);
855 if (RT_FAILURE(rc))
856 return rc;
857
858 rc = RTSemEventCreate(&pThis->ReceiveSem);
859 AssertRC(rc);
860
861#ifdef VBOX_SERIAL_PCI
862 /*
863 * Register the PCI Device and region.
864 */
865 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev);
866 if (RT_FAILURE(rc))
867 return rc;
868 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap);
869 if (RT_FAILURE(rc))
870 return rc;
871
872#else /* !VBOX_SERIAL_PCI */
873 /*
874 * Register the I/O ports.
875 */
876 pThis->base = io_base;
877 rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0,
878 serialIOPortWrite, serialIOPortRead,
879 NULL, NULL, "SERIAL");
880 if (RT_FAILURE(rc))
881 return rc;
882
883 if (pThis->fGCEnabled)
884 rc = PDMDevHlpIOPortRegisterGC(pDevIns, io_base, 8, 0, "serialIOPortWrite",
885 "serialIOPortRead", NULL, NULL, "Serial");
886
887 if (pThis->fR0Enabled)
888 rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
889 "serialIOPortRead", NULL, NULL, "Serial");
890#endif /* !VBOX_SERIAL_PCI */
891
892 /*
893 * Saved state.
894 */
895 rc = PDMDevHlpSSMRegister(
896 pDevIns, /* pDevIns */
897 pDevIns->pDevReg->szDeviceName, /* pszName */
898 iInstance, /* u32Instance */
899 SERIAL_SAVED_STATE_VERSION, /* u32Version */
900 sizeof (*pThis), /* cbGuess */
901 NULL, /* pfnSavePrep */
902 serialSaveExec, /* pfnSaveExec */
903 NULL, /* pfnSaveDone */
904 NULL, /* pfnLoadPrep */
905 serialLoadExec, /* pfnLoadExec */
906 NULL /* pfnLoadDone */
907 );
908 if (RT_FAILURE(rc))
909 return rc;
910
911 /*
912 * Attach the char driver and get the interfaces.
913 * For now no run-time changes are supported.
914 */
915 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
916 if (RT_SUCCESS(rc))
917 {
918 pThis->pDrvChar = (PDMICHAR *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_CHAR);
919 if (!pThis->pDrvChar)
920 {
921 AssertLogRelMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance));
922 return VERR_PDM_MISSING_INTERFACE;
923 }
924 /** @todo provide read notification interface!!!! */
925 }
926 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
927 {
928 pThis->pDrvBase = NULL;
929 pThis->pDrvChar = NULL;
930 LogRel(("Serial%d: no unit\n", iInstance));
931 }
932 else
933 {
934 AssertLogRelMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc));
935 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
936 return rc;
937 }
938
939 return VINF_SUCCESS;
940}
941
942
943/**
944 * The device registration structure.
945 */
946const PDMDEVREG g_DeviceSerialPort =
947{
948 /* u32Version */
949 PDM_DEVREG_VERSION,
950 /* szDeviceName */
951 "serial",
952 /* szRCMod */
953 "VBoxDDGC.gc",
954 /* szR0Mod */
955 "VBoxDDR0.r0",
956 /* pszDescription */
957 "Serial Communication Port",
958 /* fFlags */
959 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
960 /* fClass */
961 PDM_DEVREG_CLASS_SERIAL,
962 /* cMaxInstances */
963 1,
964 /* cbInstance */
965 sizeof(SerialState),
966 /* pfnConstruct */
967 serialConstruct,
968 /* pfnDestruct */
969 serialDestruct,
970 /* pfnRelocate */
971 serialRelocate,
972 /* pfnIOCtl */
973 NULL,
974 /* pfnPowerOn */
975 NULL,
976 /* pfnReset */
977 NULL,
978 /* pfnSuspend */
979 NULL,
980 /* pfnResume */
981 NULL,
982 /* pfnAttach */
983 NULL,
984 /* pfnDetach */
985 NULL,
986 /* pfnQueryInterface. */
987 NULL,
988 /* pfnInitComplete */
989 NULL,
990 /* pfnPowerOff */
991 NULL,
992 /* pfnSoftReset */
993 NULL,
994 /* u32VersionEnd */
995 PDM_DEVREG_VERSION
996};
997#endif /* IN_RING3 */
998
999
1000#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