VirtualBox

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

Last change on this file since 59497 was 59497, checked in by vboxsync, 9 years ago

Devices/PC/DevPit-i8254.cpp: Add code (currently linux host only) for passing through PC speaker beeps from the guest to the host, again with the intention to use PC speaker. No sound card conversion so far. Documentation needs to be written.

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