VirtualBox

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

Last change on this file since 9113 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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