VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DevSerialNew.cpp@ 72179

Last change on this file since 72179 was 72179, checked in by vboxsync, 7 years ago

Devices/Serial/DevSerialNew: Need to update the IRQ after the THR register was written

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.8 KB
Line 
1/* $Id: DevSerialNew.cpp 72179 2018-05-09 17:36:40Z vboxsync $ */
2/** @file
3 * DevSerial - 16550A UART emulation.
4 *
5 * The documentation for this device was taken from the PC16550D spec from TI.
6 */
7
8/*
9 * Copyright (C) 2018 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DEV_SERIAL
25#include <VBox/vmm/pdmdev.h>
26#include <VBox/vmm/pdmserialifs.h>
27#include <iprt/assert.h>
28#include <iprt/uuid.h>
29#include <iprt/string.h>
30#include <iprt/semaphore.h>
31#include <iprt/critsect.h>
32
33#include "VBoxDD.h"
34
35
36/*********************************************************************************************************************************
37* Defined Constants And Macros *
38*********************************************************************************************************************************/
39
40/** The RBR/DLL register index (from the base of the port range). */
41#define UART_REG_RBR_DLL_INDEX 0
42
43/** The THR/DLL register index (from the base of the port range). */
44#define UART_REG_THR_DLL_INDEX 0
45
46/** The IER/DLM register index (from the base of the port range). */
47#define UART_REG_IER_DLM_INDEX 1
48/** Enable received data available interrupt */
49# define UART_REG_IER_ERBFI RT_BIT(0)
50/** Enable transmitter holding register empty interrupt */
51# define UART_REG_IER_ETBEI RT_BIT(1)
52/** Enable receiver line status interrupt */
53# define UART_REG_IER_ELSI RT_BIT(2)
54/** Enable modem status interrupt. */
55# define UART_REG_IER_EDSSI RT_BIT(3)
56/** Mask of writeable bits. */
57# define UART_REG_IER_MASK_WR 0x0f
58
59/** The IIR register index (from the base of the port range). */
60#define UART_REG_IIR_INDEX 2
61/** Interrupt Pending - high means no interrupt pending. */
62# define UART_REG_IIR_IP_NO_INT RT_BIT(0)
63/** Interrupt identification mask. */
64# define UART_REG_IIR_ID_MASK 0x0e
65/** Sets the interrupt identification to the given value. */
66# define UART_REG_IIR_ID_SET(a_Val) (((a_Val) << 1) & UART_REG_IIR_ID_MASK)
67/** Receiver Line Status interrupt. */
68# define UART_REG_IIR_ID_RCL 0x3
69/** Received Data Available interrupt. */
70# define UART_REG_IIR_ID_RDA 0x2
71/** Character Timeou Indicator interrupt. */
72# define UART_REG_IIR_ID_CTI 0x6
73/** Transmitter Holding Register Empty interrupt. */
74# define UART_REG_IIR_ID_THRE 0x1
75/** Modem Status interrupt. */
76# define UART_REG_IIR_ID_MS 0x0
77/** FIFOs enabled. */
78# define UART_REG_IIR_FIFOS_EN 0xc0
79/** Bits relevant for checking whether the interrupt status has changed. */
80# define UART_REG_IIR_CHANGED_MASK 0x0f
81
82/** The FCR register index (from the base of the port range). */
83#define UART_REG_FCR_INDEX 2
84/** Enable the TX/RX FIFOs. */
85# define UART_REG_FCR_FIFO_EN RT_BIT(0)
86/** Reset the receive FIFO. */
87# define UART_REG_FCR_RCV_FIFO_RST RT_BIT(1)
88/** Reset the transmit FIFO. */
89# define UART_REG_FCR_XMIT_FIFO_RST RT_BIT(2)
90/** DMA Mode Select. */
91# define UART_REG_FCR_DMA_MODE_SEL RT_BIT(3)
92/** Receiver level interrupt trigger. */
93# define UART_REG_FCR_RCV_LVL_IRQ_MASK 0xc0
94/** Returns the receive level trigger value from the given FCR register. */
95# define UART_REG_FCR_RCV_LVL_IRQ_GET(a_Fcr) (((a_Fcr) & UART_REG_FCR_RCV_LVL_IRQ_MASK) >> 6)
96/** Mask of writeable bits. */
97# define UART_REG_FCR_MASK_WR 0xcf
98
99/** The LCR register index (from the base of the port range). */
100#define UART_REG_LCR_INDEX 3
101/** Word Length Select Mask. */
102# define UART_REG_LCR_WLS_MASK 0x3
103/** Returns the WLS value form the given LCR register value. */
104# define UART_REG_LCR_WLS_GET(a_Lcr) ((a_Lcr) & UART_REG_LCR_WLS_MASK)
105/** Number of stop bits. */
106# define UART_REG_LCR_STB RT_BIT(2)
107/** Parity Enable. */
108# define UART_REG_LCR_PEN RT_BIT(3)
109/** Even Parity. */
110# define UART_REG_LCR_EPS RT_BIT(4)
111/** Stick parity. */
112# define UART_REG_LCR_PAR_STICK RT_BIT(5)
113/** Set Break. */
114# define UART_REG_LCR_BRK_SET RT_BIT(6)
115/** Divisor Latch Access Bit. */
116# define UART_REG_LCR_DLAB RT_BIT(7)
117
118/** The MCR register index (from the base of the port range). */
119#define UART_REG_MCR_INDEX 4
120/** Data Terminal Ready. */
121# define UART_REG_MCR_DTR RT_BIT(0)
122/** Request To Send. */
123# define UART_REG_MCR_RTS RT_BIT(1)
124/** Out1. */
125# define UART_REG_MCR_OUT1 RT_BIT(2)
126/** Out2. */
127# define UART_REG_MCR_OUT2 RT_BIT(3)
128/** Loopback connection. */
129# define UART_REG_MCR_LOOP RT_BIT(4)
130/** Mask of writeable bits. */
131# define UART_REG_MCR_MASK_WR 0x1f
132
133/** The LSR register index (from the base of the port range). */
134#define UART_REG_LSR_INDEX 5
135/** Data Ready. */
136# define UART_REG_LSR_DR RT_BIT(0)
137/** Overrun Error. */
138# define UART_REG_LSR_OE RT_BIT(1)
139/** Parity Error. */
140# define UART_REG_LSR_PE RT_BIT(2)
141/** Framing Error. */
142# define UART_REG_LSR_FE RT_BIT(3)
143/** Break Interrupt. */
144# define UART_REG_LSR_BI RT_BIT(4)
145/** Transmitter Holding Register. */
146# define UART_REG_LSR_THRE RT_BIT(5)
147/** Transmitter Empty. */
148# define UART_REG_LSR_TEMT RT_BIT(6)
149/** Error in receiver FIFO. */
150# define UART_REG_LSR_RCV_FIFO_ERR RT_BIT(7)
151/** The bits to check in this register when checking for the RCL interrupt. */
152# define UART_REG_LSR_BITS_IIR_RCL 0x1e
153
154/** The MSR register index (from the base of the port range). */
155#define UART_REG_MSR_INDEX 6
156/** Delta Clear to Send. */
157# define UART_REG_MSR_DCTS RT_BIT(0)
158/** Delta Data Set Ready. */
159# define UART_REG_MSR_DDSR RT_BIT(1)
160/** Trailing Edge Ring Indicator. */
161# define UART_REG_MSR_TERI RT_BIT(2)
162/** Delta Data Carrier Detect. */
163# define UART_REG_MSR_DDCD RT_BIT(3)
164/** Clear to Send. */
165# define UART_REG_MSR_CTS RT_BIT(4)
166/** Data Set Ready. */
167# define UART_REG_MSR_DSR RT_BIT(5)
168/** Ring Indicator. */
169# define UART_REG_MSR_RI RT_BIT(6)
170/** Data Carrier Detect. */
171# define UART_REG_MSR_DCD RT_BIT(7)
172/** The bits to check in this register when checking for the MS interrupt. */
173# define UART_REG_MSR_BITS_IIR_MS 0x0f
174
175/** The SCR register index (from the base of the port range). */
176#define UART_REG_SCR_INDEX 7
177
178/** Set the specified bits in the given register. */
179#define UART_REG_SET(a_Reg, a_Set) ((a_Reg) |= (a_Set))
180/** Clear the specified bits in the given register. */
181#define UART_REG_CLR(a_Reg, a_Clr) ((a_Reg) &= ~(a_Clr))
182
183
184/*********************************************************************************************************************************
185* Structures and Typedefs *
186*********************************************************************************************************************************/
187
188/**
189 * Serial device.
190 *
191 * @implements PDMIBASE
192 * @implements PDMISERIALPORT
193 */
194typedef struct DEVSERIAL
195{
196 /** Access critical section. */
197 PDMCRITSECT CritSect;
198 /** Pointer to the device instance - R3 Ptr. */
199 PPDMDEVINSR3 pDevInsR3;
200 /** Pointer to the device instance - R0 Ptr. */
201 PPDMDEVINSR0 pDevInsR0;
202 /** Pointer to the device instance - RC Ptr. */
203 PPDMDEVINSRC pDevInsRC;
204 /** Alignment. */
205 RTRCPTR Alignment0;
206 /** LUN\#0: The base interface. */
207 PDMIBASE IBase;
208 /** LUN\#0: The serial port interface. */
209 PDMISERIALPORT ISerialPort;
210 /** Pointer to the attached base driver. */
211 R3PTRTYPE(PPDMIBASE) pDrvBase;
212 /** Pointer to the attached serial driver. */
213 R3PTRTYPE(PPDMISERIALCONNECTOR) pDrvSerial;
214 /** Flag whether the R0 portion of this device is enabled. */
215 bool fR0Enabled;
216 /** Flag whether the RC portion of this device is enabled. */
217 bool fRCEnabled;
218 /** Flag whether an 16550A (with FIFO) or a plain 16450 is emulated. */
219 bool f16550AEnabled;
220 /** Flag whether to yield on an guest LSR read. */
221 bool fYieldOnLSRRead;
222 /** The IRQ value. */
223 uint8_t uIrq;
224 /** The base I/O port the device is registered at. */
225 RTIOPORT PortBase;
226
227 /** The divisor register (DLAB = 1). */
228 uint16_t uRegDivisor;
229 /** The Receiver Buffer Register (RBR, DLAB = 0). */
230 uint8_t uRegRbr;
231 /** The Transmitter Holding Register (THR, DLAB = 0). */
232 uint8_t uRegThr;
233 /** The Interrupt Enable Register (IER). */
234 uint8_t uRegIer;
235 /** The Interrupt Identification Register (IIR). */
236 uint8_t uRegIir;
237 /** The FIFO Control Register (FCR). */
238 uint8_t uRegFcr;
239 /** The Line Control Register (LCR). */
240 uint8_t uRegLcr;
241 /** The Modem Control Register (MCR). */
242 uint8_t uRegMcr;
243 /** The Line Status Register (LSR). */
244 uint8_t uRegLsr;
245 /** The Modem Status Register (MSR). */
246 uint8_t uRegMsr;
247 /** The Scratch Register (SCR). */
248 uint8_t uRegScr;
249
250 /** Number of bytes available for reading from the layer below. */
251 volatile uint32_t cbAvailRdr;
252
253} DEVSERIAL;
254/** Pointer to the serial device state. */
255typedef DEVSERIAL *PDEVSERIAL;
256
257#ifndef VBOX_DEVICE_STRUCT_TESTCASE
258
259
260/*********************************************************************************************************************************
261* Global Variables *
262*********************************************************************************************************************************/
263#ifdef IN_RING3
264/**
265 * String versions of the parity enum.
266 */
267static const char *s_aszParity[] =
268{
269 "INVALID",
270 "NONE",
271 "EVEN",
272 "ODD",
273 "MARK",
274 "SPACE",
275 "INVALID"
276};
277
278
279/**
280 * String versions of the stop bits enum.
281 */
282static const char *s_aszStopBits[] =
283{
284 "INVALID",
285 "1",
286 "1.5",
287 "2",
288 "INVALID"
289};
290#endif
291
292
293/*********************************************************************************************************************************
294* Internal Functions *
295*********************************************************************************************************************************/
296
297
298/**
299 * Updates the IRQ state based on the current device state.
300 *
301 * @returns nothing.
302 * @param pThis The serial port instance.
303 */
304static void serialIrqUpdate(PDEVSERIAL pThis)
305{
306 LogFlowFunc(("pThis=%#p\n", pThis));
307
308 /*
309 * The interrupt uses a priority scheme, only the interrupt with the
310 * highest priority is indicated in the interrupt identification register.
311 *
312 * The priorities are as follows (high to low):
313 * * Receiver line status
314 * * Received data available
315 * * Character timeout indication (only in FIFO mode).
316 * * Transmitter holding register empty
317 * * Modem status change.
318 */
319 uint8_t uRegIirNew = UART_REG_IIR_IP_NO_INT;
320 if ( (pThis->uRegLsr & UART_REG_LSR_BITS_IIR_RCL)
321 && (pThis->uRegIer & UART_REG_IER_ELSI))
322 uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RCL);
323 else if ( (pThis->uRegLsr & UART_REG_LSR_DR)
324 && (pThis->uRegIer & UART_REG_IER_ERBFI))
325 uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RDA);
326 else if ( (pThis->uRegLsr & UART_REG_LSR_THRE)
327 && (pThis->uRegIer & UART_REG_IER_ETBEI))
328 uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_THRE);
329 else if ( (pThis->uRegMsr & UART_REG_MSR_BITS_IIR_MS)
330 && (pThis->uRegIer & UART_REG_IER_EDSSI))
331 uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_MS);
332
333 /** @todo Character timeout indication for FIFO mode. */
334
335 LogFlowFunc((" uRegIirNew=%#x uRegIir=%#x\n", uRegIirNew, pThis->uRegIir));
336
337 /* Change interrupt only if the interrupt status really changed from the previous value. */
338 if (uRegIirNew != (pThis->uRegIir & UART_REG_IIR_CHANGED_MASK))
339 {
340 LogFlow((" Interrupt source changed from %#x -> %#x (IRQ %d -> %d)\n",
341 pThis->uRegIir, uRegIirNew,
342 pThis->uRegIir == UART_REG_IIR_IP_NO_INT ? 0 : 1,
343 uRegIirNew == UART_REG_IIR_IP_NO_INT ? 0 : 1));
344 if (uRegIirNew == UART_REG_IIR_IP_NO_INT)
345 PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->uIrq, 0);
346 else
347 PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->uIrq, 1);
348 }
349 else
350 LogFlow((" No change in interrupt source\n"));
351
352 if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
353 uRegIirNew |= UART_REG_IIR_FIFOS_EN;
354
355 pThis->uRegIir = uRegIirNew;
356}
357
358
359#ifdef IN_RING3
360/**
361 * Updates the serial port parameters of the attached driver with the current configuration.
362 *
363 * @returns nothing.
364 * @param pThis The serial port instance.
365 */
366static void serialR3ParamsUpdate(PDEVSERIAL pThis)
367{
368 if ( pThis->uRegDivisor != 0
369 && pThis->pDrvSerial)
370 {
371 uint32_t uBps = 115200 / pThis->uRegDivisor; /* This is for PC compatible serial port with a 1.8432 MHz crystal. */
372 unsigned cDataBits = UART_REG_LCR_WLS_GET(pThis->uRegLcr) + 5;
373 PDMSERIALSTOPBITS enmStopBits = PDMSERIALSTOPBITS_ONE;
374 PDMSERIALPARITY enmParity = PDMSERIALPARITY_NONE;
375
376 if (pThis->uRegLcr & UART_REG_LCR_STB)
377 {
378 enmStopBits = cDataBits == 5 ? PDMSERIALSTOPBITS_ONEPOINTFIVE : PDMSERIALSTOPBITS_TWO;
379 }
380
381 if (pThis->uRegLcr & UART_REG_LCR_PEN)
382 {
383 /* Select the correct parity mode based on the even and stick parity bits. */
384 switch (pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK))
385 {
386 case 0:
387 enmParity = PDMSERIALPARITY_ODD;
388 break;
389 case UART_REG_LCR_EPS:
390 enmParity = PDMSERIALPARITY_EVEN;
391 break;
392 case UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK:
393 enmParity = PDMSERIALPARITY_SPACE;
394 break;
395 case UART_REG_LCR_PAR_STICK:
396 enmParity = PDMSERIALPARITY_MARK;
397 break;
398 default:
399 /* We should never get here as all cases where caught earlier. */
400 AssertMsgFailed(("This shouldn't happen at all: %#x\n",
401 pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK)));
402 }
403 }
404
405 LogFlowFunc(("Changing parameters to: %u,%s,%u,%s\n",
406 uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits]));
407
408 int rc = pThis->pDrvSerial->pfnChgParams(pThis->pDrvSerial, uBps, enmParity, cDataBits, enmStopBits);
409 if (RT_FAILURE(rc))
410 LogRelMax(10, ("Serial#%d: Failed to change parameters to %u,%s,%u,%s -> %Rrc\n",
411 pThis->pDevInsR3->iInstance, uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits], rc));
412 }
413}
414
415
416/**
417 * Updates the internal device state with the given PDM status line states.
418 *
419 * @returns nothing.
420 * @param pThis The serial port instance.
421 * @param fStsLines The PDM status line states.
422 */
423static void serialR3StsLinesUpdate(PDEVSERIAL pThis, uint32_t fStsLines)
424{
425 uint8_t uRegMsrNew = 0; /* The new MSR value. */
426
427 if (fStsLines & PDMISERIALPORT_STS_LINE_DCD)
428 uRegMsrNew |= UART_REG_MSR_DCD;
429 if (fStsLines & PDMISERIALPORT_STS_LINE_RI)
430 uRegMsrNew |= UART_REG_MSR_RI;
431 if (fStsLines & PDMISERIALPORT_STS_LINE_DSR)
432 uRegMsrNew |= UART_REG_MSR_DSR;
433 if (fStsLines & PDMISERIALPORT_STS_LINE_CTS)
434 uRegMsrNew |= UART_REG_MSR_CTS;
435
436 /* Compare current and new states and set remaining bits accordingly. */
437 if ((uRegMsrNew & UART_REG_MSR_CTS) != (pThis->uRegMsr & UART_REG_MSR_CTS))
438 uRegMsrNew |= UART_REG_MSR_DCTS;
439 if ((uRegMsrNew & UART_REG_MSR_DSR) != (pThis->uRegMsr & UART_REG_MSR_DSR))
440 uRegMsrNew |= UART_REG_MSR_DDSR;
441 if ((uRegMsrNew & UART_REG_MSR_RI) != 0 && (pThis->uRegMsr & UART_REG_MSR_RI) == 0)
442 uRegMsrNew |= UART_REG_MSR_TERI;
443 if ((uRegMsrNew & UART_REG_MSR_DCD) != (pThis->uRegMsr & UART_REG_MSR_DCD))
444 uRegMsrNew |= UART_REG_MSR_DDCD;
445
446 pThis->uRegMsr = uRegMsrNew;
447
448 serialIrqUpdate(pThis);
449}
450#endif
451
452
453/**
454 * Write handler for the THR/DLL register (depending on the DLAB bit in LCR).
455 *
456 * @returns VBox status code.
457 * @param pThis The serial port instance.
458 * @param uVal The value to write.
459 */
460DECLINLINE(int) serialRegThrDllWrite(PDEVSERIAL pThis, uint8_t uVal)
461{
462 int rc = VINF_SUCCESS;
463
464 /* A set DLAB causes a write to the lower 8bits of the divisor latch. */
465 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
466 {
467 if (uVal != (pThis->uRegDivisor & 0xff))
468 {
469#ifndef IN_RING3
470 rc = VINF_IOM_R3_IOPORT_WRITE;
471#else
472 pThis->uRegDivisor = (pThis->uRegDivisor & 0xff00) | uVal;
473 serialR3ParamsUpdate(pThis);
474#endif
475 }
476 }
477 else
478 {
479 if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
480 {
481 /** @todo FIFO handling. */
482 }
483 else
484 {
485 /* Notify the lower driver about available data only if the register was empty before. */
486 if (pThis->uRegLsr & UART_REG_LSR_THRE)
487 {
488#ifndef IN_RING3
489 rc = VINF_IOM_R3_IOPORT_WRITE;
490#else
491 pThis->uRegThr = uVal;
492 UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_THRE | UART_REG_LSR_TEMT);
493 serialIrqUpdate(pThis);
494 if (pThis->pDrvSerial)
495 {
496 int rc2 = pThis->pDrvSerial->pfnDataAvailWrNotify(pThis->pDrvSerial, 1);
497 if (RT_FAILURE(rc2))
498 LogRelMax(10, ("Serial#%d: Failed to send data with %Rrc\n", rc2));
499 }
500#endif
501 }
502 else
503 pThis->uRegThr = uVal;
504 }
505 }
506
507 return rc;
508}
509
510
511/**
512 * Write handler for the IER/DLM register (depending on the DLAB bit in LCR).
513 *
514 * @returns VBox status code.
515 * @param pThis The serial port instance.
516 * @param uVal The value to write.
517 */
518DECLINLINE(int) serialRegIerDlmWrite(PDEVSERIAL pThis, uint8_t uVal)
519{
520 int rc = VINF_SUCCESS;
521
522 /* A set DLAB causes a write to the higher 8bits of the divisor latch. */
523 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
524 {
525 if (uVal != (pThis->uRegDivisor & 0xff00) >> 8)
526 {
527#ifndef IN_RING3
528 rc = VINF_IOM_R3_IOPORT_WRITE;
529#else
530 pThis->uRegDivisor = (pThis->uRegDivisor & 0xff) | (uVal << 8);
531 serialR3ParamsUpdate(pThis);
532#endif
533 }
534 }
535 else
536 {
537 pThis->uRegIer = uVal & UART_REG_IER_MASK_WR;
538 serialIrqUpdate(pThis);
539 }
540
541 return rc;
542}
543
544
545/**
546 * Write handler for the FCR register.
547 *
548 * @returns VBox status code.
549 * @param pThis The serial port instance.
550 * @param uVal The value to write.
551 */
552DECLINLINE(int) serialRegFcrWrite(PDEVSERIAL pThis, uint8_t uVal)
553{
554 int rc = VINF_SUCCESS;
555
556 RT_NOREF(uVal);
557 if (pThis->f16550AEnabled)
558 {
559 /** @todo */
560 }
561
562 return rc;
563}
564
565
566/**
567 * Write handler for the LCR register.
568 *
569 * @returns VBox status code.
570 * @param pThis The serial port instance.
571 * @param uVal The value to write.
572 */
573DECLINLINE(int) serialRegLcrWrite(PDEVSERIAL pThis, uint8_t uVal)
574{
575 int rc = VINF_SUCCESS;
576
577 /* Any change except the DLAB bit causes a switch to R3. */
578 if ((pThis->uRegLcr & ~UART_REG_LCR_DLAB) != (uVal & ~UART_REG_LCR_DLAB))
579 {
580#ifndef IN_RING3
581 rc = VINF_IOM_R3_IOPORT_WRITE;
582#else
583 /* Check whether the BREAK bit changed before updating the LCR value. */
584 bool fBrkEn = RT_BOOL(uVal & UART_REG_LCR_BRK_SET);
585 bool fBrkChg = fBrkEn != RT_BOOL(pThis->uRegLcr & UART_REG_LCR_BRK_SET);
586 pThis->uRegLcr = uVal;
587 serialR3ParamsUpdate(pThis);
588
589 if ( fBrkChg
590 && pThis->pDrvSerial)
591 pThis->pDrvSerial->pfnChgBrk(pThis->pDrvSerial, fBrkEn);
592#endif
593 }
594 else
595 pThis->uRegLcr = uVal;
596
597 return rc;
598}
599
600
601/**
602 * Write handler for the MCR register.
603 *
604 * @returns VBox status code.
605 * @param pThis The serial port instance.
606 * @param uVal The value to write.
607 */
608DECLINLINE(int) serialRegMcrWrite(PDEVSERIAL pThis, uint8_t uVal)
609{
610 int rc = VINF_SUCCESS;
611
612 uVal &= UART_REG_MCR_MASK_WR;
613 if (pThis->uRegMcr != uVal)
614 {
615#ifndef IN_RING3
616 rc = VINF_IOM_R3_IOPORT_WRITE;
617#else
618 /** @todo Loopback mode handling (setting RTS, DTR to high looping everything to MSR). */
619 pThis->uRegMcr = uVal;
620 if (pThis->pDrvSerial)
621 pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial,
622 RT_BOOL(uVal & UART_REG_MCR_RTS),
623 RT_BOOL(uVal & UART_REG_MCR_DTR));
624#endif
625 }
626
627 return rc;
628}
629
630
631/**
632 * Read handler for the RBR/DLL register (depending on the DLAB bit in LCR).
633 *
634 * @returns VBox status code.
635 * @param pThis The serial port instance.
636 * @param puVal Where to store the read value on success.
637 */
638DECLINLINE(int) serialRegRbrDllRead(PDEVSERIAL pThis, uint32_t *puVal)
639{
640 int rc = VINF_SUCCESS;
641
642 /* A set DLAB causes a read from the lower 8bits of the divisor latch. */
643 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
644 *puVal = pThis->uRegDivisor & 0xff;
645 else
646 {
647 *puVal = pThis->uRegRbr;
648
649 if (pThis->uRegLsr & UART_REG_LSR_DR)
650 {
651 uint32_t cbAvail = ASMAtomicDecU32(&pThis->cbAvailRdr);
652 if (!cbAvail)
653 {
654 UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_DR);
655 serialIrqUpdate(pThis);
656 }
657 else
658 {
659#ifndef IN_RING3
660 /* Restore state and go back to R3. */
661 ASMAtomicIncU32(&pThis->cbAvailRdr);
662 rc = VINF_IOM_R3_IOPORT_READ;
663#else
664 /* Fetch new data and keep the DR bit set. */
665 AssertPtr(pThis->pDrvSerial);
666 size_t cbRead = 0;
667 int rc2 = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
668 AssertMsg(RT_SUCCESS(rc2) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
669 serialIrqUpdate(pThis);
670#endif
671 }
672 }
673 }
674
675 return rc;
676}
677
678
679/**
680 * Read handler for the IER/DLM register (depending on the DLAB bit in LCR).
681 *
682 * @returns VBox status code.
683 * @param pThis The serial port instance.
684 * @param puVal Where to store the read value on success.
685 */
686DECLINLINE(int) serialRegIerDlmRead(PDEVSERIAL pThis, uint32_t *puVal)
687{
688 int rc = VINF_SUCCESS;
689
690 /* A set DLAB causes a read from the upper 8bits of the divisor latch. */
691 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
692 *puVal = (pThis->uRegDivisor & 0xff00) >> 8;
693 else
694 *puVal = pThis->uRegIer;
695
696 return rc;
697}
698
699
700/**
701 * Read handler for the IIR register.
702 *
703 * @returns VBox status code.
704 * @param pThis The serial port instance.
705 * @param puVal Where to store the read value on success.
706 */
707DECLINLINE(int) serialRegIirRead(PDEVSERIAL pThis, uint32_t *puVal)
708{
709 *puVal = pThis->uRegIir;
710 return VINF_SUCCESS;
711}
712
713
714/**
715 * Read handler for the LSR register.
716 *
717 * @returns VBox status code.
718 * @param pThis The serial port instance.
719 * @param puVal Where to store the read value on success.
720 */
721DECLINLINE(int) serialRegLsrRead(PDEVSERIAL pThis, uint32_t *puVal)
722{
723 int rc = VINF_SUCCESS;
724
725 /* Yield if configured and there is no data available. */
726 if ( !(pThis->uRegLsr & UART_REG_LSR_DR)
727 && pThis->fYieldOnLSRRead)
728 {
729#ifndef IN_RING3
730 return VINF_IOM_R3_IOPORT_READ;
731#else
732 RTThreadYield();
733#endif
734 }
735
736 *puVal = pThis->uRegLsr;
737 /*
738 * Reading this register clears the Overrun (OE), Parity (PE) and Framing (FE) error
739 * as well as the Break Interrupt (BI).
740 */
741 UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_BITS_IIR_RCL);
742 serialIrqUpdate(pThis);
743
744 return rc;
745}
746
747
748/**
749 * Read handler for the MSR register.
750 *
751 * @returns VBox status code.
752 * @param pThis The serial port instance.
753 * @param puVal Where to store the read value on success.
754 */
755DECLINLINE(int) serialRegMsrRead(PDEVSERIAL pThis, uint32_t *puVal)
756{
757 *puVal = pThis->uRegMsr;
758
759 /** @todo Loopback handling. */
760 /* Clear any of the delta bits. */
761 UART_REG_CLR(pThis->uRegMsr, UART_REG_MSR_BITS_IIR_MS);
762 serialIrqUpdate(pThis);
763 return VINF_SUCCESS;
764}
765
766
767#ifdef LOG_ENABLED
768/**
769 * Converts the register index into a sensible memnonic.
770 *
771 * @returns Register memnonic.
772 * @param pThis The serial port instance.
773 * @param idxReg Register index.
774 * @param fWrite Flag whether the register gets written.
775 */
776DECLINLINE(const char *) serialRegIdx2Str(PDEVSERIAL pThis, uint8_t idxReg, bool fWrite)
777{
778 const char *psz = "INV";
779
780 switch (idxReg)
781 {
782 /*case UART_REG_THR_DLL_INDEX:*/
783 case UART_REG_RBR_DLL_INDEX:
784 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
785 psz = "DLL";
786 else if (fWrite)
787 psz = "THR";
788 else
789 psz = "RBR";
790 break;
791 case UART_REG_IER_DLM_INDEX:
792 if (pThis->uRegLcr & UART_REG_LCR_DLAB)
793 psz = "DLM";
794 else
795 psz = "IER";
796 break;
797 /*case UART_REG_IIR_INDEX:*/
798 case UART_REG_FCR_INDEX:
799 if (fWrite)
800 psz = "FCR";
801 else
802 psz = "IIR";
803 break;
804 case UART_REG_LCR_INDEX:
805 psz = "LCR";
806 break;
807 case UART_REG_MCR_INDEX:
808 psz = "MCR";
809 break;
810 case UART_REG_LSR_INDEX:
811 psz = "LSR";
812 break;
813 case UART_REG_MSR_INDEX:
814 psz = "MSR";
815 break;
816 case UART_REG_SCR_INDEX:
817 psz = "SCR";
818 break;
819 }
820
821 return psz;
822}
823#endif
824
825/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
826
827/**
828 * @callback_method_impl{FNIOMIOPORTOUT}
829 */
830PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
831{
832 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
833 Assert(PDMCritSectIsOwner(&pThis->CritSect));
834 RT_NOREF_PV(pvUser);
835
836 uint8_t idxReg = uPort & 0x7;
837 LogFlowFunc(("pDevIns=%#p pvUser=%#p uPort=%RTiop{%s} u32=%#x cb=%u\n",
838 pDevIns, pvUser, uPort, serialRegIdx2Str(pThis, idxReg, true /*fWrite*/), u32, cb));
839
840 AssertMsgReturn(cb == 1, ("uPort=%#x cb=%d u32=%#x\n", uPort, cb, u32), VINF_SUCCESS);
841
842 int rc = VINF_SUCCESS;
843 uint8_t uVal = (uint8_t)u32;
844 switch (idxReg)
845 {
846 case UART_REG_THR_DLL_INDEX:
847 rc = serialRegThrDllWrite(pThis, uVal);
848 break;
849 case UART_REG_IER_DLM_INDEX:
850 rc = serialRegIerDlmWrite(pThis, uVal);
851 break;
852 case UART_REG_FCR_INDEX:
853 rc = serialRegFcrWrite(pThis, uVal);
854 break;
855 case UART_REG_LCR_INDEX:
856 rc = serialRegLcrWrite(pThis, uVal);
857 break;
858 case UART_REG_MCR_INDEX:
859 rc = serialRegMcrWrite(pThis, uVal);
860 break;
861 case UART_REG_SCR_INDEX:
862 pThis->uRegScr = u32;
863 break;
864 default:
865 break;
866 }
867
868 LogFlowFunc(("-> %Rrc\n", rc));
869 return rc;
870}
871
872
873/**
874 * @callback_method_impl{FNIOMIOPORTIN}
875 */
876PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
877{
878 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
879 Assert(PDMCritSectIsOwner(&pThis->CritSect));
880 RT_NOREF_PV(pvUser);
881
882 if (cb != 1)
883 return VERR_IOM_IOPORT_UNUSED;
884
885 uint8_t idxReg = uPort & 0x7;
886 int rc = VINF_SUCCESS;
887 switch (idxReg)
888 {
889 case UART_REG_RBR_DLL_INDEX:
890 rc = serialRegRbrDllRead(pThis, pu32);
891 break;
892 case UART_REG_IER_DLM_INDEX:
893 rc = serialRegIerDlmRead(pThis, pu32);
894 break;
895 case UART_REG_IIR_INDEX:
896 rc = serialRegIirRead(pThis, pu32);
897 break;
898 case UART_REG_LCR_INDEX:
899 *pu32 = pThis->uRegLcr;
900 break;
901 case UART_REG_MCR_INDEX:
902 *pu32 = pThis->uRegMcr;
903 break;
904 case UART_REG_LSR_INDEX:
905 rc = serialRegLsrRead(pThis, pu32);
906 break;
907 case UART_REG_MSR_INDEX:
908 rc = serialRegMsrRead(pThis, pu32);
909 break;
910 case UART_REG_SCR_INDEX:
911 *pu32 = pThis->uRegScr;
912 break;
913 default:
914 rc = VERR_IOM_IOPORT_UNUSED;
915 }
916
917 LogFlowFunc(("pDevIns=%#p pvUser=%#p uPort=%RTiop{%s} u32=%#x cb=%u -> %Rrc\n",
918 pDevIns, pvUser, uPort, serialRegIdx2Str(pThis, idxReg, false /*fWrite*/), *pu32, cb, rc));
919 return rc;
920}
921
922
923#ifdef IN_RING3
924
925/* -=-=-=-=-=-=-=-=- PDMISERIALPORT on LUN#0 -=-=-=-=-=-=-=-=- */
926
927
928/**
929 * @interface_method_impl{PDMISERIALPORT,pfnDataAvailRdrNotify}
930 */
931static DECLCALLBACK(int) serialR3DataAvailRdrNotify(PPDMISERIALPORT pInterface, size_t cbAvail)
932{
933 LogFlowFunc(("pInterface=%#p cbAvail=%zu\n", pInterface, cbAvail));
934 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
935
936 AssertMsg((uint32_t)cbAvail == cbAvail, ("Too much data available\n"));
937
938 uint32_t cbAvailOld = ASMAtomicAddU32(&pThis->cbAvailRdr, (uint32_t)cbAvail);
939 if (!cbAvailOld)
940 {
941 LogFlow((" cbAvailRdr=0 -> cbAvailRdr=%zu\n", cbAvail));
942 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
943 size_t cbRead = 0;
944 int rc = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
945 AssertMsg(RT_SUCCESS(rc) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
946 UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
947 serialIrqUpdate(pThis);
948 PDMCritSectLeave(&pThis->CritSect);
949 }
950 else
951 LogFlow((" cbAvailOld=%zu\n", cbAvailOld));
952 return VINF_SUCCESS;
953}
954
955
956/**
957 * @interface_method_impl{PDMISERIALPORT,pfnDataSentNotify}
958 */
959static DECLCALLBACK(int) serialR3DataSentNotify(PPDMISERIALPORT pInterface)
960{
961 LogFlowFunc(("pInterface=%#p\n", pInterface));
962 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
963
964 /* Set the transmitter empty bit because everything was sent. */
965 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
966 UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_TEMT);
967 serialIrqUpdate(pThis);
968 PDMCritSectLeave(&pThis->CritSect);
969 return VINF_SUCCESS;
970}
971
972
973/**
974 * @interface_method_impl{PDMISERIALPORT,pfnReadWr}
975 */
976static DECLCALLBACK(int) serialR3ReadWr(PPDMISERIALPORT pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead)
977{
978 LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead));
979 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
980
981 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
982
983 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
984 if (!(pThis->uRegLsr & UART_REG_LSR_THRE))
985 {
986 /** @todo FIFO mode. */
987 *(uint8_t *)pvBuf = pThis->uRegThr;
988 *pcbRead = 1;
989 UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_THRE);
990 UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_TEMT);
991 serialIrqUpdate(pThis);
992 }
993 else
994 {
995 AssertMsgFailed(("There is no data to read!\n"));
996 *pcbRead = 0;
997 }
998
999 LogFlowFunc(("-> VINF_SUCCESS{*pcbRead=%zu}\n", *pcbRead));
1000 PDMCritSectLeave(&pThis->CritSect);
1001 return VINF_SUCCESS;
1002}
1003
1004
1005/**
1006 * @interface_method_impl{PDMISERIALPORT,pfnNotifyStsLinesChanged}
1007 */
1008static DECLCALLBACK(int) serialR3NotifyStsLinesChanged(PPDMISERIALPORT pInterface, uint32_t fNewStatusLines)
1009{
1010 LogFlowFunc(("pInterface=%#p fNewStatusLines=%#x\n", pInterface, fNewStatusLines));
1011 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
1012
1013 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
1014 serialR3StsLinesUpdate(pThis, fNewStatusLines);
1015 PDMCritSectLeave(&pThis->CritSect);
1016 return VINF_SUCCESS;
1017}
1018
1019
1020/**
1021 * @interface_method_impl{PDMISERIALPORT,pfnNotifyBrk}
1022 */
1023static DECLCALLBACK(int) serialR3NotifyBrk(PPDMISERIALPORT pInterface)
1024{
1025 LogFlowFunc(("pInterface=%#p\n", pInterface));
1026 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
1027
1028 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
1029 UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_BI);
1030 serialIrqUpdate(pThis);
1031 PDMCritSectLeave(&pThis->CritSect);
1032 return VINF_SUCCESS;
1033}
1034
1035
1036/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#0 -=-=-=-=-=-=-=-=- */
1037
1038/**
1039 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1040 */
1041static DECLCALLBACK(void *) serialR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
1042{
1043 PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, IBase);
1044 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
1045 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALPORT, &pThis->ISerialPort);
1046 return NULL;
1047}
1048
1049
1050/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
1051
1052/**
1053 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1054 */
1055static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1056{
1057 RT_NOREF(offDelta);
1058 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1059
1060 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1061}
1062
1063
1064/**
1065 * @interface_method_impl{PDMDEVREG,pfnReset}
1066 */
1067static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns)
1068{
1069 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1070
1071 pThis->uRegDivisor = 0x0c; /* Default to 9600 Baud. */
1072 pThis->uRegRbr = 0;
1073 pThis->uRegThr = 0;
1074 pThis->uRegIer = 0;
1075 pThis->uRegIir = UART_REG_IIR_IP_NO_INT;
1076 pThis->uRegFcr = 0;
1077 pThis->uRegLcr = 0; /* 5 data bits, no parity, 1 stop bit. */
1078 pThis->uRegMcr = 0;
1079 pThis->uRegLsr = UART_REG_LSR_THRE | UART_REG_LSR_TEMT;
1080 pThis->uRegMsr = 0; /* Updated below. */
1081 pThis->uRegScr = 0;
1082
1083 /** @todo Clear FIFOs. */
1084 serialR3ParamsUpdate(pThis);
1085 serialIrqUpdate(pThis);
1086
1087 if (pThis->pDrvSerial)
1088 {
1089 /* Set the modem lines to reflect the current state. */
1090 int rc = pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial, false /*fRts*/, false /*fDtr*/);
1091 if (RT_FAILURE(rc))
1092 LogRel(("Serial#%d: Failed to set modem lines with %Rrc during reset\n",
1093 pThis->pDevInsR3->iInstance, rc));
1094
1095 uint32_t fStsLines = 0;
1096 rc = pThis->pDrvSerial->pfnQueryStsLines(pThis->pDrvSerial, &fStsLines);
1097 if (RT_SUCCESS(rc))
1098 serialR3StsLinesUpdate(pThis, fStsLines);
1099 else
1100 LogRel(("Serial#%d: Failed to query status line status with %Rrc during reset\n",
1101 pThis->pDevInsR3->iInstance, rc));
1102 }
1103}
1104
1105
1106/**
1107 * @interface_method_impl{PDMDEVREG,pfnAttach}
1108 */
1109static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1110{
1111 RT_NOREF(iLUN, fFlags);
1112 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1113
1114 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
1115 if (RT_SUCCESS(rc))
1116 {
1117 pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
1118 if (!pThis->pDrvSerial)
1119 {
1120 AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", pDevIns->iInstance));
1121 return VERR_PDM_MISSING_INTERFACE;
1122 }
1123 }
1124 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1125 {
1126 pThis->pDrvBase = NULL;
1127 pThis->pDrvSerial = NULL;
1128 rc = VINF_SUCCESS;
1129 LogRel(("Serial#%d: no unit\n", pDevIns->iInstance));
1130 }
1131 else /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
1132 LogRel(("Serial#%d: Failed to attach to serial driver. rc=%Rrc\n", pDevIns->iInstance, rc));
1133
1134 return rc;
1135}
1136
1137
1138/**
1139 * @interface_method_impl{PDMDEVREG,pfnDetach}
1140 */
1141static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1142{
1143 RT_NOREF(iLUN, fFlags);
1144 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1145
1146 /* Zero out important members. */
1147 pThis->pDrvBase = NULL;
1148 pThis->pDrvSerial = NULL;
1149}
1150
1151
1152/**
1153 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1154 */
1155static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns)
1156{
1157 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1158 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1159
1160 PDMR3CritSectDelete(&pThis->CritSect);
1161 return VINF_SUCCESS;
1162}
1163
1164
1165/**
1166 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1167 */
1168static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1169{
1170 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
1171 int rc = VINF_SUCCESS;
1172
1173 Assert(iInstance < 4);
1174 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1175
1176 /*
1177 * Initialize the instance data.
1178 * (Do this early or the destructor might choke on something!)
1179 */
1180 pThis->pDevInsR3 = pDevIns;
1181 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1182 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1183
1184 /* IBase */
1185 pThis->IBase.pfnQueryInterface = serialR3QueryInterface;
1186
1187 /* ISerialPort */
1188 pThis->ISerialPort.pfnDataAvailRdrNotify = serialR3DataAvailRdrNotify;
1189 pThis->ISerialPort.pfnDataSentNotify = serialR3DataSentNotify;
1190 pThis->ISerialPort.pfnReadWr = serialR3ReadWr;
1191 pThis->ISerialPort.pfnNotifyStsLinesChanged = serialR3NotifyStsLinesChanged;
1192 pThis->ISerialPort.pfnNotifyBrk = serialR3NotifyBrk;
1193
1194 /*
1195 * Validate and read the configuration.
1196 */
1197 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0"
1198 "IOBase\0"
1199 "GCEnabled\0"
1200 "R0Enabled\0"
1201 "YieldOnLSRRead\0"
1202 "Enable16550A\0"
1203 ))
1204 {
1205 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
1206 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1207 }
1208
1209 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fRCEnabled, true);
1210 if (RT_FAILURE(rc))
1211 return PDMDEV_SET_ERROR(pDevIns, rc,
1212 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
1213
1214 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
1215 if (RT_FAILURE(rc))
1216 return PDMDEV_SET_ERROR(pDevIns, rc,
1217 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
1218
1219 rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false);
1220 if (RT_FAILURE(rc))
1221 return PDMDEV_SET_ERROR(pDevIns, rc,
1222 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
1223
1224 uint8_t uIrq = 0;
1225 rc = CFGMR3QueryU8(pCfg, "IRQ", &uIrq);
1226 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1227 {
1228 /* Provide sensible defaults. */
1229 if (iInstance == 0)
1230 uIrq = 4;
1231 else if (iInstance == 1)
1232 uIrq = 3;
1233 else
1234 AssertReleaseFailed(); /* irq_lvl is undefined. */
1235 }
1236 else if (RT_FAILURE(rc))
1237 return PDMDEV_SET_ERROR(pDevIns, rc,
1238 N_("Configuration error: Failed to get the \"IRQ\" value"));
1239
1240 uint16_t uIoBase = 0;
1241 rc = CFGMR3QueryU16(pCfg, "IOBase", &uIoBase);
1242 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1243 {
1244 if (iInstance == 0)
1245 uIoBase = 0x3f8;
1246 else if (iInstance == 1)
1247 uIoBase = 0x2f8;
1248 else
1249 AssertReleaseFailed(); /* uIoBase is undefined */
1250 }
1251 else if (RT_FAILURE(rc))
1252 return PDMDEV_SET_ERROR(pDevIns, rc,
1253 N_("Configuration error: Failed to get the \"IOBase\" value"));
1254
1255 rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &pThis->f16550AEnabled, false);
1256 if (RT_FAILURE(rc))
1257 return PDMDEV_SET_ERROR(pDevIns, rc,
1258 N_("Configuration error: Failed to get the \"Enable16550A\" value"));
1259
1260 pThis->uIrq = uIrq;
1261 pThis->PortBase = uIoBase;
1262
1263 LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n",
1264 pDevIns->iInstance, pThis->f16550AEnabled ? "16550A" : "16450", uIoBase, uIrq));
1265
1266 /*
1267 * Initialize critical section and the semaphore. Change the default
1268 * critical section to ours so that TM and IOM will enter it before
1269 * calling us.
1270 *
1271 * Note! This must of be done BEFORE creating timers, registering I/O ports
1272 * and other things which might pick up the default CS or end up
1273 * calling back into the device.
1274 */
1275 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "Serial#%d", iInstance);
1276 AssertRCReturn(rc, rc);
1277
1278 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
1279 AssertRCReturn(rc, rc);
1280
1281 /*
1282 * Register the I/O ports.
1283 */
1284 rc = PDMDevHlpIOPortRegister(pDevIns, uIoBase, 8, 0,
1285 serialIoPortWrite, serialIoPortRead,
1286 NULL, NULL, "SERIAL");
1287 if (RT_FAILURE(rc))
1288 return rc;
1289
1290 if (pThis->fRCEnabled)
1291 {
1292 rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite",
1293 "serialIoPortRead", NULL, NULL, "SERIAL");
1294 if (RT_FAILURE(rc))
1295 return rc;
1296 }
1297
1298 if (pThis->fR0Enabled)
1299 {
1300 rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite",
1301 "serialIoPortRead", NULL, NULL, "SERIAL");
1302 if (RT_FAILURE(rc))
1303 return rc;
1304 }
1305
1306#if 0 /** @todo Later */
1307 /*
1308 * Saved state.
1309 */
1310 rc = PDMDevHlpSSMRegister3(pDevIns, SERIAL_SAVED_STATE_VERSION, sizeof (*pThis),
1311 serialR3LiveExec, serialR3SaveExec, serialR3LoadExec);
1312 if (RT_FAILURE(rc))
1313 return rc;
1314#endif
1315
1316 /*
1317 * Attach the char driver and get the interfaces.
1318 */
1319 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial");
1320 if (RT_SUCCESS(rc))
1321 {
1322 pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
1323 if (!pThis->pDrvSerial)
1324 {
1325 AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", iInstance));
1326 return VERR_PDM_MISSING_INTERFACE;
1327 }
1328 }
1329 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1330 {
1331 pThis->pDrvBase = NULL;
1332 pThis->pDrvSerial = NULL;
1333 LogRel(("Serial#%d: no unit\n", iInstance));
1334 }
1335 else
1336 {
1337 AssertLogRelMsgFailed(("Serial#%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc));
1338 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
1339 return rc;
1340 }
1341
1342 serialR3Reset(pDevIns);
1343 return VINF_SUCCESS;
1344}
1345
1346
1347/**
1348 * The device registration structure.
1349 */
1350const PDMDEVREG g_DeviceSerialPort =
1351{
1352 /* u32Version */
1353 PDM_DEVREG_VERSION,
1354 /* szName */
1355 "serial",
1356 /* szRCMod */
1357 "VBoxDDRC.rc",
1358 /* szR0Mod */
1359 "VBoxDDR0.r0",
1360 /* pszDescription */
1361 "Serial Communication Port",
1362 /* fFlags */
1363 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1364 /* fClass */
1365 PDM_DEVREG_CLASS_SERIAL,
1366 /* cMaxInstances */
1367 UINT32_MAX,
1368 /* cbInstance */
1369 sizeof(DEVSERIAL),
1370 /* pfnConstruct */
1371 serialR3Construct,
1372 /* pfnDestruct */
1373 serialR3Destruct,
1374 /* pfnRelocate */
1375 serialR3Relocate,
1376 /* pfnMemSetup */
1377 NULL,
1378 /* pfnPowerOn */
1379 NULL,
1380 /* pfnReset */
1381 serialR3Reset,
1382 /* pfnSuspend */
1383 NULL,
1384 /* pfnResume */
1385 NULL,
1386 /* pfnAttach */
1387 serialR3Attach,
1388 /* pfnDetach */
1389 serialR3Detach,
1390 /* pfnQueryInterface. */
1391 NULL,
1392 /* pfnInitComplete */
1393 NULL,
1394 /* pfnPowerOff */
1395 NULL,
1396 /* pfnSoftReset */
1397 NULL,
1398 /* u32VersionEnd */
1399 PDM_DEVREG_VERSION
1400};
1401#endif /* IN_RING3 */
1402
1403#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