VirtualBox

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

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

Clear MSR changed variable after reading lsr register to clear interrupt status

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