VirtualBox

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

Last change on this file since 25776 was 25732, checked in by vboxsync, 15 years ago

PDMCritSect: Deployed lock ordering. (ring-3 only, only DEBUG_bird atm)

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