VirtualBox

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

Last change on this file since 14831 was 12978, checked in by vboxsync, 16 years ago

PDM: PDM_DEVREG_FLAGS_DEFAULT_BITS convenience.

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