VirtualBox

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

Last change on this file since 28587 was 28400, checked in by vboxsync, 15 years ago

addendum to r60160

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