VirtualBox

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

Last change on this file since 6071 was 6018, checked in by vboxsync, 17 years ago

Fix compilation

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette