VirtualBox

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

Last change on this file since 21205 was 20374, checked in by vboxsync, 15 years ago

*: s/RT_\(BEGIN|END\)_DECLS/RT_C_DECLS_\1/g

  • 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 20374 2009-06-08 00:43:21Z 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*******************************************************************************/
187RT_C_DECLS_BEGIN
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);
190RT_C_DECLS_END
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.

© 2023 Oracle
ContactPrivacy policyTerms of Use