VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevPit-i8254.cpp

Last change on this file was 103282, checked in by vboxsync, 3 months ago

Dev/PC/DevPit-i8254: Don't leak fd if speaker emulation isn't possible (found by Parfait, improved fix, doxygen fix). bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 57.8 KB
Line 
1/* $Id: DevPit-i8254.cpp 103282 2024-02-08 15:04:45Z vboxsync $ */
2/** @file
3 * DevPIT-i8254 - Intel 8254 Programmable Interval Timer (PIT) And Dummy Speaker Device.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is based on:
29 *
30 * QEMU 8253/8254 interval timer emulation
31 *
32 * Copyright (c) 2003-2004 Fabrice Bellard
33 *
34 * Permission is hereby granted, free of charge, to any person obtaining a copy
35 * of this software and associated documentation files (the "Software"), to deal
36 * in the Software without restriction, including without limitation the rights
37 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 * copies of the Software, and to permit persons to whom the Software is
39 * furnished to do so, subject to the following conditions:
40 *
41 * The above copyright notice and this permission notice shall be included in
42 * all copies or substantial portions of the Software.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
47 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50 * THE SOFTWARE.
51 */
52
53
54/*********************************************************************************************************************************
55* Header Files *
56*********************************************************************************************************************************/
57#define LOG_GROUP LOG_GROUP_DEV_PIT
58#include <VBox/vmm/pdmdev.h>
59#include <VBox/log.h>
60#include <VBox/vmm/stam.h>
61#include <iprt/assert.h>
62#include <iprt/asm-math.h>
63
64#ifdef IN_RING3
65# ifdef RT_OS_LINUX
66# include <fcntl.h>
67# include <errno.h>
68# include <unistd.h>
69# include <stdio.h>
70# include <linux/kd.h>
71# include <linux/input.h>
72# include <sys/ioctl.h>
73# endif
74# include <iprt/alloc.h>
75# include <iprt/string.h>
76# include <iprt/uuid.h>
77#endif /* IN_RING3 */
78
79#include "VBoxDD.h"
80
81
82/*********************************************************************************************************************************
83* Defined Constants And Macros *
84*********************************************************************************************************************************/
85/** The PIT frequency. */
86#define PIT_FREQ 1193182
87
88#define RW_STATE_LSB 1
89#define RW_STATE_MSB 2
90#define RW_STATE_WORD0 3
91#define RW_STATE_WORD1 4
92
93/** The current saved state version. */
94#define PIT_SAVED_STATE_VERSION 4
95/** The saved state version used by VirtualBox 3.1 and earlier.
96 * This did not include disable by HPET flag. */
97#define PIT_SAVED_STATE_VERSION_VBOX_31 3
98/** The saved state version used by VirtualBox 3.0 and earlier.
99 * This did not include the config part. */
100#define PIT_SAVED_STATE_VERSION_VBOX_30 2
101
102/** @def FAKE_REFRESH_CLOCK
103 * Define this to flip the 15usec refresh bit on every read.
104 * If not defined, it will be flipped correctly. */
105/* #define FAKE_REFRESH_CLOCK */
106#ifdef DOXYGEN_RUNNING
107# define FAKE_REFRESH_CLOCK
108#endif
109
110/** The effective counter mode - if bit 1 is set, bit 2 is ignored. */
111#define EFFECTIVE_MODE(x) ((x) & ~(((x) & 2) << 1))
112
113
114/**
115 * Acquires the PIT lock or returns.
116 */
117#define DEVPIT_LOCK_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
118 do { \
119 int const rcLock = PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, (a_rcBusy)); \
120 if (rcLock == VINF_SUCCESS) { /* likely */ } \
121 else return rcLock; \
122 } while (0)
123
124/**
125 * Releases the PIT lock.
126 */
127#define DEVPIT_UNLOCK(a_pDevIns, a_pThis) \
128 do { PDMDevHlpCritSectLeave((a_pDevIns), &(a_pThis)->CritSect); } while (0)
129
130
131/**
132 * Acquires the TM lock and PIT lock, returns on failure.
133 */
134#define DEVPIT_LOCK_BOTH_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
135 do { \
136 VBOXSTRICTRC rcLock = PDMDevHlpTimerLockClock2((a_pDevIns), (a_pThis)->channels[0].hTimer, \
137 &(a_pThis)->CritSect, (a_rcBusy)); \
138 if (RT_LIKELY(rcLock == VINF_SUCCESS)) \
139 { /* likely */ } \
140 else \
141 return rcLock; \
142 } while (0)
143
144#ifdef IN_RING3
145/**
146 * Acquires the TM lock and PIT lock, ignores failures.
147 */
148# define DEVPIT_R3_LOCK_BOTH(a_pDevIns, a_pThis) \
149 PDMDevHlpTimerLockClock2((a_pDevIns), (a_pThis)->channels[0].hTimer, &(a_pThis)->CritSect, VERR_IGNORED)
150#endif /* IN_RING3 */
151
152/**
153 * Releases the PIT lock and TM lock.
154 */
155#define DEVPIT_UNLOCK_BOTH(a_pDevIns, a_pThis) \
156 PDMDevHlpTimerUnlockClock2((a_pDevIns), (a_pThis)->channels[0].hTimer, &(a_pThis)->CritSect)
157
158
159
160/*********************************************************************************************************************************
161* Structures and Typedefs *
162*********************************************************************************************************************************/
163/**
164 * The state of one PIT channel.
165 */
166typedef struct PITCHANNEL
167{
168 /** The timer.
169 * @note Only channel 0 has a timer. */
170 TMTIMERHANDLE hTimer;
171 /** The virtual time stamp at the last reload (only used in mode 2 for now). */
172 uint64_t u64ReloadTS;
173 /** The actual time of the next tick.
174 * As apposed to the next_transition_time which contains the correct time of the next tick. */
175 uint64_t u64NextTS;
176
177 /** (count_load_time is only set by PDMDevHlpTimerGet() which returns uint64_t) */
178 uint64_t count_load_time;
179 /* irq handling */
180 int64_t next_transition_time;
181 int32_t irq;
182 /** Number of release log entries. Used to prevent flooding. */
183 uint8_t cRelLogEntries;
184 /** The channel number. */
185 uint8_t iChan;
186 uint8_t abAlignment[2];
187
188 uint32_t count; /* can be 65536 */
189 uint16_t latched_count;
190 uint8_t count_latched;
191 uint8_t status_latched;
192
193 uint8_t status;
194 uint8_t read_state;
195 uint8_t write_state;
196 uint8_t write_latch;
197
198 uint8_t rw_mode;
199 uint8_t mode;
200 uint8_t bcd; /* not supported */
201 uint8_t gate; /* timer start */
202
203} PITCHANNEL;
204/** Pointer to the state of one PIT channel. */
205typedef PITCHANNEL *PPITCHANNEL;
206
207/** Speaker emulation state. */
208typedef enum PITSPEAKEREMU
209{
210 PIT_SPEAKER_EMU_NONE = 0,
211 PIT_SPEAKER_EMU_CONSOLE,
212 PIT_SPEAKER_EMU_EVDEV,
213 PIT_SPEAKER_EMU_TTY
214} PITSPEAKEREMU;
215
216/**
217 * The shared PIT state.
218 */
219typedef struct PITSTATE
220{
221 /** Channel state. Must come first? */
222 PITCHANNEL channels[3];
223 /** Speaker data. */
224 int32_t speaker_data_on;
225#ifdef FAKE_REFRESH_CLOCK
226 /** Refresh dummy. */
227 int32_t dummy_refresh_clock;
228#else
229 uint32_t Alignment1;
230#endif
231 /** Config: I/O port base. */
232 RTIOPORT IOPortBaseCfg;
233 /** Disconnect PIT from the interrupt controllers if requested by HPET. */
234 bool fDisabledByHpet;
235 /** Number of IRQs that's been raised. */
236 STAMCOUNTER StatPITIrq;
237 /** Profiling the timer callback handler. */
238 STAMPROFILEADV StatPITHandler;
239 /** Critical section protecting the state. */
240 PDMCRITSECT CritSect;
241 /** The primary I/O port range (0x40-0x43). */
242 IOMIOPORTHANDLE hIoPorts;
243 /** The speaker I/O port range (0x40-0x43). */
244 IOMIOPORTHANDLE hIoPortSpeaker;
245} PITSTATE;
246/** Pointer to the shared PIT device state. */
247typedef PITSTATE *PPITSTATE;
248
249
250/**
251 * The ring-3 PIT state.
252 */
253typedef struct PITSTATER3
254{
255 /** PIT port interface. */
256 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
257 /** Pointer to the device instance. */
258 PPDMDEVINSR3 pDevIns;
259 /** Config: Speaker enabled. */
260 bool fSpeakerCfg;
261 /** Config: What to do with speaker activity. */
262 PITSPEAKEREMU enmSpeakerEmu;
263#ifdef RT_OS_LINUX
264 /** File handle for host speaker functionality. */
265 int hHostSpeaker;
266 int afAlignment2;
267#endif
268} PITSTATER3;
269/** Pointer to the ring-3 PIT device state. */
270typedef PITSTATER3 *PPITSTATER3;
271
272
273#ifndef VBOX_DEVICE_STRUCT_TESTCASE
274
275
276
277static int pit_get_count(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan)
278{
279 uint64_t d;
280 TMTIMERHANDLE hTimer = pThis->channels[0].hTimer;
281 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, hTimer));
282
283 if (EFFECTIVE_MODE(pChan->mode) == 2)
284 {
285 if (pChan->u64NextTS == UINT64_MAX)
286 {
287 d = ASMMultU64ByU32DivByU32(PDMDevHlpTimerGet(pDevIns, hTimer) - pChan->count_load_time,
288 PIT_FREQ, PDMDevHlpTimerGetFreq(pDevIns, hTimer));
289 return pChan->count - (d % pChan->count); /** @todo check this value. */
290 }
291 uint64_t Interval = pChan->u64NextTS - pChan->u64ReloadTS;
292 if (!Interval)
293 return pChan->count - 1; /** @todo This is WRONG! But I'm too tired to fix it properly and just want to shut up a DIV/0 trap now. */
294 d = PDMDevHlpTimerGet(pDevIns, hTimer);
295 d = ASMMultU64ByU32DivByU32(d - pChan->u64ReloadTS, pChan->count, Interval);
296 if (d >= pChan->count)
297 return 1;
298 return pChan->count - d;
299 }
300
301 d = ASMMultU64ByU32DivByU32(PDMDevHlpTimerGet(pDevIns, hTimer) - pChan->count_load_time,
302 PIT_FREQ, PDMDevHlpTimerGetFreq(pDevIns, hTimer));
303 int counter;
304 switch (EFFECTIVE_MODE(pChan->mode))
305 {
306 case 0:
307 case 1:
308 case 4:
309 case 5:
310 counter = (pChan->count - d) & 0xffff;
311 break;
312 case 3:
313 /* XXX: may be incorrect for odd counts */
314 counter = pChan->count - ((2 * d) % pChan->count);
315 break;
316 default:
317 counter = pChan->count - (d % pChan->count);
318 break;
319 }
320 /** @todo check that we don't return 0, in most modes (all?) the counter shouldn't be zero. */
321 return counter;
322}
323
324
325/* get pit output bit */
326static int pit_get_out1(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan, int64_t current_time)
327{
328 TMTIMERHANDLE hTimer = pThis->channels[0].hTimer;
329 uint64_t d;
330 int out;
331
332 d = ASMMultU64ByU32DivByU32(current_time - pChan->count_load_time, PIT_FREQ, PDMDevHlpTimerGetFreq(pDevIns, hTimer));
333 switch (EFFECTIVE_MODE(pChan->mode))
334 {
335 default:
336 case 0:
337 out = (d >= pChan->count);
338 break;
339 case 1:
340 out = (d < pChan->count);
341 break;
342 case 2:
343 Log2(("pit_get_out1: d=%llx c=%x %x \n", d, pChan->count, (unsigned)(d % pChan->count)));
344 if ((d % pChan->count) == 0 && d != 0)
345 out = 1;
346 else
347 out = 0;
348 break;
349 case 3:
350 out = (d % pChan->count) < ((pChan->count + 1) >> 1);
351 break;
352 case 4:
353 case 5:
354 out = (d != pChan->count);
355 break;
356 }
357 return out;
358}
359
360
361static int pit_get_out(PPDMDEVINS pDevIns, PPITSTATE pThis, int channel, int64_t current_time)
362{
363 PPITCHANNEL pChan = &pThis->channels[channel];
364 return pit_get_out1(pDevIns, pThis, pChan, current_time);
365}
366
367
368static int pit_get_gate(PPITSTATE pThis, int channel)
369{
370 PPITCHANNEL pChan = &pThis->channels[channel];
371 return pChan->gate;
372}
373
374
375/* if already latched, do not latch again */
376static void pit_latch_count(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan)
377{
378 if (!pChan->count_latched)
379 {
380 pChan->latched_count = pit_get_count(pDevIns, pThis, pChan);
381 pChan->count_latched = pChan->rw_mode;
382 LogFlow(("pit_latch_count: latched_count=%#06x / %10RU64 ns (c=%#06x m=%d)\n",
383 pChan->latched_count, ASMMultU64ByU32DivByU32(pChan->count - pChan->latched_count, 1000000000, PIT_FREQ),
384 pChan->count, pChan->mode));
385 }
386}
387
388#ifdef IN_RING3
389
390/* return -1 if no transition will occur. */
391static int64_t pitR3GetNextTransitionTime(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan, uint64_t current_time)
392{
393 TMTIMERHANDLE hTimer = pThis->channels[0].hTimer;
394 uint64_t d, next_time, base;
395 uint32_t period2;
396
397 d = ASMMultU64ByU32DivByU32(current_time - pChan->count_load_time, PIT_FREQ, PDMDevHlpTimerGetFreq(pDevIns, hTimer));
398 switch (EFFECTIVE_MODE(pChan->mode))
399 {
400 default:
401 case 0:
402 case 1:
403 if (d < pChan->count)
404 next_time = pChan->count;
405 else
406 return -1;
407 break;
408
409 /*
410 * Mode 2: The period is 'count' PIT ticks.
411 * When the counter reaches 1 we set the output low (for channel 0 that
412 * means lowering IRQ0). On the next tick, where we should be decrementing
413 * from 1 to 0, the count is loaded and the output goes high (channel 0
414 * means raising IRQ0 again and triggering timer interrupt).
415 *
416 * In VirtualBox we compress the pulse and flip-flop the IRQ line at the
417 * end of the period, which signals an interrupt at the exact same time.
418 */
419 case 2:
420 base = (d / pChan->count) * pChan->count;
421# ifndef VBOX /* see above */
422 if ((d - base) == 0 && d != 0)
423 next_time = base + pChan->count - 1;
424 else
425# endif
426 next_time = base + pChan->count;
427 break;
428 case 3:
429 base = (d / pChan->count) * pChan->count;
430 period2 = ((pChan->count + 1) >> 1);
431 if ((d - base) < period2)
432 next_time = base + period2;
433 else
434 next_time = base + pChan->count;
435 break;
436
437 /* Modes 4 and 5 generate a short pulse at the end of the time delay. This
438 * is similar to mode 2, except modes 4/5 aren't periodic. We use the same
439 * optimization - only use one timer callback and pulse the IRQ.
440 * Note: Tickless Linux kernels use PIT mode 4 with 'nolapic'.
441 */
442 case 4:
443 case 5:
444# ifdef VBOX
445 if (d <= pChan->count)
446 next_time = pChan->count;
447# else
448 if (d < pChan->count)
449 next_time = pChan->count;
450 else if (d == pChan->count)
451 next_time = pChan->count + 1;
452# endif
453 else
454 return -1;
455 break;
456 }
457
458 /* convert to timer units */
459 LogFlow(("PIT: next_time=%'14RU64 %'20RU64 mode=%#x count=%#06x\n", next_time,
460 ASMMultU64ByU32DivByU32(next_time, PDMDevHlpTimerGetFreq(pDevIns, hTimer), PIT_FREQ), pChan->mode, pChan->count));
461 next_time = pChan->count_load_time + ASMMultU64ByU32DivByU32(next_time, PDMDevHlpTimerGetFreq(pDevIns, hTimer), PIT_FREQ);
462
463 /* fix potential rounding problems */
464 if (next_time <= current_time)
465 next_time = current_time;
466
467 /* Add one to next_time; if we don't, integer truncation will cause
468 * the algorithm to think that at the end of each period, it'pChan still
469 * within the first one instead of at the beginning of the next one.
470 */
471 return next_time + 1;
472}
473
474
475static void pitR3IrqTimerUpdate(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan,
476 uint64_t current_time, uint64_t now, bool in_timer)
477{
478 int64_t expire_time;
479 int irq_level;
480 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->channels[0].hTimer));
481
482 if (pChan->hTimer == NIL_TMTIMERHANDLE)
483 return;
484 expire_time = pitR3GetNextTransitionTime(pDevIns, pThis, pChan, current_time);
485 irq_level = pit_get_out1(pDevIns, pThis, pChan, current_time) ? PDM_IRQ_LEVEL_HIGH : PDM_IRQ_LEVEL_LOW;
486
487 /* If PIT is disabled by HPET - simply disconnect ticks from interrupt controllers,
488 * but do not modify other aspects of device operation.
489 */
490 if (!pThis->fDisabledByHpet)
491 {
492 switch (EFFECTIVE_MODE(pChan->mode))
493 {
494 case 2:
495 case 4:
496 case 5:
497 /* We just flip-flop the IRQ line to save an extra timer call,
498 * which isn't generally required. However, the pulse is only
499 * generated when running on the timer callback (and thus on
500 * the trailing edge of the output signal pulse).
501 */
502 if (in_timer)
503 {
504 PDMDevHlpISASetIrq(pDevIns, pChan->irq, PDM_IRQ_LEVEL_FLIP_FLOP);
505 break;
506 }
507 RT_FALL_THRU();
508 default:
509 PDMDevHlpISASetIrq(pDevIns, pChan->irq, irq_level);
510 break;
511 }
512 }
513
514 if (irq_level)
515 {
516 pChan->u64ReloadTS = now;
517 STAM_COUNTER_INC(&pThis->StatPITIrq);
518 }
519
520 if (expire_time != -1)
521 {
522 Log3(("pitR3IrqTimerUpdate: next=%'RU64 now=%'RU64\n", expire_time, now));
523 pChan->u64NextTS = expire_time;
524 PDMDevHlpTimerSet(pDevIns, pChan->hTimer, pChan->u64NextTS);
525 }
526 else
527 {
528 LogFlow(("PIT: m=%d count=%#4x irq_level=%#x stopped\n", pChan->mode, pChan->count, irq_level));
529 PDMDevHlpTimerStop(pDevIns, pChan->hTimer);
530 pChan->u64NextTS = UINT64_MAX;
531 }
532 pChan->next_transition_time = expire_time;
533}
534
535
536/* val must be 0 or 1 */
537static void pitR3SetGate(PPDMDEVINS pDevIns, PPITSTATE pThis, int channel, int val)
538{
539 PPITCHANNEL pChan = &pThis->channels[channel];
540 TMTIMERHANDLE hTimer = pThis->channels[0].hTimer;
541
542 Assert((val & 1) == val);
543 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, hTimer));
544
545 switch (EFFECTIVE_MODE(pChan->mode))
546 {
547 default:
548 case 0:
549 case 4:
550 /* XXX: just disable/enable counting */
551 break;
552 case 1:
553 case 5:
554 if (pChan->gate < val)
555 {
556 /* restart counting on rising edge */
557 Log(("pitR3SetGate: restarting mode %d\n", pChan->mode));
558 pChan->count_load_time = PDMDevHlpTimerGet(pDevIns, hTimer);
559 pitR3IrqTimerUpdate(pDevIns, pThis, pChan, pChan->count_load_time, pChan->count_load_time, false);
560 }
561 break;
562 case 2:
563 case 3:
564 if (pChan->gate < val)
565 {
566 /* restart counting on rising edge */
567 Log(("pitR3SetGate: restarting mode %d\n", pChan->mode));
568 pChan->count_load_time = pChan->u64ReloadTS = PDMDevHlpTimerGet(pDevIns, hTimer);
569 pitR3IrqTimerUpdate(pDevIns, pThis, pChan, pChan->count_load_time, pChan->count_load_time, false);
570 }
571 /* XXX: disable/enable counting */
572 break;
573 }
574 pChan->gate = val;
575}
576
577
578static void pitR3LoadCount(PPDMDEVINS pDevIns, PPITSTATE pThis, PPITCHANNEL pChan, int val)
579{
580 TMTIMERHANDLE hTimer = pThis->channels[0].hTimer;
581 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, hTimer));
582
583 if (val == 0)
584 val = 0x10000;
585 pChan->count_load_time = pChan->u64ReloadTS = PDMDevHlpTimerGet(pDevIns, hTimer);
586 pChan->count = val;
587 pitR3IrqTimerUpdate(pDevIns, pThis, pChan, pChan->count_load_time, pChan->count_load_time, false);
588
589 /* log the new rate (ch 0 only). */
590 if (pChan->hTimer != NIL_TMTIMERHANDLE /* ch 0 */)
591 {
592 if (pChan->cRelLogEntries < 32)
593 {
594 pChan->cRelLogEntries++;
595 LogRel(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=0)\n",
596 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100));
597 }
598 else
599 Log(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=0)\n",
600 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100));
601 PDMDevHlpTimerSetFrequencyHint(pDevIns, hTimer, PIT_FREQ / pChan->count);
602 }
603 else
604 Log(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=%d)\n",
605 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100,
606 pChan - &pThis->channels[0]));
607}
608
609#endif /* IN_RING3 */
610
611/**
612 * @callback_method_impl{FNIOMIOPORTNEWIN}
613 */
614static DECLCALLBACK(VBOXSTRICTRC) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
615{
616 Log2(("pitIOPortRead: offPort=%#x cb=%x\n", offPort, cb));
617 NOREF(pvUser);
618 Assert(offPort < 4);
619 if (cb != 1 || offPort == 3)
620 {
621 Log(("pitIOPortRead: offPort=%#x cb=%x *pu32=unused!\n", offPort, cb));
622 return VERR_IOM_IOPORT_UNUSED;
623 }
624 RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
625
626 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
627 PPITCHANNEL pChan = &pThis->channels[offPort];
628 int ret;
629
630 DEVPIT_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
631 if (pChan->status_latched)
632 {
633 pChan->status_latched = 0;
634 ret = pChan->status;
635 DEVPIT_UNLOCK(pDevIns, pThis);
636 }
637 else if (pChan->count_latched)
638 {
639 switch (pChan->count_latched)
640 {
641 default:
642 case RW_STATE_LSB:
643 ret = pChan->latched_count & 0xff;
644 pChan->count_latched = 0;
645 break;
646 case RW_STATE_MSB:
647 ret = pChan->latched_count >> 8;
648 pChan->count_latched = 0;
649 break;
650 case RW_STATE_WORD0:
651 ret = pChan->latched_count & 0xff;
652 pChan->count_latched = RW_STATE_MSB;
653 break;
654 }
655 DEVPIT_UNLOCK(pDevIns, pThis);
656 }
657 else
658 {
659 DEVPIT_UNLOCK(pDevIns, pThis);
660 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
661 int count;
662 switch (pChan->read_state)
663 {
664 default:
665 case RW_STATE_LSB:
666 count = pit_get_count(pDevIns, pThis, pChan);
667 ret = count & 0xff;
668 break;
669 case RW_STATE_MSB:
670 count = pit_get_count(pDevIns, pThis, pChan);
671 ret = (count >> 8) & 0xff;
672 break;
673 case RW_STATE_WORD0:
674 count = pit_get_count(pDevIns, pThis, pChan);
675 ret = count & 0xff;
676 pChan->read_state = RW_STATE_WORD1;
677 break;
678 case RW_STATE_WORD1:
679 count = pit_get_count(pDevIns, pThis, pChan);
680 ret = (count >> 8) & 0xff;
681 pChan->read_state = RW_STATE_WORD0;
682 break;
683 }
684 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
685 }
686
687 *pu32 = ret;
688 Log2(("pitIOPortRead: offPort=%#x cb=%x *pu32=%#04x\n", offPort, cb, *pu32));
689 return VINF_SUCCESS;
690}
691
692
693/**
694 * @callback_method_impl{FNIOMIOPORTNEWOUT}
695 */
696static DECLCALLBACK(VBOXSTRICTRC) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
697{
698 Log2(("pitIOPortWrite: offPort=%#x cb=%x u32=%#04x\n", offPort, cb, u32));
699 NOREF(pvUser);
700 Assert(offPort < 4);
701
702 if (cb != 1)
703 return VINF_SUCCESS;
704
705 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
706 if (offPort == 3)
707 {
708 /*
709 * Port 43h - Mode/Command Register.
710 * 7 6 5 4 3 2 1 0
711 * * * . . . . . . Select channel: 0 0 = Channel 0
712 * 0 1 = Channel 1
713 * 1 0 = Channel 2
714 * 1 1 = Read-back command (8254 only)
715 * (Illegal on 8253)
716 * (Illegal on PS/2 {JAM})
717 * . . * * . . . . Command/Access mode: 0 0 = Latch count value command
718 * 0 1 = Access mode: lobyte only
719 * 1 0 = Access mode: hibyte only
720 * 1 1 = Access mode: lobyte/hibyte
721 * . . . . * * * . Operating mode: 0 0 0 = Mode 0, 0 0 1 = Mode 1,
722 * 0 1 0 = Mode 2, 0 1 1 = Mode 3,
723 * 1 0 0 = Mode 4, 1 0 1 = Mode 5,
724 * 1 1 0 = Mode 2, 1 1 1 = Mode 3
725 * . . . . . . . * BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD
726 */
727 unsigned channel = (u32 >> 6) & 0x3;
728 RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
729 if (channel == 3)
730 {
731 /* read-back command */
732 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
733 for (channel = 0; channel < RT_ELEMENTS(pThis->channels); channel++)
734 {
735 PPITCHANNEL pChan = &pThis->channels[channel];
736 if (u32 & (2 << channel))
737 {
738 if (!(u32 & 0x20))
739 pit_latch_count(pDevIns, pThis, pChan);
740 if (!(u32 & 0x10) && !pChan->status_latched)
741 {
742 /* status latch */
743 /* XXX: add BCD and null count */
744 pChan->status = (pit_get_out1(pDevIns, pThis, pChan,
745 PDMDevHlpTimerGet(pDevIns, pThis->channels[0].hTimer)) << 7)
746 | (pChan->rw_mode << 4)
747 | (pChan->mode << 1)
748 | pChan->bcd;
749 pChan->status_latched = 1;
750 }
751 }
752 }
753 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
754 }
755 else
756 {
757 PPITCHANNEL pChan = &pThis->channels[channel];
758 unsigned access = (u32 >> 4) & 3;
759 if (access == 0)
760 {
761 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
762 pit_latch_count(pDevIns, pThis, pChan);
763 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
764 }
765 else
766 {
767 DEVPIT_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
768 pChan->rw_mode = access;
769 pChan->read_state = access;
770 pChan->write_state = access;
771
772 pChan->mode = (u32 >> 1) & 7;
773 pChan->bcd = u32 & 1;
774 /* XXX: update irq timer ? */
775 DEVPIT_UNLOCK(pDevIns, pThis);
776 }
777 }
778 }
779 else
780 {
781#ifndef IN_RING3
782 /** @todo There is no reason not to do this in all contexts these
783 * days... */
784 return VINF_IOM_R3_IOPORT_WRITE;
785#else /* IN_RING3 */
786 /*
787 * Port 40-42h - Channel Data Ports.
788 */
789 RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
790 PPITCHANNEL pChan = &pThis->channels[offPort];
791 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
792 switch (pChan->write_state)
793 {
794 default:
795 case RW_STATE_LSB:
796 pitR3LoadCount(pDevIns, pThis, pChan, u32);
797 break;
798 case RW_STATE_MSB:
799 pitR3LoadCount(pDevIns, pThis, pChan, u32 << 8);
800 break;
801 case RW_STATE_WORD0:
802 pChan->write_latch = u32;
803 pChan->write_state = RW_STATE_WORD1;
804 break;
805 case RW_STATE_WORD1:
806 pitR3LoadCount(pDevIns, pThis, pChan, pChan->write_latch | (u32 << 8));
807 pChan->write_state = RW_STATE_WORD0;
808 break;
809 }
810 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
811#endif /* !IN_RING3 */
812 }
813 return VINF_SUCCESS;
814}
815
816
817/**
818 * @callback_method_impl{FNIOMIOPORTNEWIN, Speaker}
819 */
820static DECLCALLBACK(VBOXSTRICTRC)
821pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
822{
823 RT_NOREF(pvUser, offPort);
824 if (cb == 1)
825 {
826 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
827 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
828
829 const uint64_t u64Now = PDMDevHlpTimerGet(pDevIns, pThis->channels[0].hTimer);
830 Assert(PDMDevHlpTimerGetFreq(pDevIns, pThis->channels[0].hTimer) == 1000000000); /* lazy bird. */
831
832 /* bit 6,7 Parity error stuff. */
833 /* bit 5 - mirrors timer 2 output condition. */
834 const int fOut = pit_get_out(pDevIns, pThis, 2, u64Now);
835 /* bit 4 - toggled with each (DRAM?) refresh request, every 15.085 u-op Chan.
836 ASSUMES ns timer freq, see assertion above. */
837#ifndef FAKE_REFRESH_CLOCK
838 const int fRefresh = (u64Now / 15085) & 1;
839#else
840 pThis->dummy_refresh_clock ^= 1;
841 const int fRefresh = pThis->dummy_refresh_clock;
842#endif
843 /* bit 2,3 NMI / parity status stuff. */
844 /* bit 1 - speaker data status */
845 const int fSpeakerStatus = pThis->speaker_data_on;
846 /* bit 0 - timer 2 clock gate to speaker status. */
847 const int fTimer2GateStatus = pit_get_gate(pThis, 2);
848
849 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
850
851 *pu32 = fTimer2GateStatus
852 | (fSpeakerStatus << 1)
853 | (fRefresh << 4)
854 | (fOut << 5);
855 Log(("pitIOPortSpeakerRead: offPort=%#x cb=%x *pu32=%#x\n", offPort, cb, *pu32));
856 return VINF_SUCCESS;
857 }
858 Log(("pitIOPortSpeakerRead: offPort=%#x cb=%x *pu32=unused!\n", offPort, cb));
859 return VERR_IOM_IOPORT_UNUSED;
860}
861
862#ifdef IN_RING3
863
864/**
865 * @callback_method_impl{FNIOMIOPORTNEWOUT, Speaker}
866 */
867static DECLCALLBACK(VBOXSTRICTRC)
868pitR3IOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
869{
870 RT_NOREF(pvUser, offPort);
871 if (cb == 1)
872 {
873 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
874 DEVPIT_LOCK_BOTH_RETURN(pDevIns, pThis, VERR_IGNORED);
875
876 pThis->speaker_data_on = (u32 >> 1) & 1;
877 pitR3SetGate(pDevIns, pThis, 2, u32 & 1);
878
879 /** @todo r=klaus move this to a (system-specific) driver, which can
880 * abstract the details, and if necessary create a thread to minimize
881 * impact on VM execution. */
882# ifdef RT_OS_LINUX
883 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
884
885 if (pThisCC->enmSpeakerEmu != PIT_SPEAKER_EMU_NONE)
886 {
887 PPITCHANNEL pChan = &pThis->channels[2];
888 if (pThis->speaker_data_on)
889 {
890 Log2Func(("starting beep freq=%d\n", PIT_FREQ / pChan->count));
891 switch (pThisCC->enmSpeakerEmu)
892 {
893 case PIT_SPEAKER_EMU_CONSOLE:
894 {
895 int res;
896 res = ioctl(pThisCC->hHostSpeaker, KIOCSOUND, pChan->count);
897 if (res == -1)
898 {
899 LogRel(("PIT: speaker: ioctl failed errno=%d, disabling emulation\n", errno));
900 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_NONE;
901 }
902 break;
903 }
904 case PIT_SPEAKER_EMU_EVDEV:
905 {
906 struct input_event e;
907 e.type = EV_SND;
908 e.code = SND_TONE;
909 e.value = PIT_FREQ / pChan->count;
910 int res = write(pThisCC->hHostSpeaker, &e, sizeof(struct input_event));
911 NOREF(res);
912 break;
913 }
914 case PIT_SPEAKER_EMU_TTY:
915 {
916 int res = write(pThisCC->hHostSpeaker, "\a", 1);
917 NOREF(res);
918 break;
919 }
920 case PIT_SPEAKER_EMU_NONE:
921 break;
922 default:
923 Log2Func(("unknown speaker emulation %d, disabling emulation\n", pThisCC->enmSpeakerEmu));
924 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_NONE;
925 }
926 }
927 else
928 {
929 Log2Func(("stopping beep\n"));
930 switch (pThisCC->enmSpeakerEmu)
931 {
932 case PIT_SPEAKER_EMU_CONSOLE:
933 /* No error checking here. The Linux device driver
934 * implementation considers it an error (errno=22,
935 * EINVAL) to stop sound if it hasn't been started.
936 * Of course we could detect this by checking only
937 * for enabled->disabled transitions and ignoring
938 * disabled->disabled ones, but it's not worth the
939 * effort. */
940 ioctl(pThisCC->hHostSpeaker, KIOCSOUND, 0);
941 break;
942 case PIT_SPEAKER_EMU_EVDEV:
943 {
944 struct input_event e;
945 e.type = EV_SND;
946 e.code = SND_TONE;
947 e.value = 0;
948 int res = write(pThisCC->hHostSpeaker, &e, sizeof(struct input_event));
949 NOREF(res);
950 break;
951 }
952 case PIT_SPEAKER_EMU_TTY:
953 break;
954 case PIT_SPEAKER_EMU_NONE:
955 break;
956 default:
957 Log2Func(("unknown speaker emulation %d, disabling emulation\n", pThisCC->enmSpeakerEmu));
958 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_NONE;
959 }
960 }
961 }
962# endif /* RT_OS_LINUX */
963
964 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
965 }
966 Log(("pitR3IOPortSpeakerWrite: offPort=%#x cb=%x u32=%#x\n", offPort, cb, u32));
967 return VINF_SUCCESS;
968}
969
970
971/* -=-=-=-=-=- Saved state -=-=-=-=-=- */
972
973/**
974 * @callback_method_impl{FNSSMDEVLIVEEXEC}
975 */
976static DECLCALLBACK(int) pitR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
977{
978 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
979 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
980
981 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
982 RT_NOREF(uPass);
983 pHlp->pfnSSMPutIOPort(pSSM, pThis->IOPortBaseCfg);
984 pHlp->pfnSSMPutU8( pSSM, pThis->channels[0].irq);
985 pHlp->pfnSSMPutBool( pSSM, pThisCC->fSpeakerCfg);
986 return VINF_SSM_DONT_CALL_AGAIN;
987}
988
989
990/**
991 * @callback_method_impl{FNSSMDEVSAVEEXEC}
992 */
993static DECLCALLBACK(int) pitR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
994{
995 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
996 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
997 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
998 AssertRCReturn(rc, rc);
999
1000 /* The config. */
1001 pitR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
1002
1003 /* The state. */
1004 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1005 {
1006 PPITCHANNEL pChan = &pThis->channels[i];
1007 pHlp->pfnSSMPutU32(pSSM, pChan->count);
1008 pHlp->pfnSSMPutU16(pSSM, pChan->latched_count);
1009 pHlp->pfnSSMPutU8(pSSM, pChan->count_latched);
1010 pHlp->pfnSSMPutU8(pSSM, pChan->status_latched);
1011 pHlp->pfnSSMPutU8(pSSM, pChan->status);
1012 pHlp->pfnSSMPutU8(pSSM, pChan->read_state);
1013 pHlp->pfnSSMPutU8(pSSM, pChan->write_state);
1014 pHlp->pfnSSMPutU8(pSSM, pChan->write_latch);
1015 pHlp->pfnSSMPutU8(pSSM, pChan->rw_mode);
1016 pHlp->pfnSSMPutU8(pSSM, pChan->mode);
1017 pHlp->pfnSSMPutU8(pSSM, pChan->bcd);
1018 pHlp->pfnSSMPutU8(pSSM, pChan->gate);
1019 pHlp->pfnSSMPutU64(pSSM, pChan->count_load_time);
1020 pHlp->pfnSSMPutU64(pSSM, pChan->u64NextTS);
1021 pHlp->pfnSSMPutU64(pSSM, pChan->u64ReloadTS);
1022 pHlp->pfnSSMPutS64(pSSM, pChan->next_transition_time);
1023 if (pChan->hTimer != NIL_TMTIMERHANDLE)
1024 PDMDevHlpTimerSave(pDevIns, pChan->hTimer, pSSM);
1025 }
1026
1027 pHlp->pfnSSMPutS32(pSSM, pThis->speaker_data_on);
1028# ifdef FAKE_REFRESH_CLOCK
1029 pHlp->pfnSSMPutS32(pSSM, pThis->dummy_refresh_clock);
1030# else
1031 pHlp->pfnSSMPutS32(pSSM, 0);
1032# endif
1033
1034 pHlp->pfnSSMPutBool(pSSM, pThis->fDisabledByHpet);
1035
1036 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1037 return VINF_SUCCESS;
1038}
1039
1040
1041/**
1042 * @callback_method_impl{FNSSMDEVLOADEXEC}
1043 */
1044static DECLCALLBACK(int) pitR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1045{
1046 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1047 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
1048 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1049 int rc;
1050
1051 if ( uVersion != PIT_SAVED_STATE_VERSION
1052 && uVersion != PIT_SAVED_STATE_VERSION_VBOX_30
1053 && uVersion != PIT_SAVED_STATE_VERSION_VBOX_31)
1054 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1055
1056 /* The config. */
1057 if (uVersion > PIT_SAVED_STATE_VERSION_VBOX_30)
1058 {
1059 RTIOPORT IOPortBaseCfg;
1060 rc = pHlp->pfnSSMGetIOPort(pSSM, &IOPortBaseCfg); AssertRCReturn(rc, rc);
1061 if (IOPortBaseCfg != pThis->IOPortBaseCfg)
1062 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBaseCfg: saved=%RTiop config=%RTiop"),
1063 IOPortBaseCfg, pThis->IOPortBaseCfg);
1064
1065 uint8_t u8Irq;
1066 rc = pHlp->pfnSSMGetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
1067 if (u8Irq != pThis->channels[0].irq)
1068 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"),
1069 u8Irq, pThis->channels[0].irq);
1070
1071 bool fSpeakerCfg;
1072 rc = pHlp->pfnSSMGetBool(pSSM, &fSpeakerCfg); AssertRCReturn(rc, rc);
1073 if (fSpeakerCfg != pThisCC->fSpeakerCfg)
1074 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - fSpeakerCfg: saved=%RTbool config=%RTbool"),
1075 fSpeakerCfg, pThisCC->fSpeakerCfg);
1076 }
1077
1078 if (uPass != SSM_PASS_FINAL)
1079 return VINF_SUCCESS;
1080
1081 /* The state. */
1082 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1083 {
1084 PPITCHANNEL pChan = &pThis->channels[i];
1085 pHlp->pfnSSMGetU32(pSSM, &pChan->count);
1086 pHlp->pfnSSMGetU16(pSSM, &pChan->latched_count);
1087 pHlp->pfnSSMGetU8(pSSM, &pChan->count_latched);
1088 pHlp->pfnSSMGetU8(pSSM, &pChan->status_latched);
1089 pHlp->pfnSSMGetU8(pSSM, &pChan->status);
1090 pHlp->pfnSSMGetU8(pSSM, &pChan->read_state);
1091 pHlp->pfnSSMGetU8(pSSM, &pChan->write_state);
1092 pHlp->pfnSSMGetU8(pSSM, &pChan->write_latch);
1093 pHlp->pfnSSMGetU8(pSSM, &pChan->rw_mode);
1094 pHlp->pfnSSMGetU8(pSSM, &pChan->mode);
1095 pHlp->pfnSSMGetU8(pSSM, &pChan->bcd);
1096 pHlp->pfnSSMGetU8(pSSM, &pChan->gate);
1097 pHlp->pfnSSMGetU64(pSSM, &pChan->count_load_time);
1098 pHlp->pfnSSMGetU64(pSSM, &pChan->u64NextTS);
1099 pHlp->pfnSSMGetU64(pSSM, &pChan->u64ReloadTS);
1100 pHlp->pfnSSMGetS64(pSSM, &pChan->next_transition_time);
1101 if (pChan->hTimer != NIL_TMTIMERHANDLE)
1102 {
1103 rc = PDMDevHlpTimerLoad(pDevIns, pChan->hTimer, pSSM);
1104 AssertRCReturn(rc, rc);
1105 LogRel(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=%d) (restore)\n",
1106 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100, i));
1107 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
1108 AssertRCReturn(rc, rc);
1109 PDMDevHlpTimerSetFrequencyHint(pDevIns, pChan->hTimer, PIT_FREQ / pChan->count);
1110 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1111 }
1112 pThis->channels[i].cRelLogEntries = 0;
1113 }
1114
1115 pHlp->pfnSSMGetS32(pSSM, &pThis->speaker_data_on);
1116# ifdef FAKE_REFRESH_CLOCK
1117 pHlp->pfnSSMGetS32(pSSM, &pThis->dummy_refresh_clock);
1118# else
1119 int32_t u32Dummy;
1120 pHlp->pfnSSMGetS32(pSSM, &u32Dummy);
1121# endif
1122 if (uVersion > PIT_SAVED_STATE_VERSION_VBOX_31)
1123 rc = pHlp->pfnSSMGetBool(pSSM, &pThis->fDisabledByHpet);
1124
1125 return VINF_SUCCESS;
1126}
1127
1128
1129/* -=-=-=-=-=- Timer -=-=-=-=-=- */
1130
1131/**
1132 * @callback_method_impl{FNTMTIMERDEV, User argument points to the PIT channel state.}
1133 */
1134static DECLCALLBACK(void) pitR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1135{
1136 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1137 PPITCHANNEL pChan = (PPITCHANNEL)pvUser;
1138 STAM_PROFILE_ADV_START(&pThis->StatPITHandler, a);
1139 Assert(hTimer == pChan->hTimer);
1140
1141 Log(("pitR3Timer\n"));
1142 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1143 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, hTimer));
1144
1145 pitR3IrqTimerUpdate(pDevIns, pThis, pChan, pChan->next_transition_time, PDMDevHlpTimerGet(pDevIns, hTimer), true);
1146
1147 STAM_PROFILE_ADV_STOP(&pThis->StatPITHandler, a);
1148}
1149
1150
1151/* -=-=-=-=-=- Debug Info -=-=-=-=-=- */
1152
1153/**
1154 * @callback_method_impl{FNDBGFHANDLERDEV}
1155 */
1156static DECLCALLBACK(void) pitR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1157{
1158 RT_NOREF(pszArgs);
1159 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1160 unsigned i;
1161 for (i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1162 {
1163 const PITCHANNEL *pChan = &pThis->channels[i];
1164
1165 pHlp->pfnPrintf(pHlp,
1166 "PIT (i8254) channel %d status: irq=%#x\n"
1167 " count=%08x" " latched_count=%04x count_latched=%02x\n"
1168 " status=%02x status_latched=%02x read_state=%02x\n"
1169 " write_state=%02x write_latch=%02x rw_mode=%02x\n"
1170 " mode=%02x bcd=%02x gate=%02x\n"
1171 " count_load_time=%016RX64 next_transition_time=%016RX64\n"
1172 " u64ReloadTS=%016RX64 u64NextTS=%016RX64\n"
1173 ,
1174 i, pChan->irq,
1175 pChan->count, pChan->latched_count, pChan->count_latched,
1176 pChan->status, pChan->status_latched, pChan->read_state,
1177 pChan->write_state, pChan->write_latch, pChan->rw_mode,
1178 pChan->mode, pChan->bcd, pChan->gate,
1179 pChan->count_load_time, pChan->next_transition_time,
1180 pChan->u64ReloadTS, pChan->u64NextTS);
1181 }
1182# ifdef FAKE_REFRESH_CLOCK
1183 pHlp->pfnPrintf(pHlp, "speaker_data_on=%#x dummy_refresh_clock=%#x\n",
1184 pThis->speaker_data_on, pThis->dummy_refresh_clock);
1185# else
1186 pHlp->pfnPrintf(pHlp, "speaker_data_on=%#x\n", pThis->speaker_data_on);
1187# endif
1188 if (pThis->fDisabledByHpet)
1189 pHlp->pfnPrintf(pHlp, "Disabled by HPET\n");
1190}
1191
1192
1193/* -=-=-=-=-=- IHpetLegacyNotify -=-=-=-=-=- */
1194
1195/**
1196 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
1197 */
1198static DECLCALLBACK(void) pitR3NotifyHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
1199{
1200 PPITSTATER3 pThisCC = RT_FROM_MEMBER(pInterface, PITSTATER3, IHpetLegacyNotify);
1201 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1202 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1203 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
1204 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
1205
1206 pThis->fDisabledByHpet = fActivated;
1207
1208 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1209}
1210
1211
1212/* -=-=-=-=-=- PDMDEVINS::IBase -=-=-=-=-=- */
1213
1214/**
1215 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1216 */
1217static DECLCALLBACK(void *) pitR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
1218{
1219 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1220 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
1221 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1222 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThisCC->IHpetLegacyNotify);
1223 return NULL;
1224}
1225
1226
1227/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1228
1229/**
1230 * @interface_method_impl{PDMDEVREG,pfnReset}
1231 */
1232static DECLCALLBACK(void) pitR3Reset(PPDMDEVINS pDevIns)
1233{
1234 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1235 LogFlow(("pitR3Reset: \n"));
1236
1237 DEVPIT_R3_LOCK_BOTH(pDevIns, pThis);
1238
1239 pThis->fDisabledByHpet = false;
1240
1241 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1242 {
1243 PPITCHANNEL pChan = &pThis->channels[i];
1244
1245# if 1 /* Set everything back to virgin state. (might not be strictly correct) */
1246 pChan->latched_count = 0;
1247 pChan->count_latched = 0;
1248 pChan->status_latched = 0;
1249 pChan->status = 0;
1250 pChan->read_state = 0;
1251 pChan->write_state = 0;
1252 pChan->write_latch = 0;
1253 pChan->rw_mode = 0;
1254 pChan->bcd = 0;
1255# endif
1256 pChan->u64NextTS = UINT64_MAX;
1257 pChan->cRelLogEntries = 0;
1258 pChan->mode = 3;
1259 pChan->gate = (i != 2);
1260 pitR3LoadCount(pDevIns, pThis, pChan, 0);
1261 }
1262
1263 DEVPIT_UNLOCK_BOTH(pDevIns, pThis);
1264}
1265
1266# ifdef RT_OS_LINUX
1267
1268static int pitR3TryDeviceOpen(const char *pszPath, int flags)
1269{
1270 int fd = open(pszPath, flags);
1271 if (fd == -1)
1272 LogRel(("PIT: speaker: cannot open \"%s\", errno=%d\n", pszPath, errno));
1273 else
1274 LogRel(("PIT: speaker: opened \"%s\"\n", pszPath));
1275 return fd;
1276}
1277
1278
1279static int pitR3TryDeviceOpenSanitizeIoctl(const char *pszPath, int flags)
1280{
1281 int fd = open(pszPath, flags);
1282 if (fd == -1)
1283 LogRel(("PIT: speaker: cannot open \"%s\", errno=%d\n", pszPath, errno));
1284 else
1285 {
1286 int errno_eviocgsnd0 = 0;
1287 int errno_kiocsound = 0;
1288 if (ioctl(fd, EVIOCGSND(0)) == -1)
1289 {
1290 errno_eviocgsnd0 = errno;
1291 if (ioctl(fd, KIOCSOUND, 1) == -1)
1292 errno_kiocsound = errno;
1293 else
1294 ioctl(fd, KIOCSOUND, 0);
1295 }
1296 if (errno_eviocgsnd0 && errno_kiocsound)
1297 {
1298 LogRel(("PIT: speaker: cannot use \"%s\", ioctl failed errno=%d/errno=%d\n", pszPath, errno_eviocgsnd0, errno_kiocsound));
1299 close(fd);
1300 fd = -1;
1301 }
1302 else
1303 LogRel(("PIT: speaker: opened \"%s\"\n", pszPath));
1304 }
1305 return fd;
1306}
1307
1308# endif /* RT_OS_LINUX */
1309
1310/**
1311 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1312 */
1313static DECLCALLBACK(int) pitR3Destruct(PPDMDEVINS pDevIns)
1314{
1315 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1316
1317#ifdef RT_OS_LINUX
1318 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
1319
1320 if (pThisCC->enmSpeakerEmu != PIT_SPEAKER_EMU_NONE)
1321 {
1322 Assert(pThisCC->hHostSpeaker != -1);
1323 close(pThisCC->hHostSpeaker);
1324 pThisCC->hHostSpeaker = -1;
1325 }
1326#endif
1327
1328 return VINF_SUCCESS;
1329}
1330
1331
1332/**
1333 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1334 */
1335static DECLCALLBACK(int) pitR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1336{
1337 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1338 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1339 PPITSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPITSTATER3);
1340 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1341 int rc;
1342 uint8_t u8Irq;
1343 uint16_t u16Base;
1344 bool fSpeaker;
1345 unsigned i;
1346 Assert(iInstance == 0);
1347
1348 /*
1349 * Validate and read the configuration.
1350 */
1351 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|Base|SpeakerEnabled|PassthroughSpeaker|PassthroughSpeakerDevice", "");
1352
1353 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &u8Irq, 0);
1354 if (RT_FAILURE(rc))
1355 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1356
1357 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "Base", &u16Base, 0x40);
1358 if (RT_FAILURE(rc))
1359 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
1360
1361 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SpeakerEnabled", &fSpeaker, true);
1362 if (RT_FAILURE(rc))
1363 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"SpeakerEnabled\" as a bool failed"));
1364
1365 uint8_t uPassthroughSpeaker;
1366 char *pszPassthroughSpeakerDevice = NULL;
1367 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "PassthroughSpeaker", &uPassthroughSpeaker, 0);
1368 if (RT_FAILURE(rc))
1369 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: failed to read PassthroughSpeaker as uint8_t"));
1370 if (uPassthroughSpeaker)
1371 {
1372 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PassthroughSpeakerDevice", &pszPassthroughSpeakerDevice, NULL);
1373 if (RT_FAILURE(rc))
1374 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: failed to read PassthroughSpeakerDevice as string"));
1375 }
1376
1377 /*
1378 * Init the data.
1379 */
1380 pThis->IOPortBaseCfg = u16Base;
1381 pThis->channels[0].irq = u8Irq;
1382 for (i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1383 {
1384 pThis->channels[i].hTimer = NIL_TMTIMERHANDLE;
1385 pThis->channels[i].iChan = i;
1386 }
1387 pThisCC->fSpeakerCfg = fSpeaker;
1388 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_NONE;
1389 if (uPassthroughSpeaker)
1390 {
1391 /** @todo r=klaus move this to a (system-specific) driver */
1392#ifdef RT_OS_LINUX
1393 /** @todo r=andy Use defines / enums(?) for all those uPassthroughSpeaker below. */
1394 int fd = -1;
1395 if (uPassthroughSpeaker == 1 || uPassthroughSpeaker == 100)
1396 fd = pitR3TryDeviceOpenSanitizeIoctl("/dev/input/by-path/platform-pcspkr-event-spkr", O_WRONLY);
1397
1398 if (fd == -1 && (uPassthroughSpeaker == 2 || uPassthroughSpeaker == 100))
1399 fd = pitR3TryDeviceOpenSanitizeIoctl("/dev/tty", O_WRONLY);
1400
1401 if (fd == -1 && (uPassthroughSpeaker == 3 || uPassthroughSpeaker == 100))
1402 {
1403 fd = pitR3TryDeviceOpenSanitizeIoctl("/dev/tty0", O_WRONLY);
1404 if (fd == -1)
1405 fd = pitR3TryDeviceOpenSanitizeIoctl("/dev/vc/0", O_WRONLY);
1406 }
1407
1408 if (fd == -1 && (uPassthroughSpeaker == 9 || uPassthroughSpeaker == 100) && pszPassthroughSpeakerDevice)
1409 fd = pitR3TryDeviceOpenSanitizeIoctl(pszPassthroughSpeakerDevice, O_WRONLY);
1410
1411 if (fd != -1)
1412 {
1413 if (ioctl(fd, EVIOCGSND(0)) != -1)
1414 {
1415 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_EVDEV;
1416 LogRel(("PIT: speaker: emulation mode evdev\n"));
1417 }
1418 else
1419 {
1420 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_CONSOLE;
1421 LogRel(("PIT: speaker: emulation mode console\n"));
1422 }
1423 pThisCC->hHostSpeaker = fd;
1424 }
1425 else
1426 {
1427 if (uPassthroughSpeaker == 70 || uPassthroughSpeaker == 100)
1428 fd = pitR3TryDeviceOpen("/dev/tty", O_WRONLY);
1429 if (fd == -1 && (uPassthroughSpeaker == 79 || uPassthroughSpeaker == 100) && pszPassthroughSpeakerDevice)
1430 fd = pitR3TryDeviceOpen(pszPassthroughSpeakerDevice, O_WRONLY);
1431 if (fd != -1)
1432 {
1433 pThisCC->enmSpeakerEmu = PIT_SPEAKER_EMU_TTY;
1434 pThisCC->hHostSpeaker = fd;
1435 LogRel(("PIT: speaker: emulation mode tty\n"));
1436 }
1437 else
1438 LogRel(("PIT: speaker: no emulation possible\n"));
1439 }
1440#else /* !RT_OS_LINUX */
1441 LogRel(("PIT: speaker: emulation deactivated\n"));
1442#endif /* !RT_OS_LINUX */
1443 if (pszPassthroughSpeakerDevice)
1444 {
1445 PDMDevHlpMMHeapFree(pDevIns, pszPassthroughSpeakerDevice);
1446 pszPassthroughSpeakerDevice = NULL;
1447 }
1448 }
1449
1450 /*
1451 * Interfaces
1452 */
1453 /* IBase */
1454 pDevIns->IBase.pfnQueryInterface = pitR3QueryInterface;
1455 /* IHpetLegacyNotify */
1456 pThisCC->IHpetLegacyNotify.pfnModeChanged = pitR3NotifyHpetLegacyNotify_ModeChanged;
1457 pThisCC->pDevIns = pDevIns;
1458
1459 /*
1460 * We do our own locking. This must be done before creating timers.
1461 */
1462 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "pit#%u", iInstance);
1463 AssertRCReturn(rc, rc);
1464
1465 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1466 AssertRCReturn(rc, rc);
1467
1468 /*
1469 * Create the timer, make it take our critsect.
1470 */
1471 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, pitR3Timer, &pThis->channels[0],
1472 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "i8254 PIT", &pThis->channels[0].hTimer);
1473 AssertRCReturn(rc, rc);
1474 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->channels[0].hTimer, &pThis->CritSect);
1475 AssertRCReturn(rc, rc);
1476
1477 /*
1478 * Register I/O ports.
1479 */
1480 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, u16Base, 4 /*cPorts*/, pitIOPortWrite, pitIOPortRead,
1481 "i8254 Programmable Interval Timer", NULL /*paExtDescs*/, &pThis->hIoPorts);
1482 AssertRCReturn(rc, rc);
1483
1484 if (fSpeaker)
1485 {
1486 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x61, 1 /*cPorts*/, pitR3IOPortSpeakerWrite, pitIOPortSpeakerRead,
1487 "PC Speaker", NULL /*paExtDescs*/, &pThis->hIoPortSpeaker);
1488 AssertRCReturn(rc, rc);
1489 }
1490
1491 /*
1492 * Saved state.
1493 */
1494 rc = PDMDevHlpSSMRegister3(pDevIns, PIT_SAVED_STATE_VERSION, sizeof(*pThis), pitR3LiveExec, pitR3SaveExec, pitR3LoadExec);
1495 if (RT_FAILURE(rc))
1496 return rc;
1497
1498 /*
1499 * Initialize the device state.
1500 */
1501 pitR3Reset(pDevIns);
1502
1503 /*
1504 * Register statistics and debug info.
1505 */
1506 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITIrq, STAMTYPE_COUNTER, "/TM/PIT/Irq", STAMUNIT_OCCURENCES, "The number of times a timer interrupt was triggered.");
1507 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITHandler, STAMTYPE_PROFILE, "/TM/PIT/Handler", STAMUNIT_TICKS_PER_CALL, "Profiling timer callback handler.");
1508
1509 PDMDevHlpDBGFInfoRegister(pDevIns, "pit", "Display PIT (i8254) status. (no arguments)", pitR3Info);
1510
1511 return VINF_SUCCESS;
1512}
1513
1514#else /* !IN_RING3 */
1515
1516/**
1517 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1518 */
1519static DECLCALLBACK(int) picRZConstruct(PPDMDEVINS pDevIns)
1520{
1521 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1522 PPITSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPITSTATE);
1523
1524 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1525 AssertRCReturn(rc, rc);
1526
1527 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, pitIOPortWrite, pitIOPortRead, NULL /*pvUser*/);
1528 AssertRCReturn(rc, rc);
1529
1530 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortSpeaker, NULL /*pfnWrite*/, pitIOPortSpeakerRead, NULL /*pvUser*/);
1531 AssertRCReturn(rc, rc);
1532
1533 return VINF_SUCCESS;
1534}
1535
1536#endif /* !IN_RING3 */
1537
1538/**
1539 * The device registration structure.
1540 */
1541const PDMDEVREG g_DeviceI8254 =
1542{
1543 /* .u32Version = */ PDM_DEVREG_VERSION,
1544 /* .uReserved0 = */ 0,
1545 /* .szName = */ "i8254",
1546 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1547 /* .fClass = */ PDM_DEVREG_CLASS_PIT,
1548 /* .cMaxInstances = */ 1,
1549 /* .uSharedVersion = */ 42,
1550 /* .cbInstanceShared = */ sizeof(PITSTATE),
1551 /* .cbInstanceCC = */ CTX_EXPR(sizeof(PITSTATER3), 0, 0),
1552 /* .cbInstanceRC = */ 0,
1553 /* .cMaxPciDevices = */ 0,
1554 /* .cMaxMsixVectors = */ 0,
1555 /* .pszDescription = */ "Intel 8254 Programmable Interval Timer (PIT) And Dummy Speaker Device",
1556#if defined(IN_RING3)
1557 /* .pszRCMod = */ "VBoxDDRC.rc",
1558 /* .pszR0Mod = */ "VBoxDDR0.r0",
1559 /* .pfnConstruct = */ pitR3Construct,
1560 /* .pfnDestruct = */ pitR3Destruct,
1561 /* .pfnRelocate = */ NULL,
1562 /* .pfnMemSetup = */ NULL,
1563 /* .pfnPowerOn = */ NULL,
1564 /* .pfnReset = */ pitR3Reset,
1565 /* .pfnSuspend = */ NULL,
1566 /* .pfnResume = */ NULL,
1567 /* .pfnAttach = */ NULL,
1568 /* .pfnDetach = */ NULL,
1569 /* .pfnQueryInterface = */ NULL,
1570 /* .pfnInitComplete = */ NULL,
1571 /* .pfnPowerOff = */ NULL,
1572 /* .pfnSoftReset = */ NULL,
1573 /* .pfnReserved0 = */ NULL,
1574 /* .pfnReserved1 = */ NULL,
1575 /* .pfnReserved2 = */ NULL,
1576 /* .pfnReserved3 = */ NULL,
1577 /* .pfnReserved4 = */ NULL,
1578 /* .pfnReserved5 = */ NULL,
1579 /* .pfnReserved6 = */ NULL,
1580 /* .pfnReserved7 = */ NULL,
1581#elif defined(IN_RING0)
1582 /* .pfnEarlyConstruct = */ NULL,
1583 /* .pfnConstruct = */ picRZConstruct,
1584 /* .pfnDestruct = */ NULL,
1585 /* .pfnFinalDestruct = */ NULL,
1586 /* .pfnRequest = */ NULL,
1587 /* .pfnReserved0 = */ NULL,
1588 /* .pfnReserved1 = */ NULL,
1589 /* .pfnReserved2 = */ NULL,
1590 /* .pfnReserved3 = */ NULL,
1591 /* .pfnReserved4 = */ NULL,
1592 /* .pfnReserved5 = */ NULL,
1593 /* .pfnReserved6 = */ NULL,
1594 /* .pfnReserved7 = */ NULL,
1595#elif defined(IN_RC)
1596 /* .pfnConstruct = */ picRZConstruct,
1597 /* .pfnReserved0 = */ NULL,
1598 /* .pfnReserved1 = */ NULL,
1599 /* .pfnReserved2 = */ NULL,
1600 /* .pfnReserved3 = */ NULL,
1601 /* .pfnReserved4 = */ NULL,
1602 /* .pfnReserved5 = */ NULL,
1603 /* .pfnReserved6 = */ NULL,
1604 /* .pfnReserved7 = */ NULL,
1605#else
1606# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1607#endif
1608 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1609};
1610
1611#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use