VirtualBox

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

Last change on this file since 29603 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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