VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevIchAc97.cpp@ 90006

Last change on this file since 90006 was 90006, checked in by vboxsync, 3 years ago

DevIchAc97: nsRetrySetup fix. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 200.1 KB
Line 
1/* $Id: DevIchAc97.cpp 90006 2021-07-02 20:00:38Z vboxsync $ */
2/** @file
3 * DevIchAc97 - VBox ICH AC97 Audio Controller.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_AC97
23#include <VBox/log.h>
24#include <VBox/vmm/pdmdev.h>
25#include <VBox/vmm/pdmaudioifs.h>
26#include <VBox/vmm/pdmaudioinline.h>
27#include <VBox/AssertGuest.h>
28
29#include <iprt/assert.h>
30#ifdef IN_RING3
31# ifdef DEBUG
32# include <iprt/file.h>
33# endif
34# include <iprt/mem.h>
35# include <iprt/semaphore.h>
36# include <iprt/string.h>
37# include <iprt/uuid.h>
38# include <iprt/zero.h>
39#endif
40
41#include "VBoxDD.h"
42
43#include "AudioMixBuffer.h"
44#include "AudioMixer.h"
45#include "AudioHlp.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51/** Current saved state version. */
52#define AC97_SAVED_STATE_VERSION 1
53
54/** Default timer frequency (in Hz). */
55#define AC97_TIMER_HZ_DEFAULT 100
56
57/** Maximum number of streams we support. */
58#define AC97_MAX_STREAMS 3
59
60/** Maximum FIFO size (in bytes) - unused. */
61#define AC97_FIFO_MAX 256
62
63/** @name AC97_SR_XXX - Status Register Bits (AC97_NABM_OFF_SR, PI_SR, PO_SR, MC_SR).
64 * @{ */
65#define AC97_SR_FIFOE RT_BIT(4) /**< rwc, FIFO error. */
66#define AC97_SR_BCIS RT_BIT(3) /**< rwc, Buffer completion interrupt status. */
67#define AC97_SR_LVBCI RT_BIT(2) /**< rwc, Last valid buffer completion interrupt. */
68#define AC97_SR_CELV RT_BIT(1) /**< ro, Current equals last valid. */
69#define AC97_SR_DCH RT_BIT(0) /**< ro, Controller halted. */
70#define AC97_SR_VALID_MASK (RT_BIT(5) - 1)
71#define AC97_SR_WCLEAR_MASK (AC97_SR_FIFOE | AC97_SR_BCIS | AC97_SR_LVBCI)
72#define AC97_SR_RO_MASK (AC97_SR_DCH | AC97_SR_CELV)
73#define AC97_SR_INT_MASK (AC97_SR_FIFOE | AC97_SR_BCIS | AC97_SR_LVBCI)
74/** @} */
75
76/** @name AC97_CR_XXX - Control Register Bits (AC97_NABM_OFF_CR, PI_CR, PO_CR, MC_CR).
77 * @{ */
78#define AC97_CR_IOCE RT_BIT(4) /**< rw, Interrupt On Completion Enable. */
79#define AC97_CR_FEIE RT_BIT(3) /**< rw FIFO Error Interrupt Enable. */
80#define AC97_CR_LVBIE RT_BIT(2) /**< rw Last Valid Buffer Interrupt Enable. */
81#define AC97_CR_RR RT_BIT(1) /**< rw Reset Registers. */
82#define AC97_CR_RPBM RT_BIT(0) /**< rw Run/Pause Bus Master. */
83#define AC97_CR_VALID_MASK (RT_BIT(5) - 1)
84#define AC97_CR_DONT_CLEAR_MASK (AC97_CR_IOCE | AC97_CR_FEIE | AC97_CR_LVBIE)
85/** @} */
86
87/** @name AC97_GC_XXX - Global Control Bits (see AC97_GLOB_CNT).
88 * @{ */
89#define AC97_GC_WR 4 /**< rw Warm reset. */
90#define AC97_GC_CR 2 /**< rw Cold reset. */
91#define AC97_GC_VALID_MASK (RT_BIT(6) - 1)
92/** @} */
93
94/** @name AC97_GS_XXX - Global Status Bits (AC97_GLOB_STA).
95 * @{ */
96#define AC97_GS_MD3 RT_BIT(17) /**< rw */
97#define AC97_GS_AD3 RT_BIT(16) /**< rw */
98#define AC97_GS_RCS RT_BIT(15) /**< rwc */
99#define AC97_GS_B3S12 RT_BIT(14) /**< ro */
100#define AC97_GS_B2S12 RT_BIT(13) /**< ro */
101#define AC97_GS_B1S12 RT_BIT(12) /**< ro */
102#define AC97_GS_S1R1 RT_BIT(11) /**< rwc */
103#define AC97_GS_S0R1 RT_BIT(10) /**< rwc */
104#define AC97_GS_S1CR RT_BIT(9) /**< ro */
105#define AC97_GS_S0CR RT_BIT(8) /**< ro */
106#define AC97_GS_MINT RT_BIT(7) /**< ro */
107#define AC97_GS_POINT RT_BIT(6) /**< ro */
108#define AC97_GS_PIINT RT_BIT(5) /**< ro */
109#define AC97_GS_RSRVD (RT_BIT(4) | RT_BIT(3))
110#define AC97_GS_MOINT RT_BIT(2) /**< ro */
111#define AC97_GS_MIINT RT_BIT(1) /**< ro */
112#define AC97_GS_GSCI RT_BIT(0) /**< rwc */
113#define AC97_GS_RO_MASK ( AC97_GS_B3S12 \
114 | AC97_GS_B2S12 \
115 | AC97_GS_B1S12 \
116 | AC97_GS_S1CR \
117 | AC97_GS_S0CR \
118 | AC97_GS_MINT \
119 | AC97_GS_POINT \
120 | AC97_GS_PIINT \
121 | AC97_GS_RSRVD \
122 | AC97_GS_MOINT \
123 | AC97_GS_MIINT)
124#define AC97_GS_VALID_MASK (RT_BIT(18) - 1)
125#define AC97_GS_WCLEAR_MASK (AC97_GS_RCS | AC97_GS_S1R1 | AC97_GS_S0R1 | AC97_GS_GSCI)
126/** @} */
127
128/** @name Buffer Descriptor (BDLE, BDL).
129 * @{ */
130#define AC97_BD_IOC RT_BIT(31) /**< Interrupt on Completion. */
131#define AC97_BD_BUP RT_BIT(30) /**< Buffer Underrun Policy. */
132
133#define AC97_BD_LEN_MASK 0xFFFF /**< Mask for the BDL buffer length. */
134
135#define AC97_BD_LEN_CTL_MBZ UINT32_C(0x3fff0000) /**< Must-be-zero mask for AC97BDLE.ctl_len. */
136
137#define AC97_MAX_BDLE 32 /**< Maximum number of BDLEs. */
138/** @} */
139
140/** @name Extended Audio ID Register (EAID).
141 * @{ */
142#define AC97_EAID_VRA RT_BIT(0) /**< Variable Rate Audio. */
143#define AC97_EAID_VRM RT_BIT(3) /**< Variable Rate Mic Audio. */
144#define AC97_EAID_REV0 RT_BIT(10) /**< AC'97 revision compliance. */
145#define AC97_EAID_REV1 RT_BIT(11) /**< AC'97 revision compliance. */
146/** @} */
147
148/** @name Extended Audio Control and Status Register (EACS).
149 * @{ */
150#define AC97_EACS_VRA RT_BIT(0) /**< Variable Rate Audio (4.2.1.1). */
151#define AC97_EACS_VRM RT_BIT(3) /**< Variable Rate Mic Audio (4.2.1.1). */
152/** @} */
153
154/** @name Baseline Audio Register Set (BARS).
155 * @{ */
156#define AC97_BARS_VOL_MASK 0x1f /**< Volume mask for the Baseline Audio Register Set (5.7.2). */
157#define AC97_BARS_GAIN_MASK 0x0f /**< Gain mask for the Baseline Audio Register Set. */
158#define AC97_BARS_VOL_MUTE_SHIFT 15 /**< Mute bit shift for the Baseline Audio Register Set (5.7.2). */
159/** @} */
160
161/** AC'97 uses 1.5dB steps, we use 0.375dB steps: 1 AC'97 step equals 4 PDM steps. */
162#define AC97_DB_FACTOR 4
163
164/** @name Recording inputs?
165 * @{ */
166#define AC97_REC_MIC UINT8_C(0)
167#define AC97_REC_CD UINT8_C(1)
168#define AC97_REC_VIDEO UINT8_C(2)
169#define AC97_REC_AUX UINT8_C(3)
170#define AC97_REC_LINE_IN UINT8_C(4)
171#define AC97_REC_STEREO_MIX UINT8_C(5)
172#define AC97_REC_MONO_MIX UINT8_C(6)
173#define AC97_REC_PHONE UINT8_C(7)
174#define AC97_REC_MASK UINT8_C(7)
175/** @} */
176
177/** @name Mixer registers / NAM BAR registers?
178 * @{ */
179#define AC97_Reset 0x00
180#define AC97_Master_Volume_Mute 0x02
181#define AC97_Headphone_Volume_Mute 0x04 /**< Also known as AUX, see table 16, section 5.7. */
182#define AC97_Master_Volume_Mono_Mute 0x06
183#define AC97_Master_Tone_RL 0x08
184#define AC97_PC_BEEP_Volume_Mute 0x0a
185#define AC97_Phone_Volume_Mute 0x0c
186#define AC97_Mic_Volume_Mute 0x0e
187#define AC97_Line_In_Volume_Mute 0x10
188#define AC97_CD_Volume_Mute 0x12
189#define AC97_Video_Volume_Mute 0x14
190#define AC97_Aux_Volume_Mute 0x16
191#define AC97_PCM_Out_Volume_Mute 0x18
192#define AC97_Record_Select 0x1a
193#define AC97_Record_Gain_Mute 0x1c
194#define AC97_Record_Gain_Mic_Mute 0x1e
195#define AC97_General_Purpose 0x20
196#define AC97_3D_Control 0x22
197#define AC97_AC_97_RESERVED 0x24
198#define AC97_Powerdown_Ctrl_Stat 0x26
199#define AC97_Extended_Audio_ID 0x28
200#define AC97_Extended_Audio_Ctrl_Stat 0x2a
201#define AC97_PCM_Front_DAC_Rate 0x2c
202#define AC97_PCM_Surround_DAC_Rate 0x2e
203#define AC97_PCM_LFE_DAC_Rate 0x30
204#define AC97_PCM_LR_ADC_Rate 0x32
205#define AC97_MIC_ADC_Rate 0x34
206#define AC97_6Ch_Vol_C_LFE_Mute 0x36
207#define AC97_6Ch_Vol_L_R_Surround_Mute 0x38
208#define AC97_Vendor_Reserved 0x58
209#define AC97_AD_Misc 0x76
210#define AC97_Vendor_ID1 0x7c
211#define AC97_Vendor_ID2 0x7e
212/** @} */
213
214/** @name Analog Devices miscellaneous regiter bits used in AD1980.
215 * @{ */
216#define AC97_AD_MISC_LOSEL RT_BIT(5) /**< Surround (rear) goes to line out outputs. */
217#define AC97_AD_MISC_HPSEL RT_BIT(10) /**< PCM (front) goes to headphone outputs. */
218/** @} */
219
220
221/** @name BUP flag values.
222 * @{ */
223#define BUP_SET RT_BIT_32(0)
224#define BUP_LAST RT_BIT_32(1)
225/** @} */
226
227/** @name AC'97 source indices.
228 * @note The order of these indices is fixed (also applies for saved states) for
229 * the moment. So make sure you know what you're done when altering this!
230 * @{
231 */
232#define AC97SOUNDSOURCE_PI_INDEX 0 /**< PCM in */
233#define AC97SOUNDSOURCE_PO_INDEX 1 /**< PCM out */
234#define AC97SOUNDSOURCE_MC_INDEX 2 /**< Mic in */
235#define AC97SOUNDSOURCE_MAX 3 /**< Max sound sources. */
236/** @} */
237
238/** Port number (offset into NABM BAR) to stream index. */
239#define AC97_PORT2IDX(a_idx) ( ((a_idx) >> 4) & 3 )
240/** Port number (offset into NABM BAR) to stream index, but no masking. */
241#define AC97_PORT2IDX_UNMASKED(a_idx) ( ((a_idx) >> 4) )
242
243/** @name Stream offsets
244 * @{ */
245#define AC97_NABM_OFF_BDBAR 0x0 /**< Buffer Descriptor Base Address */
246#define AC97_NABM_OFF_CIV 0x4 /**< Current Index Value */
247#define AC97_NABM_OFF_LVI 0x5 /**< Last Valid Index */
248#define AC97_NABM_OFF_SR 0x6 /**< Status Register */
249#define AC97_NABM_OFF_PICB 0x8 /**< Position in Current Buffer */
250#define AC97_NABM_OFF_PIV 0xa /**< Prefetched Index Value */
251#define AC97_NABM_OFF_CR 0xb /**< Control Register */
252#define AC97_NABM_OFF_MASK 0xf /**< Mask for getting the the per-stream register. */
253/** @} */
254
255
256/** @name PCM in NABM BAR registers (0x00..0x0f).
257 * @{ */
258#define PI_BDBAR (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0x0) /**< PCM in: Buffer Descriptor Base Address */
259#define PI_CIV (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0x4) /**< PCM in: Current Index Value */
260#define PI_LVI (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0x5) /**< PCM in: Last Valid Index */
261#define PI_SR (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0x6) /**< PCM in: Status Register */
262#define PI_PICB (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0x8) /**< PCM in: Position in Current Buffer */
263#define PI_PIV (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0xa) /**< PCM in: Prefetched Index Value */
264#define PI_CR (AC97SOUNDSOURCE_PI_INDEX * 0x10 + 0xb) /**< PCM in: Control Register */
265/** @} */
266
267/** @name PCM out NABM BAR registers (0x10..0x1f).
268 * @{ */
269#define PO_BDBAR (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0x0) /**< PCM out: Buffer Descriptor Base Address */
270#define PO_CIV (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0x4) /**< PCM out: Current Index Value */
271#define PO_LVI (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0x5) /**< PCM out: Last Valid Index */
272#define PO_SR (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0x6) /**< PCM out: Status Register */
273#define PO_PICB (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0x8) /**< PCM out: Position in Current Buffer */
274#define PO_PIV (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0xa) /**< PCM out: Prefetched Index Value */
275#define PO_CR (AC97SOUNDSOURCE_PO_INDEX * 0x10 + 0xb) /**< PCM out: Control Register */
276/** @} */
277
278/** @name Mic in NABM BAR registers (0x20..0x2f).
279 * @{ */
280#define MC_BDBAR (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0x0) /**< PCM in: Buffer Descriptor Base Address */
281#define MC_CIV (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0x4) /**< PCM in: Current Index Value */
282#define MC_LVI (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0x5) /**< PCM in: Last Valid Index */
283#define MC_SR (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0x6) /**< PCM in: Status Register */
284#define MC_PICB (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0x8) /**< PCM in: Position in Current Buffer */
285#define MC_PIV (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0xa) /**< PCM in: Prefetched Index Value */
286#define MC_CR (AC97SOUNDSOURCE_MC_INDEX * 0x10 + 0xb) /**< PCM in: Control Register */
287/** @} */
288
289/** @name Misc NABM BAR registers.
290 * @{ */
291/** NABMBAR: Global Control Register.
292 * @note This is kind of in the MIC IN area. */
293#define AC97_GLOB_CNT 0x2c
294/** NABMBAR: Global Status. */
295#define AC97_GLOB_STA 0x30
296/** Codec Access Semaphore Register. */
297#define AC97_CAS 0x34
298/** @} */
299
300
301/*********************************************************************************************************************************
302* Structures and Typedefs *
303*********************************************************************************************************************************/
304/** The ICH AC'97 (Intel) controller (shared). */
305typedef struct AC97STATE *PAC97STATE;
306/** The ICH AC'97 (Intel) controller (ring-3). */
307typedef struct AC97STATER3 *PAC97STATER3;
308
309/**
310 * Buffer Descriptor List Entry (BDLE).
311 *
312 * (See section 3.2.1 in Intel document number 252751-001, or section 1.2.2.1 in
313 * Intel document number 302349-003.)
314 */
315typedef struct AC97BDLE
316{
317 /** Location of data buffer (bits 31:1). */
318 uint32_t addr;
319 /** Flags (bits 31 + 30) and length (bits 15:0) of data buffer (in audio samples).
320 * @todo split up into two 16-bit fields. */
321 uint32_t ctl_len;
322} AC97BDLE;
323AssertCompileSize(AC97BDLE, 8);
324/** Pointer to BDLE. */
325typedef AC97BDLE *PAC97BDLE;
326
327/**
328 * Bus master register set for an audio stream.
329 *
330 * (See section 16.2 in Intel document 301473-002, or section 2.2 in Intel
331 * document 302349-003.)
332 */
333typedef struct AC97BMREGS
334{
335 uint32_t bdbar; /**< rw 0, Buffer Descriptor List: BAR (Base Address Register). */
336 uint8_t civ; /**< ro 0, Current index value. */
337 uint8_t lvi; /**< rw 0, Last valid index. */
338 uint16_t sr; /**< rw 1, Status register. */
339 uint16_t picb; /**< ro 0, Position in current buffer (samples left to process). */
340 uint8_t piv; /**< ro 0, Prefetched index value. */
341 uint8_t cr; /**< rw 0, Control register. */
342 int32_t bd_valid; /**< Whether current BDLE is initialized or not. */
343 AC97BDLE bd; /**< Current Buffer Descriptor List Entry (BDLE). */
344} AC97BMREGS;
345AssertCompileSizeAlignment(AC97BMREGS, 8);
346/** Pointer to the BM registers of an audio stream. */
347typedef AC97BMREGS *PAC97BMREGS;
348
349/**
350 * The internal state of an AC'97 stream.
351 */
352typedef struct AC97STREAMSTATE
353{
354 /** Critical section for this stream. */
355 RTCRITSECT CritSect;
356 /** Circular buffer (FIFO) for holding DMA'ed data. */
357 R3PTRTYPE(PRTCIRCBUF) pCircBuf;
358#if HC_ARCH_BITS == 32
359 uint32_t Padding;
360#endif
361 /** Current circular buffer read offset (for tracing & logging). */
362 uint64_t offRead;
363 /** Current circular buffer write offset (for tracing & logging). */
364 uint64_t offWrite;
365 /** The stream's current configuration. */
366 PDMAUDIOSTREAMCFG Cfg; //+108
367 /** Timestamp of the last DMA data transfer. */
368 uint64_t tsTransferLast;
369 /** Timestamp of the next DMA data transfer.
370 * Next for determining the next scheduling window.
371 * Can be 0 if no next transfer is scheduled. */
372 uint64_t tsTransferNext;
373 /** The stream's timer Hz rate.
374 * This value can can be different from the device's default Hz rate,
375 * depending on the rate the stream expects (e.g. for 5.1 speaker setups).
376 * Set in R3StreamInit(). */
377 uint16_t uTimerHz;
378 /** Set if we've registered the asynchronous update job. */
379 bool fRegisteredAsyncUpdateJob;
380 /** Input streams only: Set when we switch from feeding the guest silence and
381 * commits to proving actual audio input bytes. */
382 bool fInputPreBuffered;
383 /** This is ZERO if stream setup succeeded, otherwise it's the RTTimeNanoTS() at
384 * which to retry setting it up. The latter applies only to same
385 * parameters. */
386 uint64_t nsRetrySetup;
387 /** Timestamp (in ns) of last stream update. */
388 uint64_t tsLastUpdateNs;
389
390 /** Size of the DMA buffer (pCircBuf) in bytes. */
391 uint32_t StatDmaBufSize;
392 /** Number of used bytes in the DMA buffer (pCircBuf). */
393 uint32_t StatDmaBufUsed;
394 /** Counter for all under/overflows problems. */
395 STAMCOUNTER StatDmaFlowProblems;
396 /** Counter for unresovled under/overflows problems. */
397 STAMCOUNTER StatDmaFlowErrors;
398 /** Number of bytes involved in unresolved flow errors. */
399 STAMCOUNTER StatDmaFlowErrorBytes;
400 STAMCOUNTER StatDmaSkippedDch;
401 STAMCOUNTER StatDmaSkippedPendingBcis;
402 STAMPROFILE StatStart;
403 STAMPROFILE StatReset;
404 STAMPROFILE StatStop;
405 STAMPROFILE StatReSetUpChanged;
406 STAMPROFILE StatReSetUpSame;
407 STAMCOUNTER StatWriteLviRecover;
408 STAMCOUNTER StatWriteCr;
409} AC97STREAMSTATE;
410AssertCompileSizeAlignment(AC97STREAMSTATE, 8);
411/** Pointer to internal state of an AC'97 stream. */
412typedef AC97STREAMSTATE *PAC97STREAMSTATE;
413
414/**
415 * Runtime configurable debug stuff for an AC'97 stream.
416 */
417typedef struct AC97STREAMDEBUGRT
418{
419 /** Whether debugging is enabled or not. */
420 bool fEnabled;
421 uint8_t Padding[7];
422 /** File for dumping stream reads / writes.
423 * For input streams, this dumps data being written to the device FIFO,
424 * whereas for output streams this dumps data being read from the device FIFO. */
425 R3PTRTYPE(PAUDIOHLPFILE) pFileStream;
426 /** File for dumping DMA reads / writes.
427 * For input streams, this dumps data being written to the device DMA,
428 * whereas for output streams this dumps data being read from the device DMA. */
429 R3PTRTYPE(PAUDIOHLPFILE) pFileDMA;
430} AC97STREAMDEBUGRT;
431
432/**
433 * Debug stuff for an AC'97 stream.
434 */
435typedef struct AC97STREAMDEBUG
436{
437 /** Runtime debug stuff. */
438 AC97STREAMDEBUGRT Runtime;
439} AC97STREAMDEBUG;
440
441/**
442 * The shared AC'97 stream state.
443 */
444typedef struct AC97STREAM
445{
446 /** Bus master registers of this stream. */
447 AC97BMREGS Regs;
448 /** Stream number (SDn). */
449 uint8_t u8SD;
450 uint8_t abPadding0[7];
451
452 /** The timer for pumping data thru the attached LUN drivers. */
453 TMTIMERHANDLE hTimer;
454 /** When the timer was armed (timer clock). */
455 uint64_t uArmedTs;
456 /** (Virtual) clock ticks per transfer. */
457 uint64_t cDmaPeriodTicks;
458 /** Transfer chunk size (in bytes) of a transfer period. */
459 uint32_t cbDmaPeriod;
460 /** DMA period counter (for logging). */
461 uint32_t uDmaPeriod;
462
463 STAMCOUNTER StatWriteLvi;
464 STAMCOUNTER StatWriteSr1;
465 STAMCOUNTER StatWriteSr2;
466 STAMCOUNTER StatWriteBdBar;
467} AC97STREAM;
468AssertCompileSizeAlignment(AC97STREAM, 8);
469/** Pointer to a shared AC'97 stream state. */
470typedef AC97STREAM *PAC97STREAM;
471
472
473/**
474 * The ring-3 AC'97 stream state.
475 */
476typedef struct AC97STREAMR3
477{
478 /** Stream number (SDn). */
479 uint8_t u8SD;
480 uint8_t abPadding0[7];
481 /** Internal state of this stream. */
482 AC97STREAMSTATE State;
483 /** Debug stuff. */
484 AC97STREAMDEBUG Dbg;
485} AC97STREAMR3;
486AssertCompileSizeAlignment(AC97STREAMR3, 8);
487/** Pointer to an AC'97 stream state for ring-3. */
488typedef AC97STREAMR3 *PAC97STREAMR3;
489
490
491/**
492 * A driver stream (host backend).
493 *
494 * Each driver has its own instances of audio mixer streams, which then
495 * can go into the same (or even different) audio mixer sinks.
496 */
497typedef struct AC97DRIVERSTREAM
498{
499 /** Associated mixer stream handle. */
500 R3PTRTYPE(PAUDMIXSTREAM) pMixStrm;
501} AC97DRIVERSTREAM;
502/** Pointer to a driver stream. */
503typedef AC97DRIVERSTREAM *PAC97DRIVERSTREAM;
504
505/**
506 * A host backend driver (LUN).
507 */
508typedef struct AC97DRIVER
509{
510 /** Node for storing this driver in our device driver list of AC97STATE. */
511 RTLISTNODER3 Node;
512 /** LUN # to which this driver has been assigned. */
513 uint8_t uLUN;
514 /** Whether this driver is in an attached state or not. */
515 bool fAttached;
516 uint8_t abPadding[6];
517 /** Pointer to the description string passed to PDMDevHlpDriverAttach(). */
518 R3PTRTYPE(char *) pszDesc;
519 /** Pointer to attached driver base interface. */
520 R3PTRTYPE(PPDMIBASE) pDrvBase;
521 /** Audio connector interface to the underlying host backend. */
522 R3PTRTYPE(PPDMIAUDIOCONNECTOR) pConnector;
523 /** Driver stream for line input. */
524 AC97DRIVERSTREAM LineIn;
525 /** Driver stream for mic input. */
526 AC97DRIVERSTREAM MicIn;
527 /** Driver stream for output. */
528 AC97DRIVERSTREAM Out;
529} AC97DRIVER;
530/** Pointer to a host backend driver (LUN). */
531typedef AC97DRIVER *PAC97DRIVER;
532
533/**
534 * Debug settings.
535 */
536typedef struct AC97STATEDEBUG
537{
538 /** Whether debugging is enabled or not. */
539 bool fEnabled;
540 bool afAlignment[7];
541 /** Path where to dump the debug output to.
542 * Can be NULL, in which the system's temporary directory will be used then. */
543 R3PTRTYPE(char *) pszOutPath;
544} AC97STATEDEBUG;
545
546
547/* Codec models. */
548typedef enum AC97CODEC
549{
550 AC97CODEC_INVALID = 0, /**< Customary illegal zero value. */
551 AC97CODEC_STAC9700, /**< SigmaTel STAC9700 */
552 AC97CODEC_AD1980, /**< Analog Devices AD1980 */
553 AC97CODEC_AD1981B, /**< Analog Devices AD1981B */
554 AC97CODEC_32BIT_HACK = 0x7fffffff
555} AC97CODEC;
556
557
558/**
559 * The shared AC'97 device state.
560 */
561typedef struct AC97STATE
562{
563 /** Critical section protecting the AC'97 state. */
564 PDMCRITSECT CritSect;
565 /** Global Control (Bus Master Control Register). */
566 uint32_t glob_cnt;
567 /** Global Status (Bus Master Control Register). */
568 uint32_t glob_sta;
569 /** Codec Access Semaphore Register (Bus Master Control Register). */
570 uint32_t cas;
571 uint32_t last_samp;
572 uint8_t mixer_data[256];
573 /** Array of AC'97 streams (parallel to AC97STATER3::aStreams). */
574 AC97STREAM aStreams[AC97_MAX_STREAMS];
575 /** The device timer Hz rate. Defaults to AC97_TIMER_HZ_DEFAULT_DEFAULT. */
576 uint16_t uTimerHz;
577 /** Config: Internal input DMA buffer size override, specified in milliseconds.
578 * Zero means default size according to buffer and stream config.
579 * @sa BufSizeInMs config value. */
580 uint16_t cMsCircBufIn;
581 /** Config: Internal output DMA buffer size override, specified in milliseconds.
582 * Zero means default size according to buffer and stream config.
583 * @sa BufSizeOutMs config value. */
584 uint16_t cMsCircBufOut;
585 uint16_t au16Padding1[1];
586 uint8_t silence[128];
587 uint32_t bup_flag;
588 /** Codec model. */
589 AC97CODEC enmCodecModel;
590
591 /** PCI region \#0: NAM I/O ports. */
592 IOMIOPORTHANDLE hIoPortsNam;
593 /** PCI region \#0: NANM I/O ports. */
594 IOMIOPORTHANDLE hIoPortsNabm;
595
596 STAMCOUNTER StatUnimplementedNabmReads;
597 STAMCOUNTER StatUnimplementedNabmWrites;
598 STAMCOUNTER StatUnimplementedNamReads;
599 STAMCOUNTER StatUnimplementedNamWrites;
600#ifdef VBOX_WITH_STATISTICS
601 STAMPROFILE StatTimer;
602#endif
603} AC97STATE;
604AssertCompileMemberAlignment(AC97STATE, aStreams, 8);
605AssertCompileMemberAlignment(AC97STATE, StatUnimplementedNabmReads, 8);
606
607
608/**
609 * The ring-3 AC'97 device state.
610 */
611typedef struct AC97STATER3
612{
613 /** Array of AC'97 streams (parallel to AC97STATE:aStreams). */
614 AC97STREAMR3 aStreams[AC97_MAX_STREAMS];
615 /** R3 pointer to the device instance. */
616 PPDMDEVINSR3 pDevIns;
617 /** List of associated LUN drivers (AC97DRIVER). */
618 RTLISTANCHORR3 lstDrv;
619 /** The device's software mixer. */
620 R3PTRTYPE(PAUDIOMIXER) pMixer;
621 /** Audio sink for PCM output. */
622 R3PTRTYPE(PAUDMIXSINK) pSinkOut;
623 /** Audio sink for line input. */
624 R3PTRTYPE(PAUDMIXSINK) pSinkLineIn;
625 /** Audio sink for microphone input. */
626 R3PTRTYPE(PAUDMIXSINK) pSinkMicIn;
627 /** The base interface for LUN\#0. */
628 PDMIBASE IBase;
629 /** Debug settings. */
630 AC97STATEDEBUG Dbg;
631} AC97STATER3;
632AssertCompileMemberAlignment(AC97STATER3, aStreams, 8);
633/** Pointer to the ring-3 AC'97 device state. */
634typedef AC97STATER3 *PAC97STATER3;
635
636
637/**
638 * Acquires the AC'97 lock.
639 */
640#define DEVAC97_LOCK(a_pDevIns, a_pThis) \
641 do { \
642 int rcLock = PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, VERR_IGNORED); \
643 AssertRC(rcLock); \
644 } while (0)
645
646/**
647 * Acquires the AC'97 lock or returns.
648 */
649# define DEVAC97_LOCK_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
650 do { \
651 int rcLock = PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, a_rcBusy); \
652 if (rcLock == VINF_SUCCESS) \
653 break; \
654 AssertRC(rcLock); \
655 return rcLock; \
656 } while (0)
657
658/**
659 * Releases the AC'97 lock.
660 */
661#define DEVAC97_UNLOCK(a_pDevIns, a_pThis) \
662 do { PDMDevHlpCritSectLeave((a_pDevIns), &(a_pThis)->CritSect); } while (0)
663
664
665#ifndef VBOX_DEVICE_STRUCT_TESTCASE
666
667
668/*********************************************************************************************************************************
669* Internal Functions *
670*********************************************************************************************************************************/
671static void ichac97StreamUpdateSR(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream, uint32_t new_sr);
672static uint16_t ichac97MixerGet(PAC97STATE pThis, uint32_t uMixerIdx);
673#ifdef IN_RING3
674DECLINLINE(void) ichac97R3StreamLock(PAC97STREAMR3 pStreamCC);
675DECLINLINE(void) ichac97R3StreamUnlock(PAC97STREAMR3 pStreamCC);
676static void ichac97R3DbgPrintBdl(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream,
677 PCDBGFINFOHLP pHlp, const char *pszPrefix);
678static DECLCALLBACK(void) ichac97R3Reset(PPDMDEVINS pDevIns);
679#endif
680
681
682/*********************************************************************************************************************************
683* Global Variables *
684*********************************************************************************************************************************/
685#ifdef IN_RING3
686/** NABM I/O port descriptions. */
687static const IOMIOPORTDESC g_aNabmPorts[] =
688{
689 { "PCM IN - BDBAR", "PCM IN - BDBAR", NULL, NULL },
690 { "", NULL, NULL, NULL },
691 { "", NULL, NULL, NULL },
692 { "", NULL, NULL, NULL },
693 { "PCM IN - CIV", "PCM IN - CIV", NULL, NULL },
694 { "PCM IN - LVI", "PCM IN - LIV", NULL, NULL },
695 { "PCM IN - SR", "PCM IN - SR", NULL, NULL },
696 { "", NULL, NULL, NULL },
697 { "PCM IN - PICB", "PCM IN - PICB", NULL, NULL },
698 { "", NULL, NULL, NULL },
699 { "PCM IN - PIV", "PCM IN - PIV", NULL, NULL },
700 { "PCM IN - CR", "PCM IN - CR", NULL, NULL },
701 { "", NULL, NULL, NULL },
702 { "", NULL, NULL, NULL },
703 { "", NULL, NULL, NULL },
704 { "", NULL, NULL, NULL },
705
706 { "PCM OUT - BDBAR", "PCM OUT - BDBAR", NULL, NULL },
707 { "", NULL, NULL, NULL },
708 { "", NULL, NULL, NULL },
709 { "", NULL, NULL, NULL },
710 { "PCM OUT - CIV", "PCM OUT - CIV", NULL, NULL },
711 { "PCM OUT - LVI", "PCM OUT - LIV", NULL, NULL },
712 { "PCM OUT - SR", "PCM OUT - SR", NULL, NULL },
713 { "", NULL, NULL, NULL },
714 { "PCM OUT - PICB", "PCM OUT - PICB", NULL, NULL },
715 { "", NULL, NULL, NULL },
716 { "PCM OUT - PIV", "PCM OUT - PIV", NULL, NULL },
717 { "PCM OUT - CR", "PCM IN - CR", NULL, NULL },
718 { "", NULL, NULL, NULL },
719 { "", NULL, NULL, NULL },
720 { "", NULL, NULL, NULL },
721 { "", NULL, NULL, NULL },
722
723 { "MIC IN - BDBAR", "MIC IN - BDBAR", NULL, NULL },
724 { "", NULL, NULL, NULL },
725 { "", NULL, NULL, NULL },
726 { "", NULL, NULL, NULL },
727 { "MIC IN - CIV", "MIC IN - CIV", NULL, NULL },
728 { "MIC IN - LVI", "MIC IN - LIV", NULL, NULL },
729 { "MIC IN - SR", "MIC IN - SR", NULL, NULL },
730 { "", NULL, NULL, NULL },
731 { "MIC IN - PICB", "MIC IN - PICB", NULL, NULL },
732 { "", NULL, NULL, NULL },
733 { "MIC IN - PIV", "MIC IN - PIV", NULL, NULL },
734 { "MIC IN - CR", "MIC IN - CR", NULL, NULL },
735 { "GLOB CNT", "GLOB CNT", NULL, NULL },
736 { "", NULL, NULL, NULL },
737 { "", NULL, NULL, NULL },
738 { "", NULL, NULL, NULL },
739
740 { "GLOB STA", "GLOB STA", NULL, NULL },
741 { "", NULL, NULL, NULL },
742 { "", NULL, NULL, NULL },
743 { "", NULL, NULL, NULL },
744 { "CAS", "CAS", NULL, NULL },
745 { NULL, NULL, NULL, NULL },
746};
747
748/** @name Source indices
749 * @{ */
750# define AC97SOUNDSOURCE_PI_INDEX 0 /**< PCM in */
751# define AC97SOUNDSOURCE_PO_INDEX 1 /**< PCM out */
752# define AC97SOUNDSOURCE_MC_INDEX 2 /**< Mic in */
753# define AC97SOUNDSOURCE_MAX 3 /**< Max sound sources. */
754/** @} */
755
756/** Port number (offset into NABM BAR) to stream index. */
757# define AC97_PORT2IDX(a_idx) ( ((a_idx) >> 4) & 3 )
758/** Port number (offset into NABM BAR) to stream index, but no masking. */
759# define AC97_PORT2IDX_UNMASKED(a_idx) ( ((a_idx) >> 4) )
760
761/** @name Stream offsets
762 * @{ */
763# define AC97_NABM_OFF_BDBAR 0x0 /**< Buffer Descriptor Base Address */
764# define AC97_NABM_OFF_CIV 0x4 /**< Current Index Value */
765# define AC97_NABM_OFF_LVI 0x5 /**< Last Valid Index */
766# define AC97_NABM_OFF_SR 0x6 /**< Status Register */
767# define AC97_NABM_OFF_PICB 0x8 /**< Position in Current Buffer */
768# define AC97_NABM_OFF_PIV 0xa /**< Prefetched Index Value */
769# define AC97_NABM_OFF_CR 0xb /**< Control Register */
770# define AC97_NABM_OFF_MASK 0xf /**< Mask for getting the the per-stream register. */
771/** @} */
772
773#endif /* IN_RING3 */
774
775
776
777static void ichac97WarmReset(PAC97STATE pThis)
778{
779 NOREF(pThis);
780}
781
782static void ichac97ColdReset(PAC97STATE pThis)
783{
784 NOREF(pThis);
785}
786
787
788#ifdef IN_RING3
789
790/**
791 * Returns the audio direction of a specified stream descriptor.
792 *
793 * @return Audio direction.
794 */
795DECLINLINE(PDMAUDIODIR) ichac97R3GetDirFromSD(uint8_t uSD)
796{
797 switch (uSD)
798 {
799 case AC97SOUNDSOURCE_PI_INDEX: return PDMAUDIODIR_IN;
800 case AC97SOUNDSOURCE_PO_INDEX: return PDMAUDIODIR_OUT;
801 case AC97SOUNDSOURCE_MC_INDEX: return PDMAUDIODIR_IN;
802 }
803
804 AssertFailed();
805 return PDMAUDIODIR_UNKNOWN;
806}
807
808
809/**
810 * Retrieves the audio mixer sink of a corresponding AC'97 stream index.
811 *
812 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
813 * @param pThisCC The ring-3 AC'97 state.
814 * @param uIndex Stream index to get audio mixer sink for.
815 */
816DECLINLINE(PAUDMIXSINK) ichac97R3IndexToSink(PAC97STATER3 pThisCC, uint8_t uIndex)
817{
818 switch (uIndex)
819 {
820 case AC97SOUNDSOURCE_PI_INDEX: return pThisCC->pSinkLineIn;
821 case AC97SOUNDSOURCE_PO_INDEX: return pThisCC->pSinkOut;
822 case AC97SOUNDSOURCE_MC_INDEX: return pThisCC->pSinkMicIn;
823 default:
824 AssertMsgFailedReturn(("Wrong index %RU8\n", uIndex), NULL);
825 }
826}
827
828
829/*********************************************************************************************************************************
830* Stream DMA *
831*********************************************************************************************************************************/
832
833/**
834 * Retrieves the available size of (buffered) audio data (in bytes) of a given AC'97 stream.
835 *
836 * @returns Available data (in bytes).
837 * @param pStreamCC The AC'97 stream to retrieve size for (ring-3).
838 */
839DECLINLINE(uint32_t) ichac97R3StreamGetUsed(PAC97STREAMR3 pStreamCC)
840{
841 PRTCIRCBUF const pCircBuf = pStreamCC->State.pCircBuf;
842 if (pCircBuf)
843 return (uint32_t)RTCircBufUsed(pCircBuf);
844 return 0;
845}
846
847
848/**
849 * Retrieves the free size of audio data (in bytes) of a given AC'97 stream.
850 *
851 * @returns Free data (in bytes).
852 * @param pStreamCC AC'97 stream to retrieve size for (ring-3).
853 */
854DECLINLINE(uint32_t) ichac97R3StreamGetFree(PAC97STREAMR3 pStreamCC)
855{
856 PRTCIRCBUF const pCircBuf = pStreamCC->State.pCircBuf;
857 if (pCircBuf)
858 return (uint32_t)RTCircBufFree(pCircBuf);
859 return 0;
860}
861
862
863# if 0 /* Unused */
864static void ichac97R3WriteBUP(PAC97STATE pThis, uint32_t cbElapsed)
865{
866 LogFlowFunc(("cbElapsed=%RU32\n", cbElapsed));
867
868 if (!(pThis->bup_flag & BUP_SET))
869 {
870 if (pThis->bup_flag & BUP_LAST)
871 {
872 unsigned int i;
873 uint32_t *p = (uint32_t*)pThis->silence;
874 for (i = 0; i < sizeof(pThis->silence) / 4; i++) /** @todo r=andy Assumes 16-bit samples, stereo. */
875 *p++ = pThis->last_samp;
876 }
877 else
878 RT_ZERO(pThis->silence);
879
880 pThis->bup_flag |= BUP_SET;
881 }
882
883 while (cbElapsed)
884 {
885 uint32_t cbToWrite = RT_MIN(cbElapsed, (uint32_t)sizeof(pThis->silence));
886 uint32_t cbWrittenToStream;
887
888 int rc2 = AudioMixerSinkWrite(pThisCC->pSinkOut, AUDMIXOP_COPY,
889 pThis->silence, cbToWrite, &cbWrittenToStream);
890 if (RT_SUCCESS(rc2))
891 {
892 if (cbWrittenToStream < cbToWrite) /* Lagging behind? */
893 LogFlowFunc(("Warning: Only written %RU32 / %RU32 bytes, expect lags\n", cbWrittenToStream, cbToWrite));
894 }
895
896 /* Always report all data as being written;
897 * backends who were not able to catch up have to deal with it themselves. */
898 Assert(cbElapsed >= cbToWrite);
899 cbElapsed -= cbToWrite;
900 }
901}
902# endif /* Unused */
903
904
905/**
906 * Fetches the next buffer descriptor (BDLE) updating the stream registers.
907 *
908 * This will skip zero length descriptors.
909 *
910 * @returns Zero, or AC97_SR_BCIS if skipped zero length buffer with IOC set.
911 * @param pDevIns The device instance.
912 * @param pStream AC'97 stream to fetch BDLE for.
913 * @param pStreamCC The AC'97 stream, ring-3 state.
914 *
915 * @remarks Updates CIV, PIV, BD and PICB.
916 *
917 * @note Both PIV and CIV will be zero after a stream reset, so the first
918 * time we advance the buffer position afterwards, CIV will remain zero
919 * and PIV becomes 1. Thus we will start processing from BDLE00 and
920 * not BDLE01 as CIV=0 may lead you to think.
921 */
922static uint32_t ichac97R3StreamFetchNextBdle(PPDMDEVINS pDevIns, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
923{
924 RT_NOREF(pStreamCC);
925 uint32_t fSrBcis = 0;
926
927 /*
928 * Loop for skipping zero length entries.
929 */
930 for (;;)
931 {
932 /* Advance the buffer. */
933 pStream->Regs.civ = pStream->Regs.piv % AC97_MAX_BDLE /* (paranoia) */;
934 pStream->Regs.piv = (pStream->Regs.piv + 1) % AC97_MAX_BDLE;
935
936 /* Load it. */
937 AC97BDLE Bdle = { 0, 0 };
938 PDMDevHlpPCIPhysRead(pDevIns, pStream->Regs.bdbar + pStream->Regs.civ * sizeof(AC97BDLE), &Bdle, sizeof(AC97BDLE));
939 pStream->Regs.bd_valid = 1;
940 pStream->Regs.bd.addr = RT_H2LE_U32(Bdle.addr) & ~3;
941 pStream->Regs.bd.ctl_len = RT_H2LE_U32(Bdle.ctl_len);
942 pStream->Regs.picb = pStream->Regs.bd.ctl_len & AC97_BD_LEN_MASK;
943
944 LogFlowFunc(("BDLE%02u: %#RX32 L %#x / LB %#x, ctl=%#06x%s%s\n",
945 pStream->Regs.civ, pStream->Regs.bd.addr, pStream->Regs.bd.ctl_len & AC97_BD_LEN_MASK,
946 (pStream->Regs.bd.ctl_len & AC97_BD_LEN_MASK) * PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props),
947 pStream->Regs.bd.ctl_len >> 16,
948 pStream->Regs.bd.ctl_len & AC97_BD_IOC ? " ioc" : "",
949 pStream->Regs.bd.ctl_len & AC97_BD_BUP ? " bup" : ""));
950
951 /* Complain about any reserved bits set in CTL and ADDR: */
952 ASSERT_GUEST_MSG(!(pStream->Regs.bd.ctl_len & AC97_BD_LEN_CTL_MBZ),
953 ("Reserved bits set: %#RX32\n", pStream->Regs.bd.ctl_len));
954 ASSERT_GUEST_MSG(!(RT_H2LE_U32(Bdle.addr) & 3),
955 ("Reserved addr bits set: %#RX32\n", RT_H2LE_U32(Bdle.addr) ));
956
957 /* If the length is non-zero or if we've reached LVI, we're done regardless
958 of what's been loaded. Otherwise, we skip zero length buffers. */
959 if (pStream->Regs.picb)
960 break;
961 if (pStream->Regs.civ == (pStream->Regs.lvi % AC97_MAX_BDLE /* (paranoia) */))
962 {
963 LogFunc(("BDLE%02u is zero length! Can't skip (CIV=LVI). %#RX32 %#RX32\n", pStream->Regs.civ, Bdle.addr, Bdle.ctl_len));
964 break;
965 }
966 LogFunc(("BDLE%02u is zero length! Skipping. %#RX32 %#RX32\n", pStream->Regs.civ, Bdle.addr, Bdle.ctl_len));
967
968 /* If the buffer has IOC set, make sure it's triggered by the caller. */
969 if (pStream->Regs.bd.ctl_len & AC97_BD_IOC)
970 fSrBcis |= AC97_SR_BCIS;
971 }
972
973 /* 1.2.4.2 PCM Buffer Restrictions (in 302349-003) - #1 */
974 ASSERT_GUEST_MSG(!(pStream->Regs.picb & 1),
975 ("Odd lengths buffers are not allowed: %#x (%d) samples\n", pStream->Regs.picb, pStream->Regs.picb));
976
977 /* 1.2.4.2 PCM Buffer Restrictions (in 302349-003) - #2 */
978 ASSERT_GUEST_MSG(pStream->Regs.picb > 0, ("Zero length buffers not allowed to terminate list (LVI=%u CIV=%u)\n",
979 pStream->Regs.lvi, pStream->Regs.civ));
980
981 return fSrBcis;
982}
983
984
985/**
986 * Transfers data of an AC'97 stream according to its usage (input / output).
987 *
988 * For an SDO (output) stream this means reading DMA data from the device to
989 * the AC'97 stream's internal FIFO buffer.
990 *
991 * For an SDI (input) stream this is reading audio data from the AC'97 stream's
992 * internal FIFO buffer and writing it as DMA data to the device.
993 *
994 * @returns VBox status code.
995 * @param pDevIns The device instance.
996 * @param pThis The shared AC'97 state.
997 * @param pStream The AC'97 stream to update (shared).
998 * @param pStreamCC The AC'97 stream to update (ring-3).
999 * @param cbToProcess The max amount of data to process (i.e.
1000 * put into / remove from the circular buffer).
1001 * Unless something is going seriously wrong, this
1002 * will always be transfer size for the current
1003 * period. The current period will never be
1004 * larger than what can be stored in the current
1005 * buffer (i.e. what PICB indicates).
1006 * @param fWriteSilence Whether to write silence if this is an input
1007 * stream (done while waiting for backend to get
1008 * going).
1009 * @param fInput Set if input, clear if output.
1010 */
1011static int ichac97R3StreamTransfer(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream,
1012 PAC97STREAMR3 pStreamCC, uint32_t cbToProcess, bool fWriteSilence, bool fInput)
1013{
1014 if (RT_LIKELY(cbToProcess > 0))
1015 Assert(PDMAudioPropsIsSizeAligned(&pStreamCC->State.Cfg.Props, cbToProcess));
1016 else
1017 return VINF_SUCCESS;
1018
1019 ichac97R3StreamLock(pStreamCC);
1020
1021 /*
1022 * Check that the controller is not halted (DCH) and that the buffer
1023 * completion interrupt isn't pending.
1024 */
1025 /** @todo r=bird: Why do we not just barge ahead even when BCIS is set? Can't
1026 * find anything in spec indicating that we shouldn't. Linux shouldn't
1027 * care if be bundle IOCs, as it checks how many steps we've taken using
1028 * CIV. The Windows AC'97 sample driver doesn't care at all, since it
1029 * just sets LIV to CIV-1 (thought that's probably not what the real
1030 * windows driver does)...
1031 *
1032 * This is not going to sound good if it happens often enough, because
1033 * each time we'll lose one DMA period (exact length depends on the
1034 * buffer here).
1035 *
1036 * If we're going to keep this hack, there should be a
1037 * PDMDevHlpTimerSetRelative call arm-ing the DMA timer to fire shortly
1038 * after BCIS is cleared. Otherwise, we might lag behind even more
1039 * before we get stuff going again.
1040 *
1041 * I just wish there was some clear reasoning in the source code for
1042 * weird shit like this. This is just random voodoo. Sigh^3! */
1043 if (!(pStream->Regs.sr & (AC97_SR_DCH | AC97_SR_BCIS))) /* Controller halted? */
1044 { /* not halted nor does it have pending interrupt - likely */ }
1045 else
1046 {
1047 /** @todo Stop DMA timer when DCH is set. */
1048 if (pStream->Regs.sr & AC97_SR_DCH)
1049 {
1050 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaSkippedDch);
1051 LogFunc(("[SD%RU8] DCH set\n", pStream->u8SD));
1052 }
1053 if (pStream->Regs.sr & AC97_SR_BCIS)
1054 {
1055 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaSkippedPendingBcis);
1056 LogFunc(("[SD%RU8] BCIS set\n", pStream->u8SD));
1057 }
1058 if ((pStream->Regs.cr & AC97_CR_RPBM) /* Bus master operation started. */ && !fInput)
1059 {
1060 /*ichac97R3WriteBUP(pThis, cbToProcess);*/
1061 }
1062
1063 ichac97R3StreamUnlock(pStreamCC);
1064 return VINF_SUCCESS;
1065 }
1066
1067 /* 0x1ba*2 = 0x374 (884) 0x3c0
1068 * Transfer loop.
1069 */
1070#ifdef LOG_ENABLED
1071 uint32_t cbProcessedTotal = 0;
1072#endif
1073 int rc = VINF_SUCCESS;
1074 PRTCIRCBUF pCircBuf = pStreamCC->State.pCircBuf;
1075 AssertReturnStmt(pCircBuf, ichac97R3StreamUnlock(pStreamCC), VINF_SUCCESS);
1076 Assert((uint32_t)pStream->Regs.picb * PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props) >= cbToProcess);
1077 Log3Func(("[SD%RU8] cbToProcess=%#x PICB=%#x/%#x\n", pStream->u8SD, cbToProcess,
1078 pStream->Regs.picb, pStream->Regs.picb * PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props)));
1079
1080 while (cbToProcess > 0)
1081 {
1082 uint32_t cbChunk = cbToProcess;
1083
1084 /*
1085 * Output.
1086 */
1087 if (!fInput)
1088 {
1089 void *pvDst = NULL;
1090 size_t cbDst = 0;
1091 RTCircBufAcquireWriteBlock(pCircBuf, cbChunk, &pvDst, &cbDst);
1092
1093 if (cbDst)
1094 {
1095 int rc2 = PDMDevHlpPCIPhysRead(pDevIns, pStream->Regs.bd.addr, pvDst, cbDst);
1096 AssertRC(rc2);
1097
1098 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
1099 { /* likely */ }
1100 else
1101 AudioHlpFileWrite(pStreamCC->Dbg.Runtime.pFileDMA, pvDst, cbDst, 0 /* fFlags */);
1102 }
1103
1104 RTCircBufReleaseWriteBlock(pCircBuf, cbDst);
1105
1106 cbChunk = (uint32_t)cbDst; /* Update the current chunk size to what really has been written. */
1107 }
1108 /*
1109 * Input.
1110 */
1111 else if (!fWriteSilence)
1112 {
1113 void *pvSrc = NULL;
1114 size_t cbSrc = 0;
1115 RTCircBufAcquireReadBlock(pCircBuf, cbChunk, &pvSrc, &cbSrc);
1116
1117 if (cbSrc)
1118 {
1119 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns, pStream->Regs.bd.addr, pvSrc, cbSrc);
1120 AssertRC(rc2);
1121
1122 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
1123 { /* likely */ }
1124 else
1125 AudioHlpFileWrite(pStreamCC->Dbg.Runtime.pFileDMA, pvSrc, cbSrc, 0 /* fFlags */);
1126 }
1127
1128 RTCircBufReleaseReadBlock(pCircBuf, cbSrc);
1129
1130 cbChunk = (uint32_t)cbSrc; /* Update the current chunk size to what really has been read. */
1131 }
1132 else
1133 {
1134 /* Since the format is signed 16-bit or 32-bit integer samples, we can
1135 use g_abRTZero64K as source and avoid some unnecessary bzero() work. */
1136 cbChunk = RT_MIN(cbChunk, sizeof(g_abRTZero64K));
1137 cbChunk = PDMAudioPropsFloorBytesToFrame(&pStreamCC->State.Cfg.Props, cbChunk);
1138
1139 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns, pStream->Regs.bd.addr, g_abRTZero64K, cbChunk);
1140 AssertRC(rc2);
1141 }
1142
1143 Assert(PDMAudioPropsIsSizeAligned(&pStreamCC->State.Cfg.Props, cbChunk));
1144 Assert(cbChunk <= cbToProcess);
1145
1146 /*
1147 * Advance.
1148 */
1149 pStream->Regs.picb -= cbChunk / PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props);
1150 pStream->Regs.bd.addr += cbChunk;
1151 cbToProcess -= cbChunk;
1152#ifdef LOG_ENABLED
1153 cbProcessedTotal += cbChunk;
1154#endif
1155 LogFlowFunc(("[SD%RU8] cbChunk=%#x, cbToProcess=%#x, cbTotal=%#x picb=%#x\n",
1156 pStream->u8SD, cbChunk, cbToProcess, cbProcessedTotal, pStream->Regs.picb));
1157 }
1158
1159 /*
1160 * Fetch a new buffer descriptor if we've exhausted the current one.
1161 */
1162 if (!pStream->Regs.picb)
1163 {
1164 uint32_t fNewSr = pStream->Regs.sr & ~AC97_SR_CELV;
1165
1166 if (pStream->Regs.bd.ctl_len & AC97_BD_IOC)
1167 fNewSr |= AC97_SR_BCIS;
1168
1169 if (pStream->Regs.civ != pStream->Regs.lvi)
1170 fNewSr |= ichac97R3StreamFetchNextBdle(pDevIns, pStream, pStreamCC);
1171 else
1172 {
1173 LogFunc(("Underrun CIV (%RU8) == LVI (%RU8)\n", pStream->Regs.civ, pStream->Regs.lvi));
1174 fNewSr |= AC97_SR_LVBCI | AC97_SR_DCH | AC97_SR_CELV;
1175 pThis->bup_flag = (pStream->Regs.bd.ctl_len & AC97_BD_BUP) ? BUP_LAST : 0;
1176 /** @todo r=bird: The bup_flag isn't cleared anywhere else. We should probably
1177 * do what the spec says, and keep writing zeros (silence).
1178 * Alternatively, we could hope the guest will pause the DMA engine
1179 * immediately after seeing this condition, in which case we should
1180 * stop the DMA timer from being re-armed. */
1181 }
1182
1183 ichac97StreamUpdateSR(pDevIns, pThis, pStream, fNewSr);
1184 }
1185
1186 ichac97R3StreamUnlock(pStreamCC);
1187 LogFlowFuncLeaveRC(rc);
1188 return rc;
1189}
1190
1191
1192/**
1193 * Input streams: Pulls data from the mixer, putting it in the internal DMA
1194 * buffer.
1195 *
1196 * @param pStreamR3 The AC'97 stream (ring-3 bits).
1197 * @param pSink The mixer sink to pull from.
1198 */
1199static void ichac97R3StreamPullFromMixer(PAC97STREAMR3 pStreamR3, PAUDMIXSINK pSink)
1200{
1201# ifdef LOG_ENABLED
1202 uint64_t const offWriteOld = pStreamR3->State.offWrite;
1203# endif
1204 pStreamR3->State.offWrite = AudioMixerSinkTransferToCircBuf(pSink,
1205 pStreamR3->State.pCircBuf,
1206 pStreamR3->State.offWrite,
1207 pStreamR3->u8SD,
1208 pStreamR3->Dbg.Runtime.fEnabled
1209 ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
1210
1211 Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
1212 pStreamR3->State.offWrite - offWriteOld, pStreamR3->State.offWrite));
1213
1214 /* Update buffer stats. */
1215 pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1216}
1217
1218
1219/**
1220 * Output streams: Pushes data to the mixer.
1221 *
1222 * @param pStreamR3 The AC'97 stream (ring-3 bits).
1223 * @param pSink The mixer sink to push to.
1224 */
1225static void ichac97R3StreamPushToMixer(PAC97STREAMR3 pStreamR3, PAUDMIXSINK pSink)
1226{
1227# ifdef LOG_ENABLED
1228 uint64_t const offReadOld = pStreamR3->State.offRead;
1229# endif
1230 pStreamR3->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
1231 pStreamR3->State.pCircBuf,
1232 pStreamR3->State.offRead,
1233 pStreamR3->u8SD,
1234 pStreamR3->Dbg.Runtime.fEnabled
1235 ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
1236
1237 Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
1238 pStreamR3->State.offRead - offReadOld, pStreamR3->State.offRead));
1239
1240 /* Update buffer stats. */
1241 pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1242}
1243
1244
1245/**
1246 * Updates an AC'97 stream by doing its DMA transfers.
1247 *
1248 * The host sink(s) set the overall pace (bird: no it doesn't, the DMA timer
1249 * does - we just hope like heck it matches the speed at which the *backend*
1250 * host audio driver processes samples).
1251 *
1252 * @param pDevIns The device instance.
1253 * @param pThis The shared AC'97 state.
1254 * @param pThisCC The ring-3 AC'97 state.
1255 * @param pStream The AC'97 stream to update (shared).
1256 * @param pStreamCC The AC'97 stream to update (ring-3).
1257 * @param pSink The sink being updated.
1258 */
1259static void ichac97R3StreamUpdateDma(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC,
1260 PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, PAUDMIXSINK pSink)
1261{
1262 RT_NOREF(pThisCC);
1263 int rc2;
1264
1265 /* The amount we're supposed to be transfering in this DMA period. */
1266 uint32_t cbPeriod = pStream->cbDmaPeriod;
1267
1268 /*
1269 * Output streams (SDO).
1270 */
1271 if (pStreamCC->State.Cfg.enmDir == PDMAUDIODIR_OUT)
1272 {
1273 /*
1274 * Check how much room we have in our DMA buffer. There should be at
1275 * least one period worth of space there or we're in an overflow situation.
1276 */
1277 uint32_t cbStreamFree = ichac97R3StreamGetFree(pStreamCC);
1278 if (cbStreamFree >= cbPeriod)
1279 { /* likely */ }
1280 else
1281 {
1282 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaFlowProblems);
1283 LogFunc(("Warning! Stream #%u has insufficient space free: %u bytes, need %u. Will try move data out of the buffer...\n",
1284 pStreamCC->u8SD, cbStreamFree, cbPeriod));
1285 int rc = AudioMixerSinkTryLock(pSink);
1286 if (RT_SUCCESS(rc))
1287 {
1288 ichac97R3StreamPushToMixer(pStreamCC, pSink);
1289 AudioMixerSinkUpdate(pSink, 0, 0);
1290 AudioMixerSinkUnlock(pSink);
1291 }
1292 else
1293 RTThreadYield();
1294 LogFunc(("Gained %u bytes.\n", ichac97R3StreamGetFree(pStreamCC) - cbStreamFree));
1295
1296 cbStreamFree = ichac97R3StreamGetFree(pStreamCC);
1297 if (cbStreamFree < cbPeriod)
1298 {
1299 /* Unable to make sufficient space. Drop the whole buffer content.
1300 * This is needed in order to keep the device emulation running at a constant rate,
1301 * at the cost of losing valid (but too much) data. */
1302 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaFlowErrors);
1303 LogRel2(("AC97: Warning: Hit stream #%RU8 overflow, dropping %u bytes of audio data\n",
1304 pStreamCC->u8SD, ichac97R3StreamGetUsed(pStreamCC)));
1305# ifdef AC97_STRICT
1306 AssertMsgFailed(("Hit stream #%RU8 overflow -- timing bug?\n", pStreamCC->u8SD));
1307# endif
1308 RTCircBufReset(pStreamCC->State.pCircBuf);
1309 pStreamCC->State.offWrite = 0;
1310 pStreamCC->State.offRead = 0;
1311 cbStreamFree = ichac97R3StreamGetFree(pStreamCC);
1312 Assert(cbStreamFree >= cbPeriod);
1313 }
1314 }
1315
1316 /*
1317 * Do the DMA transfer.
1318 */
1319 Log3Func(("[SD%RU8] PICB=%#x samples / %RU64 ms, cbFree=%#x / %RU64 ms, cbTransferChunk=%#x / %RU64 ms\n", pStream->u8SD,
1320 pStream->Regs.picb, PDMAudioPropsBytesToMilli(&pStreamCC->State.Cfg.Props,
1321 PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props)
1322 * pStream->Regs.picb),
1323 cbStreamFree, PDMAudioPropsBytesToMilli(&pStreamCC->State.Cfg.Props, cbStreamFree),
1324 cbPeriod, PDMAudioPropsBytesToMilli(&pStreamCC->State.Cfg.Props, cbPeriod)));
1325
1326 rc2 = ichac97R3StreamTransfer(pDevIns, pThis, pStream, pStreamCC, RT_MIN(cbStreamFree, cbPeriod),
1327 false /*fWriteSilence*/, false /*fInput*/);
1328 AssertRC(rc2);
1329
1330 pStreamCC->State.tsLastUpdateNs = RTTimeNanoTS();
1331
1332
1333 /*
1334 * Notify the AIO thread.
1335 */
1336 rc2 = AudioMixerSinkSignalUpdateJob(pSink);
1337 AssertRC(rc2);
1338 }
1339 /*
1340 * Input stream (SDI).
1341 */
1342 else
1343 {
1344 /*
1345 * See how much data we've got buffered...
1346 */
1347 bool fWriteSilence = false;
1348 uint32_t cbStreamUsed = ichac97R3StreamGetUsed(pStreamCC);
1349 if (pStreamCC->State.fInputPreBuffered && cbStreamUsed >= cbPeriod)
1350 { /*likely*/ }
1351 /*
1352 * Because it may take a while for the input stream to get going (at least
1353 * with pulseaudio), we feed the guest silence till we've pre-buffer a
1354 * couple of timer Hz periods. (This avoid lots of bogus buffer underruns
1355 * when starting an input stream and hogging the timer EMT.)
1356 */
1357 else if (!pStreamCC->State.fInputPreBuffered)
1358 {
1359 uint32_t const cbPreBuffer = PDMAudioPropsNanoToBytes(&pStreamCC->State.Cfg.Props,
1360 RT_NS_1SEC / pStreamCC->State.uTimerHz);
1361 if (cbStreamUsed < cbPreBuffer)
1362 {
1363 Log3Func(("Pre-buffering (got %#x out of %#x bytes)...\n", cbStreamUsed, cbPreBuffer));
1364 fWriteSilence = true;
1365 cbStreamUsed = cbPeriod;
1366 }
1367 else
1368 {
1369 Log3Func(("Completed pre-buffering (got %#x, needed %#x bytes).\n", cbStreamUsed, cbPreBuffer));
1370 pStreamCC->State.fInputPreBuffered = true;
1371 fWriteSilence = ichac97R3StreamGetFree(pStreamCC) >= cbPreBuffer + cbPreBuffer / 2;
1372 if (fWriteSilence)
1373 cbStreamUsed = cbPeriod;
1374 }
1375 }
1376 /*
1377 * When we're low on data, we must really try fetch some ourselves
1378 * as buffer underruns must not happen.
1379 */
1380 else
1381 {
1382 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaFlowProblems);
1383 LogFunc(("Warning! Stream #%u has insufficient data available: %u bytes, need %u. Will try move pull more data into the buffer...\n",
1384 pStreamCC->u8SD, cbStreamUsed, cbPeriod));
1385 int rc = AudioMixerSinkTryLock(pSink);
1386 if (RT_SUCCESS(rc))
1387 {
1388 AudioMixerSinkUpdate(pSink, cbStreamUsed, cbPeriod);
1389 ichac97R3StreamPullFromMixer(pStreamCC, pSink);
1390 AudioMixerSinkUnlock(pSink);
1391 }
1392 else
1393 RTThreadYield();
1394 LogFunc(("Gained %u bytes.\n", ichac97R3StreamGetUsed(pStreamCC) - cbStreamUsed));
1395 cbStreamUsed = ichac97R3StreamGetUsed(pStreamCC);
1396 if (cbStreamUsed < cbPeriod)
1397 {
1398 /* Unable to find sufficient input data by simple prodding.
1399 In order to keep a constant byte stream following thru the DMA
1400 engine into the guest, we will try again and then fall back on
1401 filling the gap with silence. */
1402 uint32_t cbSilence = 0;
1403 do
1404 {
1405 AudioMixerSinkLock(pSink);
1406
1407 cbStreamUsed = ichac97R3StreamGetUsed(pStreamCC);
1408 if (cbStreamUsed < cbPeriod)
1409 {
1410 ichac97R3StreamPullFromMixer(pStreamCC, pSink);
1411 cbStreamUsed = ichac97R3StreamGetUsed(pStreamCC);
1412 while (cbStreamUsed < cbPeriod)
1413 {
1414 void *pvDstBuf;
1415 size_t cbDstBuf;
1416 RTCircBufAcquireWriteBlock(pStreamCC->State.pCircBuf, cbPeriod - cbStreamUsed,
1417 &pvDstBuf, &cbDstBuf);
1418 RT_BZERO(pvDstBuf, cbDstBuf);
1419 RTCircBufReleaseWriteBlock(pStreamCC->State.pCircBuf, cbDstBuf);
1420 cbSilence += (uint32_t)cbDstBuf;
1421 cbStreamUsed += (uint32_t)cbDstBuf;
1422 }
1423 }
1424
1425 AudioMixerSinkUnlock(pSink);
1426 } while (cbStreamUsed < cbPeriod);
1427 if (cbSilence > 0)
1428 {
1429 STAM_REL_COUNTER_INC(&pStreamCC->State.StatDmaFlowErrors);
1430 STAM_REL_COUNTER_ADD(&pStreamCC->State.StatDmaFlowErrorBytes, cbSilence);
1431 LogRel2(("AC97: Warning: Stream #%RU8 underrun, added %u bytes of silence (%u us)\n", pStreamCC->u8SD,
1432 cbSilence, PDMAudioPropsBytesToMicro(&pStreamCC->State.Cfg.Props, cbSilence)));
1433 }
1434 }
1435 }
1436
1437 /*
1438 * Do the DMA'ing.
1439 */
1440 if (cbStreamUsed)
1441 {
1442 rc2 = ichac97R3StreamTransfer(pDevIns, pThis, pStream, pStreamCC, RT_MIN(cbPeriod, cbStreamUsed),
1443 fWriteSilence, true /*fInput*/);
1444 AssertRC(rc2);
1445
1446 pStreamCC->State.tsLastUpdateNs = RTTimeNanoTS();
1447 }
1448
1449 /*
1450 * We should always kick the AIO thread.
1451 */
1452 /** @todo This isn't entirely ideal. If we get into an underrun situation,
1453 * we ideally want the AIO thread to run right before the DMA timer
1454 * rather than right after it ran. */
1455 Log5Func(("Notifying AIO thread\n"));
1456 rc2 = AudioMixerSinkSignalUpdateJob(pSink);
1457 AssertRC(rc2);
1458 }
1459}
1460
1461
1462/**
1463 * @callback_method_impl{FNAUDMIXSINKUPDATE}
1464 *
1465 * For output streams this moves data from the internal DMA buffer (in which
1466 * ichac97R3StreamUpdateDma put it), thru the mixer and to the various backend
1467 * audio devices.
1468 *
1469 * For input streams this pulls data from the backend audio device(s), thru the
1470 * mixer and puts it in the internal DMA buffer ready for
1471 * ichac97R3StreamUpdateDma to pump into guest memory.
1472 */
1473static DECLCALLBACK(void) ichac97R3StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser)
1474{
1475 PAC97STATER3 const pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
1476 PAC97STREAMR3 const pStreamCC = (PAC97STREAMR3)pvUser;
1477 Assert(pStreamCC->u8SD == (uintptr_t)(pStreamCC - &pThisCC->aStreams[0]));
1478 Assert(pSink == ichac97R3IndexToSink(pThisCC, pStreamCC->u8SD));
1479 RT_NOREF(pThisCC);
1480
1481 /*
1482 * Output (SDO).
1483 */
1484 if (pStreamCC->State.Cfg.enmDir == PDMAUDIODIR_OUT)
1485 ichac97R3StreamPushToMixer(pStreamCC, pSink);
1486 /*
1487 * Input (SDI).
1488 */
1489 else
1490 ichac97R3StreamPullFromMixer(pStreamCC, pSink);
1491}
1492
1493
1494/**
1495 * Updates the next transfer based on a specific amount of bytes.
1496 *
1497 * @param pDevIns The device instance.
1498 * @param pStream The AC'97 stream to update (shared).
1499 * @param pStreamCC The AC'97 stream to update (ring-3).
1500 */
1501static void ichac97R3StreamTransferUpdate(PPDMDEVINS pDevIns, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
1502{
1503 /*
1504 * Get the number of bytes left in the current buffer.
1505 *
1506 * This isn't entirely optimal iff the current entry doesn't have IOC set, in
1507 * that case we should use the number of bytes to the next IOC. Unfortuantely,
1508 * it seems the spec doesn't allow us to prefetch more than one BDLE, so we
1509 * probably cannot look ahead without violating that restriction. This is
1510 * probably a purely theoretical problem at this point.
1511 */
1512 uint32_t const cbLeftInBdle = pStream->Regs.picb * PDMAudioPropsSampleSize(&pStreamCC->State.Cfg.Props);
1513 if (cbLeftInBdle > 0) /** @todo r=bird: see todo about this in ichac97R3StreamFetchBDLE. */
1514 {
1515 /*
1516 * Since the buffer can be up to 0xfffe samples long (frame aligning stereo
1517 * prevents 0xffff), which translates to 743ms at a 44.1kHz rate, we must
1518 * also take the nominal timer frequency into account here so we keep
1519 * moving data at a steady rate. (In theory, I think the guest can even
1520 * set up just one buffer and anticipate where we are in the buffer
1521 * processing when it writes/reads from it. Linux seems to be doing such
1522 * configs when not playing or something.)
1523 */
1524 uint32_t const cbMaxPerHz = PDMAudioPropsNanoToBytes(&pStreamCC->State.Cfg.Props, RT_NS_1SEC / pStreamCC->State.uTimerHz);
1525
1526 if (cbLeftInBdle <= cbMaxPerHz)
1527 pStream->cbDmaPeriod = cbLeftInBdle;
1528 /* Try avoid leaving a very short period at the end of a buffer. */
1529 else if (cbLeftInBdle >= cbMaxPerHz + cbMaxPerHz / 2)
1530 pStream->cbDmaPeriod = cbMaxPerHz;
1531 else
1532 pStream->cbDmaPeriod = PDMAudioPropsFloorBytesToFrame(&pStreamCC->State.Cfg.Props, cbLeftInBdle / 2);
1533
1534 /*
1535 * Translate the chunk size to timer ticks.
1536 */
1537 uint64_t const cNsXferChunk = PDMAudioPropsBytesToNano(&pStreamCC->State.Cfg.Props, pStream->cbDmaPeriod);
1538 pStream->cDmaPeriodTicks = PDMDevHlpTimerFromNano(pDevIns, pStream->hTimer, cNsXferChunk);
1539 Assert(pStream->cDmaPeriodTicks > 0);
1540
1541 Log3Func(("[SD%RU8] cbLeftInBdle=%#RX32 cbMaxPerHz=%#RX32 (%RU16Hz) -> cbDmaPeriod=%#RX32 cDmaPeriodTicks=%RX64\n",
1542 pStream->u8SD, cbLeftInBdle, cbMaxPerHz, pStreamCC->State.uTimerHz, pStream->cbDmaPeriod, pStream->cDmaPeriodTicks));
1543 }
1544}
1545
1546
1547/**
1548 * Sets the virtual device timer to a new expiration time.
1549 *
1550 * @param pDevIns The device instance.
1551 * @param pStream AC'97 stream to set timer for.
1552 * @param cTicksToDeadline The number of ticks to the new deadline.
1553 *
1554 * @remarks This used to be more complicated a long time ago...
1555 */
1556DECLINLINE(void) ichac97R3TimerSet(PPDMDEVINS pDevIns, PAC97STREAM pStream, uint64_t cTicksToDeadline)
1557{
1558 int rc = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimer, cTicksToDeadline, &pStream->uArmedTs);
1559 AssertRC(rc);
1560}
1561
1562
1563/**
1564 * @callback_method_impl{FNTMTIMERDEV,
1565 * Timer callback which handles the audio data transfers on a periodic basis.}
1566 */
1567static DECLCALLBACK(void) ichac97R3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1568{
1569 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
1570 STAM_PROFILE_START(&pThis->StatTimer, a);
1571 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
1572 PAC97STREAM pStream = (PAC97STREAM)pvUser;
1573 PAC97STREAMR3 pStreamCC = &RT_SAFE_SUBSCRIPT8(pThisCC->aStreams, pStream->u8SD);
1574 Assert(hTimer == pStream->hTimer); RT_NOREF(hTimer);
1575
1576 Assert(pStream - &pThis->aStreams[0] == pStream->u8SD);
1577 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1578 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pStream->hTimer));
1579
1580 PAUDMIXSINK pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
1581 if (pSink && AudioMixerSinkIsActive(pSink))
1582 {
1583 ichac97R3StreamUpdateDma(pDevIns, pThis, pThisCC, pStream, pStreamCC, pSink);
1584
1585 pStream->uDmaPeriod++;
1586 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
1587 ichac97R3TimerSet(pDevIns, pStream, pStream->cDmaPeriodTicks);
1588 }
1589
1590 STAM_PROFILE_STOP(&pThis->StatTimer, a);
1591}
1592
1593#endif /* IN_RING3 */
1594
1595
1596/*********************************************************************************************************************************
1597* AC'97 Stream Management *
1598*********************************************************************************************************************************/
1599#ifdef IN_RING3
1600
1601/**
1602 * Locks an AC'97 stream for serialized access.
1603 *
1604 * @returns VBox status code.
1605 * @param pStreamCC The AC'97 stream to lock (ring-3).
1606 */
1607DECLINLINE(void) ichac97R3StreamLock(PAC97STREAMR3 pStreamCC)
1608{
1609 int rc2 = RTCritSectEnter(&pStreamCC->State.CritSect);
1610 AssertRC(rc2);
1611}
1612
1613/**
1614 * Unlocks a formerly locked AC'97 stream.
1615 *
1616 * @returns VBox status code.
1617 * @param pStreamCC The AC'97 stream to unlock (ring-3).
1618 */
1619DECLINLINE(void) ichac97R3StreamUnlock(PAC97STREAMR3 pStreamCC)
1620{
1621 int rc2 = RTCritSectLeave(&pStreamCC->State.CritSect);
1622 AssertRC(rc2);
1623}
1624
1625#endif /* IN_RING3 */
1626
1627/**
1628 * Updates the status register (SR) of an AC'97 audio stream.
1629 *
1630 * @param pDevIns The device instance.
1631 * @param pThis The shared AC'97 state.
1632 * @param pStream AC'97 stream to update SR for.
1633 * @param new_sr New value for status register (SR).
1634 */
1635static void ichac97StreamUpdateSR(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream, uint32_t new_sr)
1636{
1637 bool fSignal = false;
1638 int iIRQL = 0;
1639
1640 uint32_t new_mask = new_sr & AC97_SR_INT_MASK;
1641 uint32_t old_mask = pStream->Regs.sr & AC97_SR_INT_MASK;
1642
1643 if (new_mask ^ old_mask)
1644 {
1645 /** @todo Is IRQ deasserted when only one of status bits is cleared? */
1646 if (!new_mask)
1647 {
1648 fSignal = true;
1649 iIRQL = 0;
1650 }
1651 else if ((new_mask & AC97_SR_LVBCI) && (pStream->Regs.cr & AC97_CR_LVBIE))
1652 {
1653 fSignal = true;
1654 iIRQL = 1;
1655 }
1656 else if ((new_mask & AC97_SR_BCIS) && (pStream->Regs.cr & AC97_CR_IOCE))
1657 {
1658 fSignal = true;
1659 iIRQL = 1;
1660 }
1661 }
1662
1663 pStream->Regs.sr = new_sr;
1664
1665 LogFlowFunc(("IOC%d, LVB%d, sr=%#x, fSignal=%RTbool, IRQL=%d\n",
1666 pStream->Regs.sr & AC97_SR_BCIS, pStream->Regs.sr & AC97_SR_LVBCI, pStream->Regs.sr, fSignal, iIRQL));
1667
1668 if (fSignal)
1669 {
1670 static uint32_t const s_aMasks[] = { AC97_GS_PIINT, AC97_GS_POINT, AC97_GS_MINT };
1671 Assert(pStream->u8SD < AC97_MAX_STREAMS);
1672 if (iIRQL)
1673 pThis->glob_sta |= s_aMasks[pStream->u8SD];
1674 else
1675 pThis->glob_sta &= ~s_aMasks[pStream->u8SD];
1676
1677 LogFlowFunc(("Setting IRQ level=%d\n", iIRQL));
1678 PDMDevHlpPCISetIrq(pDevIns, 0, iIRQL);
1679 }
1680}
1681
1682/**
1683 * Writes a new value to a stream's status register (SR).
1684 *
1685 * @param pDevIns The device instance.
1686 * @param pThis The shared AC'97 device state.
1687 * @param pStream Stream to update SR for.
1688 * @param u32Val New value to set the stream's SR to.
1689 */
1690static void ichac97StreamWriteSR(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream, uint32_t u32Val)
1691{
1692 Log3Func(("[SD%RU8] SR <- %#x (sr %#x)\n", pStream->u8SD, u32Val, pStream->Regs.sr));
1693
1694 pStream->Regs.sr |= u32Val & ~(AC97_SR_RO_MASK | AC97_SR_WCLEAR_MASK);
1695 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr & ~(u32Val & AC97_SR_WCLEAR_MASK));
1696}
1697
1698#ifdef IN_RING3
1699
1700/**
1701 * Resets an AC'97 stream.
1702 *
1703 * @param pThis The shared AC'97 state.
1704 * @param pStream The AC'97 stream to reset (shared).
1705 * @param pStreamCC The AC'97 stream to reset (ring-3).
1706 */
1707static void ichac97R3StreamReset(PAC97STATE pThis, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
1708{
1709 ichac97R3StreamLock(pStreamCC);
1710
1711 LogFunc(("[SD%RU8]\n", pStream->u8SD));
1712
1713 if (pStreamCC->State.pCircBuf)
1714 RTCircBufReset(pStreamCC->State.pCircBuf);
1715
1716 pStream->Regs.bdbar = 0;
1717 pStream->Regs.civ = 0;
1718 pStream->Regs.lvi = 0;
1719
1720 pStream->Regs.picb = 0;
1721 pStream->Regs.piv = 0; /* Note! Because this is also zero, we will actually start transferring with BDLE00. */
1722 pStream->Regs.cr &= AC97_CR_DONT_CLEAR_MASK;
1723 pStream->Regs.bd_valid = 0;
1724
1725 RT_ZERO(pThis->silence);
1726
1727 ichac97R3StreamUnlock(pStreamCC);
1728}
1729
1730/**
1731 * Retrieves a specific driver stream of a AC'97 driver.
1732 *
1733 * @returns Pointer to driver stream if found, or NULL if not found.
1734 * @param pDrv Driver to retrieve driver stream for.
1735 * @param enmDir Stream direction to retrieve.
1736 * @param enmPath Stream destination / source to retrieve.
1737 */
1738static PAC97DRIVERSTREAM ichac97R3MixerGetDrvStream(PAC97DRIVER pDrv, PDMAUDIODIR enmDir, PDMAUDIOPATH enmPath)
1739{
1740 if (enmDir == PDMAUDIODIR_IN)
1741 {
1742 LogFunc(("enmRecSource=%d\n", enmPath));
1743 switch (enmPath)
1744 {
1745 case PDMAUDIOPATH_IN_LINE:
1746 return &pDrv->LineIn;
1747 case PDMAUDIOPATH_IN_MIC:
1748 return &pDrv->MicIn;
1749 default:
1750 AssertFailedBreak();
1751 }
1752 }
1753 else if (enmDir == PDMAUDIODIR_OUT)
1754 {
1755 LogFunc(("enmPlaybackDst=%d\n", enmPath));
1756 switch (enmPath)
1757 {
1758 case PDMAUDIOPATH_OUT_FRONT:
1759 return &pDrv->Out;
1760 default:
1761 AssertFailedBreak();
1762 }
1763 }
1764 else
1765 AssertFailed();
1766
1767 return NULL;
1768}
1769
1770/**
1771 * Adds a driver stream to a specific mixer sink.
1772 *
1773 * Called by ichac97R3MixerAddDrvStreams() and ichac97R3MixerAddDrv().
1774 *
1775 * @returns VBox status code.
1776 * @param pDevIns The device instance.
1777 * @param pMixSink Mixer sink to add driver stream to.
1778 * @param pCfg Stream configuration to use.
1779 * @param pDrv Driver stream to add.
1780 */
1781static int ichac97R3MixerAddDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PCPDMAUDIOSTREAMCFG pCfg, PAC97DRIVER pDrv)
1782{
1783 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
1784 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pCfg->szName));
1785
1786 int rc;
1787 PAC97DRIVERSTREAM pDrvStream = ichac97R3MixerGetDrvStream(pDrv, pCfg->enmDir, pCfg->enmPath);
1788 if (pDrvStream)
1789 {
1790 AssertMsg(pDrvStream->pMixStrm == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
1791
1792 PAUDMIXSTREAM pMixStrm;
1793 rc = AudioMixerSinkCreateStream(pMixSink, pDrv->pConnector, pCfg, pDevIns, &pMixStrm);
1794 LogFlowFunc(("LUN#%RU8: Created stream \"%s\" for sink, rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
1795 if (RT_SUCCESS(rc))
1796 {
1797 rc = AudioMixerSinkAddStream(pMixSink, pMixStrm);
1798 LogFlowFunc(("LUN#%RU8: Added stream \"%s\" to sink, rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
1799 if (RT_SUCCESS(rc))
1800 pDrvStream->pMixStrm = pMixStrm;
1801 else
1802 AudioMixerStreamDestroy(pMixStrm, pDevIns, true /*fImmediate*/);
1803 }
1804 }
1805 else
1806 rc = VERR_INVALID_PARAMETER;
1807
1808 LogFlowFuncLeaveRC(rc);
1809 return rc;
1810}
1811
1812
1813/**
1814 * Adds all current driver streams to a specific mixer sink.
1815 *
1816 * Called by ichac97R3StreamSetUp().
1817 *
1818 * @returns VBox status code.
1819 * @param pDevIns The device instance.
1820 * @param pThisCC The ring-3 AC'97 state.
1821 * @param pMixSink Mixer sink to add stream to.
1822 * @param pCfg Stream configuration to use.
1823 */
1824static int ichac97R3MixerAddDrvStreams(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAUDMIXSINK pMixSink, PCPDMAUDIOSTREAMCFG pCfg)
1825{
1826 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
1827
1828 int rc;
1829 if (AudioHlpStreamCfgIsValid(pCfg))
1830 {
1831 rc = AudioMixerSinkSetFormat(pMixSink, &pCfg->Props, pCfg->Device.cMsSchedulingHint);
1832 if (RT_SUCCESS(rc))
1833 {
1834 PAC97DRIVER pDrv;
1835 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
1836 {
1837 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pMixSink, pCfg, pDrv);
1838 if (RT_FAILURE(rc2))
1839 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
1840
1841 /* Do not pass failure to rc here, as there might be drivers which aren't
1842 configured / ready yet. */
1843 }
1844 }
1845 }
1846 else
1847 rc = VERR_INVALID_PARAMETER;
1848
1849 LogFlowFuncLeaveRC(rc);
1850 return rc;
1851}
1852
1853
1854/**
1855 * Removes a driver stream from a specific mixer sink.
1856 *
1857 * Worker for ichac97R3MixerRemoveDrvStreams.
1858 *
1859 * @param pDevIns The device instance.
1860 * @param pMixSink Mixer sink to remove audio streams from.
1861 * @param enmDir Stream direction to remove.
1862 * @param enmPath Stream destination / source to remove.
1863 * @param pDrv Driver stream to remove.
1864 */
1865static void ichac97R3MixerRemoveDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PDMAUDIODIR enmDir,
1866 PDMAUDIOPATH enmPath, PAC97DRIVER pDrv)
1867{
1868 PAC97DRIVERSTREAM pDrvStream = ichac97R3MixerGetDrvStream(pDrv, enmDir, enmPath);
1869 if (pDrvStream)
1870 {
1871 if (pDrvStream->pMixStrm)
1872 {
1873 AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm);
1874
1875 AudioMixerStreamDestroy(pDrvStream->pMixStrm, pDevIns, false /*fImmediate*/);
1876 pDrvStream->pMixStrm = NULL;
1877 }
1878 }
1879}
1880
1881/**
1882 * Removes all driver streams from a specific mixer sink.
1883 *
1884 * Called by ichac97R3StreamSetUp() and ichac97R3StreamsDestroy().
1885 *
1886 * @param pDevIns The device instance.
1887 * @param pThisCC The ring-3 AC'97 state.
1888 * @param pMixSink Mixer sink to remove audio streams from.
1889 * @param enmDir Stream direction to remove.
1890 * @param enmPath Stream destination / source to remove.
1891 */
1892static void ichac97R3MixerRemoveDrvStreams(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAUDMIXSINK pMixSink,
1893 PDMAUDIODIR enmDir, PDMAUDIOPATH enmPath)
1894{
1895 AssertPtrReturnVoid(pMixSink);
1896
1897 PAC97DRIVER pDrv;
1898 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
1899 {
1900 ichac97R3MixerRemoveDrvStream(pDevIns, pMixSink, enmDir, enmPath, pDrv);
1901 }
1902}
1903
1904
1905/**
1906 * Gets the frequency of a given stream.
1907 *
1908 * @returns The frequency. Zero if invalid stream index.
1909 * @param pThis The shared AC'97 device state.
1910 * @param idxStream The stream.
1911 */
1912DECLINLINE(uint32_t) ichach97R3CalcStreamHz(PAC97STATE pThis, uint8_t idxStream)
1913{
1914 switch (idxStream)
1915 {
1916 case AC97SOUNDSOURCE_PI_INDEX:
1917 return ichac97MixerGet(pThis, AC97_PCM_LR_ADC_Rate);
1918
1919 case AC97SOUNDSOURCE_MC_INDEX:
1920 return ichac97MixerGet(pThis, AC97_MIC_ADC_Rate);
1921
1922 case AC97SOUNDSOURCE_PO_INDEX:
1923 return ichac97MixerGet(pThis, AC97_PCM_Front_DAC_Rate);
1924
1925 default:
1926 AssertMsgFailedReturn(("%d\n", idxStream), 0);
1927 }
1928}
1929
1930
1931/**
1932 * Gets the PCM properties for a given stream.
1933 *
1934 * @returns pProps.
1935 * @param pThis The shared AC'97 device state.
1936 * @param idxStream Which stream
1937 * @param pProps Where to return the stream properties.
1938 */
1939DECLINLINE(PPDMAUDIOPCMPROPS) ichach97R3CalcStreamProps(PAC97STATE pThis, uint8_t idxStream, PPDMAUDIOPCMPROPS pProps)
1940{
1941 PDMAudioPropsInit(pProps, 2 /*16-bit*/, true /*signed*/, 2 /*stereo*/, ichach97R3CalcStreamHz(pThis, idxStream));
1942 return pProps;
1943}
1944
1945
1946/**
1947 * Sets up an AC'97 stream with its current mixer settings.
1948 *
1949 * This will set up an AC'97 stream with 2 (stereo) channels, 16-bit samples and
1950 * the last set sample rate in the AC'97 mixer for this stream.
1951 *
1952 * @returns VBox status code.
1953 * @retval VINF_NO_CHANGE if the streams weren't re-created.
1954 *
1955 * @param pDevIns The device instance.
1956 * @param pThis The shared AC'97 device state (shared).
1957 * @param pThisCC The shared AC'97 device state (ring-3).
1958 * @param pStream The AC'97 stream to open (shared).
1959 * @param pStreamCC The AC'97 stream to open (ring-3).
1960 * @param fForce Whether to force re-opening the stream or not.
1961 * Otherwise re-opening only will happen if the PCM properties have changed.
1962 *
1963 * @remarks This is called holding:
1964 * -# The AC'97 device lock.
1965 * -# The AC'97 stream lock.
1966 * -# The mixer sink lock (to prevent racing AIO thread).
1967 */
1968static int ichac97R3StreamSetUp(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC, PAC97STREAM pStream,
1969 PAC97STREAMR3 pStreamCC, bool fForce)
1970{
1971 /*
1972 * Assemble the stream config and get the associated mixer sink.
1973 */
1974 PDMAUDIOPCMPROPS PropsTmp;
1975 PDMAUDIOSTREAMCFG Cfg;
1976 PDMAudioStrmCfgInitWithProps(&Cfg, ichach97R3CalcStreamProps(pThis, pStream->u8SD, &PropsTmp));
1977 Assert(Cfg.enmDir != PDMAUDIODIR_UNKNOWN);
1978
1979 PAUDMIXSINK pMixSink;
1980 switch (pStream->u8SD)
1981 {
1982 case AC97SOUNDSOURCE_PI_INDEX:
1983 Cfg.enmDir = PDMAUDIODIR_IN;
1984 Cfg.enmPath = PDMAUDIOPATH_IN_LINE;
1985 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Line-In");
1986
1987 pMixSink = pThisCC->pSinkLineIn;
1988 break;
1989
1990 case AC97SOUNDSOURCE_MC_INDEX:
1991 Cfg.enmDir = PDMAUDIODIR_IN;
1992 Cfg.enmPath = PDMAUDIOPATH_IN_MIC;
1993 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Mic-In");
1994
1995 pMixSink = pThisCC->pSinkMicIn;
1996 break;
1997
1998 case AC97SOUNDSOURCE_PO_INDEX:
1999 Cfg.enmDir = PDMAUDIODIR_OUT;
2000 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
2001 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output");
2002
2003 pMixSink = pThisCC->pSinkOut;
2004 break;
2005
2006 default:
2007 AssertMsgFailedReturn(("u8SD=%d\n", pStream->u8SD), VERR_INTERNAL_ERROR_3);
2008 }
2009
2010 /*
2011 * Don't continue if the frequency is out of range (the rest of the
2012 * properties should be okay).
2013 */
2014 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX];
2015 ASSERT_GUEST_MSG_RETURN(AudioHlpStreamCfgIsValid(&Cfg),
2016 ("Invalid stream #%u rate: %s\n", pStreamCC->u8SD,
2017 PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp)) ),
2018 VERR_OUT_OF_RANGE);
2019
2020 /*
2021 * Read the buffer descriptors and check what the max distance between
2022 * interrupts are, so we can more correctly size the internal DMA buffer.
2023 *
2024 * Note! The buffer list are not fixed once the stream starts running as
2025 * with HDA, so this is just a general idea of what the guest is
2026 * up to and we cannot really make much of a plan out of it.
2027 */
2028 AC97BDLE aBdl[AC97_MAX_BDLE];
2029 RT_ZERO(aBdl);
2030 PDMDevHlpPCIPhysRead(pDevIns, pStream->Regs.bdbar, aBdl, sizeof(aBdl));
2031
2032 uint8_t const bLvi = pStream->Regs.lvi % AC97_MAX_BDLE /* paranoia */;
2033 uint8_t const bCiv = pStream->Regs.civ % AC97_MAX_BDLE /* paranoia */;
2034 uint32_t cSamplesMax = 0;
2035 uint32_t cSamplesMin = UINT32_MAX;
2036 uint32_t cSamplesCur = 0;
2037 uint32_t cSamplesTotal = 0;
2038 uint32_t cBuffers = 1;
2039 for (uintptr_t i = bCiv; ; cBuffers++)
2040 {
2041 cSamplesTotal += aBdl[i].ctl_len & AC97_BD_LEN_MASK;
2042 cSamplesCur += aBdl[i].ctl_len & AC97_BD_LEN_MASK;
2043 if (aBdl[i].ctl_len & AC97_BD_IOC)
2044 {
2045 if (cSamplesCur > cSamplesMax)
2046 cSamplesMax = cSamplesCur;
2047 if (cSamplesCur < cSamplesMin)
2048 cSamplesMin = cSamplesCur;
2049 cSamplesCur = 0;
2050 }
2051
2052 /* Advance. */
2053 if (i != bLvi)
2054 i = (i + 1) % RT_ELEMENTS(aBdl);
2055 else
2056 break;
2057 }
2058 if (!cSamplesCur)
2059 { /* likely */ }
2060 else if (!cSamplesMax)
2061 {
2062 LogFlowFunc(("%u buffers without IOC set, assuming %#x samples as the IOC period.\n", cBuffers, cSamplesMax));
2063 cSamplesMin = cSamplesMax = cSamplesCur;
2064 }
2065 else if (cSamplesCur > cSamplesMax)
2066 {
2067 LogFlowFunc(("final buffer is without IOC, using open period as max (%#x vs current max %#x).\n", cSamplesCur, cSamplesMax));
2068 cSamplesMax = cSamplesCur;
2069 }
2070 else
2071 LogFlowFunc(("final buffer is without IOC, ignoring (%#x vs current max %#x).\n", cSamplesCur, cSamplesMax));
2072
2073 uint32_t const cbDmaMinBuf = cSamplesMax * PDMAudioPropsSampleSize(&Cfg.Props) * 3; /* see further down */
2074 uint32_t const cMsDmaMinBuf = PDMAudioPropsBytesToMilli(&Cfg.Props, cbDmaMinBuf);
2075 LogRel3(("AC97: [SD%RU8] buffer length stats: total=%#x in %u buffers, min=%#x, max=%#x => min DMA buffer %u ms / %#x bytes\n",
2076 pStream->u8SD, cSamplesTotal, cBuffers, cSamplesMin, cSamplesMax, cMsDmaMinBuf, cbDmaMinBuf));
2077
2078 /*
2079 * Calculate the timer Hz / scheduling hint based on the stream frame rate.
2080 */
2081 uint32_t uTimerHz;
2082 if (pThis->uTimerHz == AC97_TIMER_HZ_DEFAULT) /* Make sure that we don't have any custom Hz rate set we want to enforce */
2083 {
2084 if (Cfg.Props.uHz > 44100) /* E.g. 48000 Hz. */
2085 uTimerHz = 200;
2086 else
2087 uTimerHz = AC97_TIMER_HZ_DEFAULT;
2088 }
2089 else
2090 uTimerHz = pThis->uTimerHz;
2091
2092 if ( uTimerHz >= 10
2093 && uTimerHz <= 500)
2094 { /* likely */ }
2095 else
2096 {
2097 LogFunc(("[SD%RU8] Adjusting uTimerHz=%u to %u\n", pStream->u8SD, uTimerHz,
2098 Cfg.Props.uHz > 44100 ? 200 : AC97_TIMER_HZ_DEFAULT));
2099 uTimerHz = Cfg.Props.uHz > 44100 ? 200 : AC97_TIMER_HZ_DEFAULT;
2100 }
2101
2102 /* Translate it to a scheduling hint. */
2103 uint32_t const cMsSchedulingHint = RT_MS_1SEC / uTimerHz;
2104
2105 /*
2106 * Calculate the circular buffer size so we can decide whether to recreate
2107 * the stream or not.
2108 *
2109 * As mentioned in the HDA code, this should be at least able to hold the
2110 * data transferred in three DMA periods and in three AIO period (whichever
2111 * is higher). However, if we assume that the DMA code will engage the DMA
2112 * timer thread (currently EMT) if the AIO thread isn't getting schduled to
2113 * transfer data thru the stack, we don't need to go overboard and double
2114 * the minimums here. The less buffer the less possible delay can build when
2115 * TM is doing catch up.
2116 */
2117 uint32_t cMsCircBuf = Cfg.enmDir == PDMAUDIODIR_IN ? pThis->cMsCircBufIn : pThis->cMsCircBufOut;
2118 cMsCircBuf = RT_MAX(cMsCircBuf, cMsDmaMinBuf);
2119 cMsCircBuf = RT_MAX(cMsCircBuf, cMsSchedulingHint * 3);
2120 cMsCircBuf = RT_MIN(cMsCircBuf, RT_MS_1SEC * 2);
2121 uint32_t const cbCircBuf = PDMAudioPropsMilliToBytes(&Cfg.Props, cMsCircBuf);
2122
2123 LogFlowFunc(("Stream %u: uTimerHz: %u -> %u; cMsSchedulingHint: %u -> %u; cbCircBuf: %#zx -> %#x (%u ms, cMsDmaMinBuf=%u)%s\n",
2124 pStreamCC->u8SD, pStreamCC->State.uTimerHz, uTimerHz,
2125 pStreamCC->State.Cfg.Device.cMsSchedulingHint, cMsSchedulingHint,
2126 pStreamCC->State.pCircBuf ? RTCircBufSize(pStreamCC->State.pCircBuf) : 0, cbCircBuf, cMsCircBuf, cMsDmaMinBuf,
2127 !pStreamCC->State.pCircBuf || RTCircBufSize(pStreamCC->State.pCircBuf) != cbCircBuf ? " - re-creating DMA buffer" : ""));
2128
2129 /*
2130 * Update the stream's timer rate and scheduling hint, re-registering the AIO
2131 * update job if necessary.
2132 */
2133 if ( pStreamCC->State.Cfg.Device.cMsSchedulingHint != cMsSchedulingHint
2134 || !pStreamCC->State.fRegisteredAsyncUpdateJob)
2135 {
2136 if (pStreamCC->State.fRegisteredAsyncUpdateJob)
2137 AudioMixerSinkRemoveUpdateJob(pMixSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC);
2138 int rc2 = AudioMixerSinkAddUpdateJob(pMixSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC,
2139 pStreamCC->State.Cfg.Device.cMsSchedulingHint);
2140 AssertRC(rc2);
2141 pStreamCC->State.fRegisteredAsyncUpdateJob = RT_SUCCESS(rc2) || rc2 == VERR_ALREADY_EXISTS;
2142 }
2143
2144 pStreamCC->State.uTimerHz = uTimerHz;
2145 Cfg.Device.cMsSchedulingHint = cMsSchedulingHint;
2146
2147 /*
2148 * Re-create the circular buffer if necessary, resetting if not.
2149 */
2150 if ( pStreamCC->State.pCircBuf
2151 && RTCircBufSize(pStreamCC->State.pCircBuf) == cbCircBuf)
2152 RTCircBufReset(pStreamCC->State.pCircBuf);
2153 else
2154 {
2155 if (pStreamCC->State.pCircBuf)
2156 RTCircBufDestroy(pStreamCC->State.pCircBuf);
2157
2158 int rc = RTCircBufCreate(&pStreamCC->State.pCircBuf, cbCircBuf);
2159 AssertRCReturnStmt(rc, pStreamCC->State.pCircBuf = NULL, rc);
2160
2161 pStreamCC->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStreamCC->State.pCircBuf);
2162 }
2163 Assert(pStreamCC->State.StatDmaBufSize == cbCircBuf);
2164
2165 /*
2166 * Only (re-)create the stream (and driver chain) if we really have to.
2167 * Otherwise avoid this and just reuse it, as this costs performance.
2168 */
2169 int rc = VINF_SUCCESS;
2170 if ( fForce
2171 || !PDMAudioStrmCfgMatchesProps(&Cfg, &pStreamCC->State.Cfg.Props)
2172 || (pStreamCC->State.nsRetrySetup && RTTimeNanoTS() >= pStreamCC->State.nsRetrySetup))
2173 {
2174 LogRel2(("AC97: Setting up stream #%u: %s\n", pStreamCC->u8SD, PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp)) ));
2175
2176 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pMixSink, Cfg.enmDir, Cfg.enmPath);
2177
2178 rc = ichac97R3MixerAddDrvStreams(pDevIns, pThisCC, pMixSink, &Cfg);
2179 if (RT_SUCCESS(rc))
2180 {
2181 PDMAudioStrmCfgCopy(&pStreamCC->State.Cfg, &Cfg);
2182 pStreamCC->State.nsRetrySetup = 0;
2183 LogFlowFunc(("[SD%RU8] success (uHz=%u)\n", pStreamCC->u8SD, PDMAudioPropsHz(&Cfg.Props)));
2184 }
2185 else
2186 {
2187 LogFunc(("[SD%RU8] ichac97R3MixerAddDrvStreams failed: %Rrc (uHz=%u)\n",
2188 pStreamCC->u8SD, rc, PDMAudioPropsHz(&Cfg.Props)));
2189 pStreamCC->State.nsRetrySetup = RTTimeNanoTS() + 5*RT_NS_1SEC_64; /* retry in 5 seconds, unless config changes. */
2190 }
2191 }
2192 else
2193 {
2194 LogFlowFunc(("[SD%RU8] Skipping set-up (unchanged: %s)\n",
2195 pStreamCC->u8SD, PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp))));
2196 rc = VINF_NO_CHANGE;
2197 }
2198 return rc;
2199}
2200
2201
2202/**
2203 * Tears down an AC'97 stream (counter part to ichac97R3StreamSetUp).
2204 *
2205 * Empty stub at present, nothing to do here as we reuse streams and only really
2206 * re-open them if parameters changed (seldom).
2207 *
2208 * @param pStream The AC'97 stream to close (shared).
2209 */
2210static void ichac97R3StreamTearDown(PAC97STREAM pStream)
2211{
2212 RT_NOREF(pStream);
2213 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2214}
2215
2216
2217/**
2218 * Tears down and sets up an AC'97 stream on the backend side with the current
2219 * AC'97 mixer settings for this stream.
2220 *
2221 * @returns VBox status code.
2222 * @param pDevIns The device instance.
2223 * @param pThis The shared AC'97 device state.
2224 * @param pThisCC The ring-3 AC'97 device state.
2225 * @param pStream The AC'97 stream to re-open (shared).
2226 * @param pStreamCC The AC'97 stream to re-open (ring-3).
2227 * @param fForce Whether to force re-opening the stream or not.
2228 * Otherwise re-opening only will happen if the PCM properties have changed.
2229 */
2230static int ichac97R3StreamReSetUp(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC,
2231 PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, bool fForce)
2232{
2233 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatReSetUpChanged, r);
2234 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2235 Assert(pStream->u8SD == pStreamCC->u8SD);
2236 Assert(pStream - &pThis->aStreams[0] == pStream->u8SD);
2237 Assert(pStreamCC - &pThisCC->aStreams[0] == pStream->u8SD);
2238
2239 ichac97R3StreamTearDown(pStream);
2240 int rc = ichac97R3StreamSetUp(pDevIns, pThis, pThisCC, pStream, pStreamCC, fForce);
2241 if (rc == VINF_NO_CHANGE)
2242 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReSetUpSame, r);
2243 else
2244 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReSetUpChanged, r);
2245 return rc;
2246}
2247
2248
2249/**
2250 * Enables or disables an AC'97 audio stream.
2251 *
2252 * @returns VBox status code.
2253 * @param pDevIns The device instance.
2254 * @param pThis The shared AC'97 state.
2255 * @param pThisCC The ring-3 AC'97 state.
2256 * @param pStream The AC'97 stream to enable or disable (shared state).
2257 * @param pStreamCC The ring-3 stream state (matching to @a pStream).
2258 * @param fEnable Whether to enable or disable the stream.
2259 *
2260 */
2261static int ichac97R3StreamEnable(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC,
2262 PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, bool fEnable)
2263{
2264 ichac97R3StreamLock(pStreamCC);
2265 PAUDMIXSINK const pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2266 AudioMixerSinkLock(pSink);
2267
2268 int rc = VINF_SUCCESS;
2269 /*
2270 * Enable.
2271 */
2272 if (fEnable)
2273 {
2274 /* Reset the input pre-buffering state and DMA period counter. */
2275 pStreamCC->State.fInputPreBuffered = false;
2276 pStream->uDmaPeriod = 0;
2277
2278 /* Set up (update) the AC'97 stream as needed. */
2279 rc = ichac97R3StreamSetUp(pDevIns, pThis, pThisCC, pStream, pStreamCC, false /* fForce */);
2280 if (RT_SUCCESS(rc))
2281 {
2282 /* Open debug files. */
2283 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2284 { /* likely */ }
2285 else
2286 {
2287 if (!AudioHlpFileIsOpen(pStreamCC->Dbg.Runtime.pFileStream))
2288 AudioHlpFileOpen(pStreamCC->Dbg.Runtime.pFileStream, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2289 &pStreamCC->State.Cfg.Props);
2290 if (!AudioHlpFileIsOpen(pStreamCC->Dbg.Runtime.pFileDMA))
2291 AudioHlpFileOpen(pStreamCC->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2292 &pStreamCC->State.Cfg.Props);
2293 }
2294
2295 /* Do the actual enabling (won't fail as long as pSink is valid). */
2296 rc = AudioMixerSinkStart(pSink);
2297 }
2298 }
2299 /*
2300 * Disable
2301 */
2302 else
2303 {
2304 rc = AudioMixerSinkDrainAndStop(pSink, pStreamCC->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamCC->State.pCircBuf) : 0);
2305 ichac97R3StreamTearDown(pStream);
2306 }
2307
2308 /* Make sure to leave the lock before (eventually) starting the timer. */
2309 AudioMixerSinkUnlock(pSink);
2310 ichac97R3StreamUnlock(pStreamCC);
2311 LogFunc(("[SD%RU8] fEnable=%RTbool, rc=%Rrc\n", pStream->u8SD, fEnable, rc));
2312 return rc;
2313}
2314
2315
2316/**
2317 * Returns whether an AC'97 stream is enabled or not.
2318 *
2319 * Only used by ichac97R3SaveExec().
2320 *
2321 * @returns VBox status code.
2322 * @param pThisCC The ring-3 AC'97 device state.
2323 * @param pStream Stream to return status for.
2324 */
2325static bool ichac97R3StreamIsEnabled(PAC97STATER3 pThisCC, PAC97STREAM pStream)
2326{
2327 PAUDMIXSINK pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2328 bool fIsEnabled = pSink && (AudioMixerSinkGetStatus(pSink) & AUDMIXSINK_STS_RUNNING);
2329
2330 LogFunc(("[SD%RU8] fIsEnabled=%RTbool\n", pStream->u8SD, fIsEnabled));
2331 return fIsEnabled;
2332}
2333
2334
2335/**
2336 * Terminates an AC'97 audio stream (VM destroy).
2337 *
2338 * This is called by ichac97R3StreamsDestroy during VM poweroff & destruction.
2339 *
2340 * @returns VBox status code.
2341 * @param pThisCC The ring-3 AC'97 state.
2342 * @param pStream The AC'97 stream to destroy (shared).
2343 * @param pStreamCC The AC'97 stream to destroy (ring-3).
2344 * @sa ichac97R3StreamConstruct
2345 */
2346static void ichac97R3StreamDestroy(PAC97STATER3 pThisCC, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
2347{
2348 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2349
2350 ichac97R3StreamTearDown(pStream);
2351
2352 int rc2 = RTCritSectDelete(&pStreamCC->State.CritSect);
2353 AssertRC(rc2);
2354
2355 if (pStreamCC->State.fRegisteredAsyncUpdateJob)
2356 {
2357 PAUDMIXSINK pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2358 if (pSink)
2359 AudioMixerSinkRemoveUpdateJob(pSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC);
2360 pStreamCC->State.fRegisteredAsyncUpdateJob = false;
2361 }
2362
2363 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2364 { /* likely */ }
2365 else
2366 {
2367 AudioHlpFileDestroy(pStreamCC->Dbg.Runtime.pFileStream);
2368 pStreamCC->Dbg.Runtime.pFileStream = NULL;
2369
2370 AudioHlpFileDestroy(pStreamCC->Dbg.Runtime.pFileDMA);
2371 pStreamCC->Dbg.Runtime.pFileDMA = NULL;
2372 }
2373
2374 if (pStreamCC->State.pCircBuf)
2375 {
2376 RTCircBufDestroy(pStreamCC->State.pCircBuf);
2377 pStreamCC->State.pCircBuf = NULL;
2378 }
2379
2380 LogFlowFuncLeave();
2381}
2382
2383
2384/**
2385 * Initializes an AC'97 audio stream (VM construct).
2386 *
2387 * This is only called by ichac97R3Construct.
2388 *
2389 * @returns VBox status code.
2390 * @param pThisCC The ring-3 AC'97 state.
2391 * @param pStream The AC'97 stream to create (shared).
2392 * @param pStreamCC The AC'97 stream to create (ring-3).
2393 * @param u8SD Stream descriptor number to assign.
2394 * @sa ichac97R3StreamDestroy
2395 */
2396static int ichac97R3StreamConstruct(PAC97STATER3 pThisCC, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, uint8_t u8SD)
2397{
2398 LogFunc(("[SD%RU8] pStream=%p\n", u8SD, pStream));
2399
2400 AssertReturn(u8SD < AC97_MAX_STREAMS, VERR_INVALID_PARAMETER);
2401 pStream->u8SD = u8SD;
2402 pStreamCC->u8SD = u8SD;
2403
2404 int rc = RTCritSectInit(&pStreamCC->State.CritSect);
2405 AssertRCReturn(rc, rc);
2406
2407 pStreamCC->Dbg.Runtime.fEnabled = pThisCC->Dbg.fEnabled;
2408
2409 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2410 { /* likely */ }
2411 else
2412 {
2413 char szFile[64];
2414 if (ichac97R3GetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN)
2415 RTStrPrintf(szFile, sizeof(szFile), "ac97StreamWriteSD%RU8", pStream->u8SD);
2416 else
2417 RTStrPrintf(szFile, sizeof(szFile), "ac97StreamReadSD%RU8", pStream->u8SD);
2418
2419 char szPath[RTPATH_MAX];
2420 int rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThisCC->Dbg.pszOutPath, szFile,
2421 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
2422 AssertRC(rc2);
2423 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStreamCC->Dbg.Runtime.pFileStream);
2424 AssertRC(rc2);
2425
2426 if (ichac97R3GetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN)
2427 RTStrPrintf(szFile, sizeof(szFile), "ac97DMAWriteSD%RU8", pStream->u8SD);
2428 else
2429 RTStrPrintf(szFile, sizeof(szFile), "ac97DMAReadSD%RU8", pStream->u8SD);
2430
2431 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThisCC->Dbg.pszOutPath, szFile,
2432 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
2433 AssertRC(rc2);
2434
2435 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStreamCC->Dbg.Runtime.pFileDMA);
2436 AssertRC(rc2);
2437
2438 /* Delete stale debugging files from a former run. */
2439 AudioHlpFileDelete(pStreamCC->Dbg.Runtime.pFileStream);
2440 AudioHlpFileDelete(pStreamCC->Dbg.Runtime.pFileDMA);
2441 }
2442
2443 return rc;
2444}
2445
2446#endif /* IN_RING3 */
2447
2448
2449/*********************************************************************************************************************************
2450* NABM I/O Port Handlers (Global + Stream) *
2451*********************************************************************************************************************************/
2452
2453/**
2454 * @callback_method_impl{FNIOMIOPORTNEWIN}
2455 */
2456static DECLCALLBACK(VBOXSTRICTRC)
2457ichac97IoPortNabmRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2458{
2459 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
2460 RT_NOREF(pvUser);
2461
2462 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
2463
2464 /* Get the index of the NABMBAR port. */
2465 if ( AC97_PORT2IDX_UNMASKED(offPort) < AC97_MAX_STREAMS
2466 && offPort != AC97_GLOB_CNT)
2467 {
2468 PAC97STREAM pStream = &pThis->aStreams[AC97_PORT2IDX(offPort)];
2469
2470 switch (cb)
2471 {
2472 case 1:
2473 switch (offPort & AC97_NABM_OFF_MASK)
2474 {
2475 case AC97_NABM_OFF_CIV:
2476 /* Current Index Value Register */
2477 *pu32 = pStream->Regs.civ;
2478 Log3Func(("CIV[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2479 break;
2480 case AC97_NABM_OFF_LVI:
2481 /* Last Valid Index Register */
2482 *pu32 = pStream->Regs.lvi;
2483 Log3Func(("LVI[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2484 break;
2485 case AC97_NABM_OFF_PIV:
2486 /* Prefetched Index Value Register */
2487 *pu32 = pStream->Regs.piv;
2488 Log3Func(("PIV[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2489 break;
2490 case AC97_NABM_OFF_CR:
2491 /* Control Register */
2492 *pu32 = pStream->Regs.cr;
2493 Log3Func(("CR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2494 break;
2495 case AC97_NABM_OFF_SR:
2496 /* Status Register (lower part) */
2497 *pu32 = RT_LO_U8(pStream->Regs.sr);
2498 Log3Func(("SRb[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2499 break;
2500 default:
2501 *pu32 = UINT32_MAX;
2502 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
2503 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2504 break;
2505 }
2506 break;
2507
2508 case 2:
2509 switch (offPort & AC97_NABM_OFF_MASK)
2510 {
2511 case AC97_NABM_OFF_SR:
2512 /* Status Register */
2513 *pu32 = pStream->Regs.sr;
2514 Log3Func(("SR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2515 break;
2516 case AC97_NABM_OFF_PICB:
2517 /* Position in Current Buffer
2518 * ---
2519 * We can do DMA work here if we want to give the guest a better impression of
2520 * the DMA engine of a real device. For ring-0 we'd have to add some buffering
2521 * to AC97STREAM (4K or so), only going to ring-3 if full. Ring-3 would commit
2522 * that buffer and write directly to the internal DMA pCircBuf.
2523 *
2524 * Checking a Linux guest (knoppix 8.6.2), I see some PIC reads each DMA cycle,
2525 * however most of these happen very very early, 1-10% into the buffer. So, I'm
2526 * not sure if it's worth it, as it'll be a big complication... */
2527#if 1
2528 *pu32 = pStream->Regs.picb;
2529# ifdef LOG_ENABLED
2530 if (LogIs3Enabled())
2531 {
2532 uint64_t offPeriod = PDMDevHlpTimerGet(pDevIns, pStream->hTimer) - pStream->uArmedTs;
2533 Log3Func(("PICB[%d] -> %#x (%RU64 of %RU64 ticks / %RU64%% into DMA period #%RU32)\n",
2534 AC97_PORT2IDX(offPort), *pu32, offPeriod, pStream->cDmaPeriodTicks,
2535 pStream->cDmaPeriodTicks ? offPeriod * 100 / pStream->cDmaPeriodTicks : 0,
2536 pStream->uDmaPeriod));
2537 }
2538# endif
2539#else /* For trying out sub-buffer PICB. Will cause distortions, but can be helpful to see if it help eliminate other issues. */
2540 if ( (pStream->Regs.cr & AC97_CR_RPBM)
2541 && !(pStream->Regs.sr & AC97_SR_DCH)
2542 && pStream->uArmedTs > 0
2543 && pStream->cDmaPeriodTicks > 0)
2544 {
2545 uint64_t const offPeriod = PDMDevHlpTimerGet(pDevIns, pStream->hTimer) - pStream->uArmedTs;
2546 uint32_t cSamples;
2547 if (offPeriod < pStream->cDmaPeriodTicks)
2548 cSamples = pStream->Regs.picb * offPeriod / pStream->cDmaPeriodTicks;
2549 else
2550 cSamples = pStream->Regs.picb;
2551 if (cSamples + 8 < pStream->Regs.picb)
2552 { /* likely */ }
2553 else if (pStream->Regs.picb > 8)
2554 cSamples = pStream->Regs.picb - 8;
2555 else
2556 cSamples = 0;
2557 *pu32 = pStream->Regs.picb - cSamples;
2558 Log3Func(("PICB[%d] -> %#x (PICB=%#x cSamples=%#x offPeriod=%RU64 of %RU64 / %RU64%%)\n",
2559 AC97_PORT2IDX(offPort), *pu32, pStream->Regs.picb, cSamples, offPeriod,
2560 pStream->cDmaPeriodTicks, offPeriod * 100 / pStream->cDmaPeriodTicks));
2561 }
2562 else
2563 {
2564 *pu32 = pStream->Regs.picb;
2565 Log3Func(("PICB[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2566 }
2567#endif
2568 break;
2569 default:
2570 *pu32 = UINT32_MAX;
2571 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort));
2572 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2573 break;
2574 }
2575 break;
2576
2577 case 4:
2578 switch (offPort & AC97_NABM_OFF_MASK)
2579 {
2580 case AC97_NABM_OFF_BDBAR:
2581 /* Buffer Descriptor Base Address Register */
2582 *pu32 = pStream->Regs.bdbar;
2583 Log3Func(("BMADDR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2584 break;
2585 case AC97_NABM_OFF_CIV:
2586 /* 32-bit access: Current Index Value Register +
2587 * Last Valid Index Register +
2588 * Status Register */
2589 *pu32 = pStream->Regs.civ | ((uint32_t)pStream->Regs.lvi << 8) | ((uint32_t)pStream->Regs.sr << 16);
2590 Log3Func(("CIV LVI SR[%d] -> %#x, %#x, %#x\n",
2591 AC97_PORT2IDX(offPort), pStream->Regs.civ, pStream->Regs.lvi, pStream->Regs.sr));
2592 break;
2593 case AC97_NABM_OFF_PICB:
2594 /* 32-bit access: Position in Current Buffer Register +
2595 * Prefetched Index Value Register +
2596 * Control Register */
2597 *pu32 = pStream->Regs.picb | ((uint32_t)pStream->Regs.piv << 16) | ((uint32_t)pStream->Regs.cr << 24);
2598 Log3Func(("PICB PIV CR[%d] -> %#x %#x %#x %#x\n",
2599 AC97_PORT2IDX(offPort), *pu32, pStream->Regs.picb, pStream->Regs.piv, pStream->Regs.cr));
2600 break;
2601
2602 default:
2603 *pu32 = UINT32_MAX;
2604 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
2605 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2606 break;
2607 }
2608 break;
2609
2610 default:
2611 DEVAC97_UNLOCK(pDevIns, pThis);
2612 AssertFailed();
2613 return VERR_IOM_IOPORT_UNUSED;
2614 }
2615 }
2616 else
2617 {
2618 switch (cb)
2619 {
2620 case 1:
2621 switch (offPort)
2622 {
2623 case AC97_CAS:
2624 /* Codec Access Semaphore Register */
2625 Log3Func(("CAS %d\n", pThis->cas));
2626 *pu32 = pThis->cas;
2627 pThis->cas = 1;
2628 break;
2629 default:
2630 *pu32 = UINT32_MAX;
2631 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
2632 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2633 break;
2634 }
2635 break;
2636
2637 case 2:
2638 *pu32 = UINT32_MAX;
2639 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort));
2640 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2641 break;
2642
2643 case 4:
2644 switch (offPort)
2645 {
2646 case AC97_GLOB_CNT:
2647 /* Global Control */
2648 *pu32 = pThis->glob_cnt;
2649 Log3Func(("glob_cnt -> %#x\n", *pu32));
2650 break;
2651 case AC97_GLOB_STA:
2652 /* Global Status */
2653 *pu32 = pThis->glob_sta | AC97_GS_S0CR;
2654 Log3Func(("glob_sta -> %#x\n", *pu32));
2655 break;
2656 default:
2657 *pu32 = UINT32_MAX;
2658 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
2659 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2660 break;
2661 }
2662 break;
2663
2664 default:
2665 DEVAC97_UNLOCK(pDevIns, pThis);
2666 AssertFailed();
2667 return VERR_IOM_IOPORT_UNUSED;
2668 }
2669 }
2670
2671 DEVAC97_UNLOCK(pDevIns, pThis);
2672 return VINF_SUCCESS;
2673}
2674
2675
2676/**
2677 * @callback_method_impl{FNIOMIOPORTNEWOUT}
2678 */
2679static DECLCALLBACK(VBOXSTRICTRC)
2680ichac97IoPortNabmWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2681{
2682 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
2683#ifdef IN_RING3
2684 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
2685#endif
2686 RT_NOREF(pvUser);
2687
2688 VBOXSTRICTRC rc = VINF_SUCCESS;
2689 if ( AC97_PORT2IDX_UNMASKED(offPort) < AC97_MAX_STREAMS
2690 && offPort != AC97_GLOB_CNT)
2691 {
2692#ifdef IN_RING3
2693 PAC97STREAMR3 pStreamCC = &pThisCC->aStreams[AC97_PORT2IDX(offPort)];
2694#endif
2695 PAC97STREAM pStream = &pThis->aStreams[AC97_PORT2IDX(offPort)];
2696
2697 switch (cb)
2698 {
2699 case 1:
2700 switch (offPort & AC97_NABM_OFF_MASK)
2701 {
2702 /*
2703 * Last Valid Index.
2704 */
2705 case AC97_NABM_OFF_LVI:
2706 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2707
2708 if ( !(pStream->Regs.sr & AC97_SR_DCH)
2709 || !(pStream->Regs.cr & AC97_CR_RPBM))
2710 {
2711 pStream->Regs.lvi = u32 % AC97_MAX_BDLE;
2712 STAM_REL_COUNTER_INC(&pStream->StatWriteLvi);
2713 DEVAC97_UNLOCK(pDevIns, pThis);
2714 Log3Func(("[SD%RU8] LVI <- %#x\n", pStream->u8SD, u32));
2715 }
2716 else
2717 {
2718#ifdef IN_RING3
2719 /* Recover from underflow situation where CIV caught up with LVI
2720 and the DMA processing stopped. We clear the status condition,
2721 update LVI and then try to load the next BDLE. Unfortunately,
2722 we cannot do this from ring-3 as much of the BDLE state is
2723 ring-3 only. */
2724 pStream->Regs.sr &= ~(AC97_SR_DCH | AC97_SR_CELV);
2725 pStream->Regs.lvi = u32 % AC97_MAX_BDLE;
2726 if (ichac97R3StreamFetchNextBdle(pDevIns, pStream, pStreamCC))
2727 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr | AC97_SR_BCIS);
2728
2729 /* We now have to re-arm the DMA timer according to the new BDLE length.
2730 This means leaving the device lock to avoid virtual sync lock order issues. */
2731 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
2732 uint64_t const cTicksToDeadline = pStream->cDmaPeriodTicks;
2733
2734 /** @todo Stop the DMA timer when we get into the AC97_SR_CELV situation to
2735 * avoid potential race here. */
2736 STAM_REL_COUNTER_INC(&pStreamCC->State.StatWriteLviRecover);
2737 DEVAC97_UNLOCK(pDevIns, pThis);
2738
2739 LogFunc(("[SD%RU8] LVI <- %#x; CIV=%#x PIV=%#x SR=%#x cTicksToDeadline=%#RX64 [recovering]\n",
2740 pStream->u8SD, u32, pStream->Regs.civ, pStream->Regs.piv, pStream->Regs.sr, cTicksToDeadline));
2741
2742 int rc2 = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimer, cTicksToDeadline, &pStream->uArmedTs);
2743 AssertRC(rc2);
2744#else
2745 rc = VINF_IOM_R3_IOPORT_WRITE;
2746#endif
2747 }
2748 break;
2749
2750 /*
2751 * Control Registers.
2752 */
2753 case AC97_NABM_OFF_CR:
2754 {
2755#ifdef IN_RING3
2756 DEVAC97_LOCK(pDevIns, pThis);
2757 STAM_REL_COUNTER_INC(&pStreamCC->State.StatWriteCr);
2758
2759 uint32_t const fCrChanged = pStream->Regs.cr ^ u32;
2760 Log3Func(("[SD%RU8] CR <- %#x (was %#x; changed %#x)\n", pStream->u8SD, u32, pStream->Regs.cr, fCrChanged));
2761
2762 /*
2763 * Busmaster reset.
2764 */
2765 if (u32 & AC97_CR_RR)
2766 {
2767 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatReset, r);
2768 LogFunc(("[SD%RU8] Reset\n", pStream->u8SD));
2769
2770 /* Make sure that Run/Pause Bus Master bit (RPBM) is cleared (0).
2771 3.2.7 in 302349-003 says RPBM be must be clear when resetting
2772 and that behavior is undefined if it's set. */
2773 ASSERT_GUEST_STMT((pStream->Regs.cr & AC97_CR_RPBM) == 0,
2774 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream,
2775 pStreamCC, false /* fEnable */));
2776
2777 ichac97R3StreamReset(pThis, pStream, pStreamCC);
2778
2779 ichac97StreamUpdateSR(pDevIns, pThis, pStream, AC97_SR_DCH); /** @todo Do we need to do that? */
2780
2781 DEVAC97_UNLOCK(pDevIns, pThis);
2782 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReset, r);
2783 break;
2784 }
2785
2786 /*
2787 * Write the new value to the register and if RPBM didn't change we're done.
2788 */
2789 pStream->Regs.cr = u32 & AC97_CR_VALID_MASK;
2790
2791 if (!(fCrChanged & AC97_CR_RPBM))
2792 DEVAC97_UNLOCK(pDevIns, pThis); /* Probably not so likely, but avoid one extra intentation level. */
2793 /*
2794 * Pause busmaster.
2795 */
2796 else if (!(pStream->Regs.cr & AC97_CR_RPBM))
2797 {
2798 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatStop, p);
2799 LogFunc(("[SD%RU8] Pause busmaster (disable stream) SR=%#x -> %#x\n",
2800 pStream->u8SD, pStream->Regs.sr, pStream->Regs.sr | AC97_SR_DCH));
2801 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, false /* fEnable */);
2802 pStream->Regs.sr |= AC97_SR_DCH;
2803
2804 DEVAC97_UNLOCK(pDevIns, pThis);
2805 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatStop, p);
2806 }
2807 /*
2808 * Run busmaster.
2809 */
2810 else
2811 {
2812 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatStart, r);
2813 LogFunc(("[SD%RU8] Run busmaster (enable stream) SR=%#x -> %#x\n",
2814 pStream->u8SD, pStream->Regs.sr, pStream->Regs.sr & ~AC97_SR_DCH));
2815 pStream->Regs.sr &= ~AC97_SR_DCH;
2816
2817 if (ichac97R3StreamFetchNextBdle(pDevIns, pStream, pStreamCC))
2818 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr | AC97_SR_BCIS);
2819# ifdef LOG_ENABLED
2820 if (LogIsFlowEnabled())
2821 ichac97R3DbgPrintBdl(pDevIns, pThis, pStream, DBGFR3InfoLogHlp(), "ichac97IoPortNabmWrite: ");
2822# endif
2823 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, true /* fEnable */);
2824
2825 /*
2826 * Arm the DMA timer. Must drop the AC'97 device lock first as it would
2827 * create a lock order violation with the virtual sync time lock otherwise.
2828 */
2829 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
2830 uint64_t const cTicksToDeadline = pStream->cDmaPeriodTicks;
2831
2832 DEVAC97_UNLOCK(pDevIns, pThis);
2833
2834 /** @todo for output streams we could probably service this a little bit
2835 * earlier if we push it, just to reduce the lag... For HDA we do a
2836 * DMA run immediately after the stream is enabled. */
2837 int rc2 = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimer, cTicksToDeadline, &pStream->uArmedTs);
2838 AssertRC(rc2);
2839
2840 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatStart, r);
2841 }
2842#else /* !IN_RING3 */
2843 rc = VINF_IOM_R3_IOPORT_WRITE;
2844#endif
2845 break;
2846 }
2847
2848 /*
2849 * Status Registers.
2850 */
2851 case AC97_NABM_OFF_SR:
2852 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2853 ichac97StreamWriteSR(pDevIns, pThis, pStream, u32);
2854 STAM_REL_COUNTER_INC(&pStream->StatWriteSr1);
2855 DEVAC97_UNLOCK(pDevIns, pThis);
2856 break;
2857
2858 default:
2859 /* Linux tries to write CIV. */
2860 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x%s <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n",
2861 offPort, (offPort & AC97_NABM_OFF_MASK) == AC97_NABM_OFF_CIV ? " (CIV)" : "" , u32));
2862 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2863 break;
2864 }
2865 break;
2866
2867 case 2:
2868 switch (offPort & AC97_NABM_OFF_MASK)
2869 {
2870 case AC97_NABM_OFF_SR:
2871 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2872 ichac97StreamWriteSR(pDevIns, pThis, pStream, u32);
2873 STAM_REL_COUNTER_INC(&pStream->StatWriteSr2);
2874 DEVAC97_UNLOCK(pDevIns, pThis);
2875 break;
2876 default:
2877 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2878 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2879 break;
2880 }
2881 break;
2882
2883 case 4:
2884 switch (offPort & AC97_NABM_OFF_MASK)
2885 {
2886 case AC97_NABM_OFF_BDBAR:
2887 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2888 /* Buffer Descriptor list Base Address Register */
2889 pStream->Regs.bdbar = u32 & ~(uint32_t)3;
2890 Log3Func(("[SD%RU8] BDBAR <- %#x (bdbar %#x)\n", AC97_PORT2IDX(offPort), u32, pStream->Regs.bdbar));
2891 STAM_REL_COUNTER_INC(&pStream->StatWriteBdBar);
2892 DEVAC97_UNLOCK(pDevIns, pThis);
2893 break;
2894 default:
2895 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2896 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2897 break;
2898 }
2899 break;
2900
2901 default:
2902 AssertMsgFailed(("offPort=%#x <- %#x LB %u\n", offPort, u32, cb));
2903 break;
2904 }
2905 }
2906 else
2907 {
2908 switch (cb)
2909 {
2910 case 1:
2911 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2912 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2913 break;
2914
2915 case 2:
2916 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2917 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2918 break;
2919
2920 case 4:
2921 switch (offPort)
2922 {
2923 case AC97_GLOB_CNT:
2924 /* Global Control */
2925 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2926 if (u32 & AC97_GC_WR)
2927 ichac97WarmReset(pThis);
2928 if (u32 & AC97_GC_CR)
2929 ichac97ColdReset(pThis);
2930 if (!(u32 & (AC97_GC_WR | AC97_GC_CR)))
2931 pThis->glob_cnt = u32 & AC97_GC_VALID_MASK;
2932 Log3Func(("glob_cnt <- %#x (glob_cnt %#x)\n", u32, pThis->glob_cnt));
2933 DEVAC97_UNLOCK(pDevIns, pThis);
2934 break;
2935 case AC97_GLOB_STA:
2936 /* Global Status */
2937 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2938 pThis->glob_sta &= ~(u32 & AC97_GS_WCLEAR_MASK);
2939 pThis->glob_sta |= (u32 & ~(AC97_GS_WCLEAR_MASK | AC97_GS_RO_MASK)) & AC97_GS_VALID_MASK;
2940 Log3Func(("glob_sta <- %#x (glob_sta %#x)\n", u32, pThis->glob_sta));
2941 DEVAC97_UNLOCK(pDevIns, pThis);
2942 break;
2943 default:
2944 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2945 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2946 break;
2947 }
2948 break;
2949
2950 default:
2951 AssertMsgFailed(("offPort=%#x <- %#x LB %u\n", offPort, u32, cb));
2952 break;
2953 }
2954 }
2955
2956 return rc;
2957}
2958
2959
2960/*********************************************************************************************************************************
2961* Mixer & NAM I/O handlers *
2962*********************************************************************************************************************************/
2963
2964/**
2965 * Sets a AC'97 mixer control to a specific value.
2966 *
2967 * @returns VBox status code.
2968 * @param pThis The shared AC'97 state.
2969 * @param uMixerIdx Mixer control to set value for.
2970 * @param uVal Value to set.
2971 */
2972static void ichac97MixerSet(PAC97STATE pThis, uint8_t uMixerIdx, uint16_t uVal)
2973{
2974 AssertMsgReturnVoid(uMixerIdx + 2U <= sizeof(pThis->mixer_data),
2975 ("Index %RU8 out of bounds (%zu)\n", uMixerIdx, sizeof(pThis->mixer_data)));
2976
2977 LogRel2(("AC97: Setting mixer index #%RU8 to %RU16 (%RU8 %RU8)\n", uMixerIdx, uVal, RT_HI_U8(uVal), RT_LO_U8(uVal)));
2978
2979 pThis->mixer_data[uMixerIdx + 0] = RT_LO_U8(uVal);
2980 pThis->mixer_data[uMixerIdx + 1] = RT_HI_U8(uVal);
2981}
2982
2983
2984/**
2985 * Gets a value from a specific AC'97 mixer control.
2986 *
2987 * @returns Retrieved mixer control value.
2988 * @param pThis The shared AC'97 state.
2989 * @param uMixerIdx Mixer control to get value for.
2990 */
2991static uint16_t ichac97MixerGet(PAC97STATE pThis, uint32_t uMixerIdx)
2992{
2993 AssertMsgReturn(uMixerIdx + 2U <= sizeof(pThis->mixer_data),
2994 ("Index %RU8 out of bounds (%zu)\n", uMixerIdx, sizeof(pThis->mixer_data)),
2995 UINT16_MAX);
2996 return RT_MAKE_U16(pThis->mixer_data[uMixerIdx + 0], pThis->mixer_data[uMixerIdx + 1]);
2997}
2998
2999#ifdef IN_RING3
3000
3001/**
3002 * Sets the volume of a specific AC'97 mixer control.
3003 *
3004 * This currently only supports attenuation -- gain support is currently not implemented.
3005 *
3006 * @returns VBox status code.
3007 * @param pThis The shared AC'97 state.
3008 * @param pThisCC The ring-3 AC'97 state.
3009 * @param index AC'97 mixer index to set volume for.
3010 * @param enmMixerCtl Corresponding audio mixer sink.
3011 * @param uVal Volume value to set.
3012 */
3013static int ichac97R3MixerSetVolume(PAC97STATE pThis, PAC97STATER3 pThisCC, int index, PDMAUDIOMIXERCTL enmMixerCtl, uint32_t uVal)
3014{
3015 /*
3016 * From AC'97 SoundMax Codec AD1981A/AD1981B:
3017 * "Because AC '97 defines 6-bit volume registers, to maintain compatibility whenever the
3018 * D5 or D13 bits are set to 1, their respective lower five volume bits are automatically
3019 * set to 1 by the Codec logic. On readback, all lower 5 bits will read ones whenever
3020 * these bits are set to 1."
3021 *
3022 * Linux ALSA depends on this behavior to detect that only 5 bits are used for volume
3023 * control and the optional 6th bit is not used. Note that this logic only applies to the
3024 * master volume controls.
3025 */
3026 if ( index == AC97_Master_Volume_Mute
3027 || index == AC97_Headphone_Volume_Mute
3028 || index == AC97_Master_Volume_Mono_Mute)
3029 {
3030 if (uVal & RT_BIT(5)) /* D5 bit set? */
3031 uVal |= RT_BIT(4) | RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0);
3032 if (uVal & RT_BIT(13)) /* D13 bit set? */
3033 uVal |= RT_BIT(12) | RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8);
3034 }
3035
3036 const bool fCtlMuted = (uVal >> AC97_BARS_VOL_MUTE_SHIFT) & 1;
3037 uint8_t uCtlAttLeft = (uVal >> 8) & AC97_BARS_VOL_MASK;
3038 uint8_t uCtlAttRight = uVal & AC97_BARS_VOL_MASK;
3039
3040 /* For the master and headphone volume, 0 corresponds to 0dB attenuation. For the other
3041 * volume controls, 0 means 12dB gain and 8 means unity gain.
3042 */
3043 if (index != AC97_Master_Volume_Mute && index != AC97_Headphone_Volume_Mute)
3044 {
3045# ifndef VBOX_WITH_AC97_GAIN_SUPPORT
3046 /* NB: Currently there is no gain support, only attenuation. */
3047 uCtlAttLeft = uCtlAttLeft < 8 ? 0 : uCtlAttLeft - 8;
3048 uCtlAttRight = uCtlAttRight < 8 ? 0 : uCtlAttRight - 8;
3049# endif
3050 }
3051 Assert(uCtlAttLeft <= 255 / AC97_DB_FACTOR);
3052 Assert(uCtlAttRight <= 255 / AC97_DB_FACTOR);
3053
3054 LogFunc(("index=0x%x, uVal=%RU32, enmMixerCtl=%RU32\n", index, uVal, enmMixerCtl));
3055 LogFunc(("uCtlAttLeft=%RU8, uCtlAttRight=%RU8 ", uCtlAttLeft, uCtlAttRight));
3056
3057 /*
3058 * For AC'97 volume controls, each additional step means -1.5dB attenuation with
3059 * zero being maximum. In contrast, we're internally using 255 (PDMAUDIO_VOLUME_MAX)
3060 * steps, each -0.375dB, where 0 corresponds to -96dB and 255 corresponds to 0dB.
3061 */
3062 uint8_t lVol = PDMAUDIO_VOLUME_MAX - uCtlAttLeft * AC97_DB_FACTOR;
3063 uint8_t rVol = PDMAUDIO_VOLUME_MAX - uCtlAttRight * AC97_DB_FACTOR;
3064
3065 Log(("-> fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n", fCtlMuted, lVol, rVol));
3066
3067 int rc = VINF_SUCCESS;
3068
3069 if (pThisCC->pMixer) /* Device can be in reset state, so no mixer available. */
3070 {
3071 PDMAUDIOVOLUME Vol;
3072 PDMAudioVolumeInitFromStereo(&Vol, fCtlMuted, lVol, rVol);
3073
3074 PAUDMIXSINK pSink = NULL;
3075 switch (enmMixerCtl)
3076 {
3077 case PDMAUDIOMIXERCTL_VOLUME_MASTER:
3078 rc = AudioMixerSetMasterVolume(pThisCC->pMixer, &Vol);
3079 break;
3080
3081 case PDMAUDIOMIXERCTL_FRONT:
3082 pSink = pThisCC->pSinkOut;
3083 break;
3084
3085 case PDMAUDIOMIXERCTL_MIC_IN:
3086 case PDMAUDIOMIXERCTL_LINE_IN:
3087 /* These are recognized but do nothing. */
3088 break;
3089
3090 default:
3091 AssertFailed();
3092 rc = VERR_NOT_SUPPORTED;
3093 break;
3094 }
3095
3096 if (pSink)
3097 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3098 }
3099
3100 ichac97MixerSet(pThis, index, uVal);
3101
3102 if (RT_FAILURE(rc))
3103 LogFlowFunc(("Failed with %Rrc\n", rc));
3104
3105 return rc;
3106}
3107
3108/**
3109 * Sets the gain of a specific AC'97 recording control.
3110 *
3111 * @note Gain support is currently not implemented in PDM audio.
3112 *
3113 * @returns VBox status code.
3114 * @param pThis The shared AC'97 state.
3115 * @param pThisCC The ring-3 AC'97 state.
3116 * @param index AC'97 mixer index to set volume for.
3117 * @param enmMixerCtl Corresponding audio mixer sink.
3118 * @param uVal Volume value to set.
3119 */
3120static int ichac97R3MixerSetGain(PAC97STATE pThis, PAC97STATER3 pThisCC, int index, PDMAUDIOMIXERCTL enmMixerCtl, uint32_t uVal)
3121{
3122 /*
3123 * For AC'97 recording controls, each additional step means +1.5dB gain with
3124 * zero being 0dB gain and 15 being +22.5dB gain.
3125 */
3126 bool const fCtlMuted = (uVal >> AC97_BARS_VOL_MUTE_SHIFT) & 1;
3127 uint8_t uCtlGainLeft = (uVal >> 8) & AC97_BARS_GAIN_MASK;
3128 uint8_t uCtlGainRight = uVal & AC97_BARS_GAIN_MASK;
3129
3130 Assert(uCtlGainLeft <= 255 / AC97_DB_FACTOR);
3131 Assert(uCtlGainRight <= 255 / AC97_DB_FACTOR);
3132
3133 LogFunc(("index=0x%x, uVal=%RU32, enmMixerCtl=%RU32\n", index, uVal, enmMixerCtl));
3134 LogFunc(("uCtlGainLeft=%RU8, uCtlGainRight=%RU8 ", uCtlGainLeft, uCtlGainRight));
3135
3136 uint8_t lVol = PDMAUDIO_VOLUME_MAX + uCtlGainLeft * AC97_DB_FACTOR;
3137 uint8_t rVol = PDMAUDIO_VOLUME_MAX + uCtlGainRight * AC97_DB_FACTOR;
3138
3139 /* We do not currently support gain. Since AC'97 does not support attenuation
3140 * for the recording input, the best we can do is set the maximum volume.
3141 */
3142# ifndef VBOX_WITH_AC97_GAIN_SUPPORT
3143 /* NB: Currently there is no gain support, only attenuation. Since AC'97 does not
3144 * support attenuation for the recording inputs, the best we can do is set the
3145 * maximum volume.
3146 */
3147 lVol = rVol = PDMAUDIO_VOLUME_MAX;
3148# endif
3149
3150 Log(("-> fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n", fCtlMuted, lVol, rVol));
3151
3152 int rc = VINF_SUCCESS;
3153
3154 if (pThisCC->pMixer) /* Device can be in reset state, so no mixer available. */
3155 {
3156 PDMAUDIOVOLUME Vol;
3157 PDMAudioVolumeInitFromStereo(&Vol, fCtlMuted, lVol, rVol);
3158
3159 PAUDMIXSINK pSink = NULL;
3160 switch (enmMixerCtl)
3161 {
3162 case PDMAUDIOMIXERCTL_MIC_IN:
3163 pSink = pThisCC->pSinkMicIn;
3164 break;
3165
3166 case PDMAUDIOMIXERCTL_LINE_IN:
3167 pSink = pThisCC->pSinkLineIn;
3168 break;
3169
3170 default:
3171 AssertFailed();
3172 rc = VERR_NOT_SUPPORTED;
3173 break;
3174 }
3175
3176 if (pSink)
3177 {
3178 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3179 /* There is only one AC'97 recording gain control. If line in
3180 * is changed, also update the microphone. If the optional dedicated
3181 * microphone is changed, only change that.
3182 * NB: The codecs we support do not have the dedicated microphone control.
3183 */
3184 if (pSink == pThisCC->pSinkLineIn && pThisCC->pSinkMicIn)
3185 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3186 }
3187 }
3188
3189 ichac97MixerSet(pThis, index, uVal);
3190
3191 if (RT_FAILURE(rc))
3192 LogFlowFunc(("Failed with %Rrc\n", rc));
3193
3194 return rc;
3195}
3196
3197
3198/**
3199 * Converts an AC'97 recording source index to a PDM audio recording source.
3200 *
3201 * @returns PDM audio recording source.
3202 * @param uIdx AC'97 index to convert.
3203 */
3204static PDMAUDIOPATH ichac97R3IdxToRecSource(uint8_t uIdx)
3205{
3206 switch (uIdx)
3207 {
3208 case AC97_REC_MIC: return PDMAUDIOPATH_IN_MIC;
3209 case AC97_REC_CD: return PDMAUDIOPATH_IN_CD;
3210 case AC97_REC_VIDEO: return PDMAUDIOPATH_IN_VIDEO;
3211 case AC97_REC_AUX: return PDMAUDIOPATH_IN_AUX;
3212 case AC97_REC_LINE_IN: return PDMAUDIOPATH_IN_LINE;
3213 case AC97_REC_PHONE: return PDMAUDIOPATH_IN_PHONE;
3214 default:
3215 break;
3216 }
3217
3218 LogFlowFunc(("Unknown record source %d, using MIC\n", uIdx));
3219 return PDMAUDIOPATH_IN_MIC;
3220}
3221
3222
3223/**
3224 * Converts a PDM audio recording source to an AC'97 recording source index.
3225 *
3226 * @returns AC'97 recording source index.
3227 * @param enmRecSrc PDM audio recording source to convert.
3228 */
3229static uint8_t ichac97R3RecSourceToIdx(PDMAUDIOPATH enmRecSrc)
3230{
3231 switch (enmRecSrc)
3232 {
3233 case PDMAUDIOPATH_IN_MIC: return AC97_REC_MIC;
3234 case PDMAUDIOPATH_IN_CD: return AC97_REC_CD;
3235 case PDMAUDIOPATH_IN_VIDEO: return AC97_REC_VIDEO;
3236 case PDMAUDIOPATH_IN_AUX: return AC97_REC_AUX;
3237 case PDMAUDIOPATH_IN_LINE: return AC97_REC_LINE_IN;
3238 case PDMAUDIOPATH_IN_PHONE: return AC97_REC_PHONE;
3239 default:
3240 AssertMsgFailedBreak(("%d\n", enmRecSrc));
3241 }
3242
3243 LogFlowFunc(("Unknown audio recording source %d using MIC\n", enmRecSrc));
3244 return AC97_REC_MIC;
3245}
3246
3247
3248/**
3249 * Performs an AC'97 mixer record select to switch to a different recording
3250 * source.
3251 *
3252 * @param pThis The shared AC'97 state.
3253 * @param val AC'97 recording source index to set.
3254 */
3255static void ichac97R3MixerRecordSelect(PAC97STATE pThis, uint32_t val)
3256{
3257 uint8_t rs = val & AC97_REC_MASK;
3258 uint8_t ls = (val >> 8) & AC97_REC_MASK;
3259
3260 PDMAUDIOPATH const ars = ichac97R3IdxToRecSource(rs);
3261 PDMAUDIOPATH const als = ichac97R3IdxToRecSource(ls);
3262
3263 rs = ichac97R3RecSourceToIdx(ars);
3264 ls = ichac97R3RecSourceToIdx(als);
3265
3266 LogRel(("AC97: Record select to left=%s, right=%s\n", PDMAudioPathGetName(ars), PDMAudioPathGetName(als)));
3267
3268 ichac97MixerSet(pThis, AC97_Record_Select, rs | (ls << 8));
3269}
3270
3271/**
3272 * Resets the AC'97 mixer.
3273 *
3274 * @returns VBox status code.
3275 * @param pThis The shared AC'97 state.
3276 * @param pThisCC The ring-3 AC'97 state.
3277 */
3278static int ichac97R3MixerReset(PAC97STATE pThis, PAC97STATER3 pThisCC)
3279{
3280 LogFlowFuncEnter();
3281
3282 RT_ZERO(pThis->mixer_data);
3283
3284 /* Note: Make sure to reset all registers first before bailing out on error. */
3285
3286 ichac97MixerSet(pThis, AC97_Reset , 0x0000); /* 6940 */
3287 ichac97MixerSet(pThis, AC97_Master_Volume_Mono_Mute , 0x8000);
3288 ichac97MixerSet(pThis, AC97_PC_BEEP_Volume_Mute , 0x0000);
3289
3290 ichac97MixerSet(pThis, AC97_Phone_Volume_Mute , 0x8008);
3291 ichac97MixerSet(pThis, AC97_Mic_Volume_Mute , 0x8008);
3292 ichac97MixerSet(pThis, AC97_CD_Volume_Mute , 0x8808);
3293 ichac97MixerSet(pThis, AC97_Aux_Volume_Mute , 0x8808);
3294 ichac97MixerSet(pThis, AC97_Record_Gain_Mic_Mute , 0x8000);
3295 ichac97MixerSet(pThis, AC97_General_Purpose , 0x0000);
3296 ichac97MixerSet(pThis, AC97_3D_Control , 0x0000);
3297 ichac97MixerSet(pThis, AC97_Powerdown_Ctrl_Stat , 0x000f);
3298
3299 /* Configure Extended Audio ID (EAID) + Control & Status (EACS) registers. */
3300 const uint16_t fEAID = AC97_EAID_REV1 | AC97_EACS_VRA | AC97_EACS_VRM; /* Our hardware is AC'97 rev2.3 compliant. */
3301 const uint16_t fEACS = AC97_EACS_VRA | AC97_EACS_VRM; /* Variable Rate PCM Audio (VRA) + Mic-In (VRM) capable. */
3302
3303 LogRel(("AC97: Mixer reset (EAID=0x%x, EACS=0x%x)\n", fEAID, fEACS));
3304
3305 ichac97MixerSet(pThis, AC97_Extended_Audio_ID, fEAID);
3306 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, fEACS);
3307 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3308 ichac97MixerSet(pThis, AC97_PCM_Surround_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3309 ichac97MixerSet(pThis, AC97_PCM_LFE_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3310 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate , 0xbb80 /* 48000 Hz by default */);
3311 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate , 0xbb80 /* 48000 Hz by default */);
3312
3313 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3314 {
3315 /* Analog Devices 1980 (AD1980) */
3316 ichac97MixerSet(pThis, AC97_Reset , 0x0010); /* Headphones. */
3317 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
3318 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5370);
3319 ichac97MixerSet(pThis, AC97_Headphone_Volume_Mute , 0x8000);
3320 }
3321 else if (pThis->enmCodecModel == AC97CODEC_AD1981B)
3322 {
3323 /* Analog Devices 1981B (AD1981B) */
3324 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
3325 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5374);
3326 }
3327 else
3328 {
3329 /* Sigmatel 9700 (STAC9700) */
3330 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x8384);
3331 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x7600); /* 7608 */
3332 }
3333 ichac97R3MixerRecordSelect(pThis, 0);
3334
3335 /* The default value is 8000h, which corresponds to 0 dB attenuation with mute on. */
3336 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER, 0x8000);
3337
3338 /* The default value for stereo registers is 8808h, which corresponds to 0 dB gain with mute on.*/
3339 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT, 0x8808);
3340 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8808);
3341 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Mic_Volume_Mute, PDMAUDIOMIXERCTL_MIC_IN, 0x8008);
3342
3343 /* The default for record controls is 0 dB gain with mute on. */
3344 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8000);
3345 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mic_Mute, PDMAUDIOMIXERCTL_MIC_IN, 0x8000);
3346
3347 return VINF_SUCCESS;
3348}
3349
3350#endif /* IN_RING3 */
3351
3352/**
3353 * @callback_method_impl{FNIOMIOPORTNEWIN}
3354 */
3355static DECLCALLBACK(VBOXSTRICTRC)
3356ichac97IoPortNamRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3357{
3358 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3359 RT_NOREF(pvUser);
3360 Assert(offPort < 256);
3361
3362 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
3363
3364 VBOXSTRICTRC rc = VINF_SUCCESS;
3365 switch (cb)
3366 {
3367 case 1:
3368 LogRel2(("AC97: Warning: Unimplemented NAM read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
3369 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamReads);
3370 pThis->cas = 0;
3371 *pu32 = UINT32_MAX;
3372 break;
3373
3374 case 2:
3375 pThis->cas = 0;
3376 *pu32 = ichac97MixerGet(pThis, offPort);
3377 break;
3378
3379 case 4:
3380 LogRel2(("AC97: Warning: Unimplemented NAM read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
3381 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamReads);
3382 pThis->cas = 0;
3383 *pu32 = UINT32_MAX;
3384 break;
3385
3386 default:
3387 AssertFailed();
3388 rc = VERR_IOM_IOPORT_UNUSED;
3389 break;
3390 }
3391
3392 DEVAC97_UNLOCK(pDevIns, pThis);
3393 return rc;
3394}
3395
3396/**
3397 * @callback_method_impl{FNIOMIOPORTNEWOUT}
3398 */
3399static DECLCALLBACK(VBOXSTRICTRC)
3400ichac97IoPortNamWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3401{
3402 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3403#ifdef IN_RING3
3404 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3405#endif
3406 RT_NOREF(pvUser);
3407
3408 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
3409
3410 VBOXSTRICTRC rc = VINF_SUCCESS;
3411 switch (cb)
3412 {
3413 case 1:
3414 LogRel2(("AC97: Warning: Unimplemented NAM write offPort=%#x <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3415 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3416 pThis->cas = 0;
3417 break;
3418
3419 case 2:
3420 {
3421 pThis->cas = 0;
3422 switch (offPort)
3423 {
3424 case AC97_Reset:
3425#ifdef IN_RING3
3426 ichac97R3Reset(pDevIns);
3427#else
3428 rc = VINF_IOM_R3_IOPORT_WRITE;
3429#endif
3430 break;
3431 case AC97_Powerdown_Ctrl_Stat:
3432 u32 &= ~0xf;
3433 u32 |= ichac97MixerGet(pThis, offPort) & 0xf;
3434 ichac97MixerSet(pThis, offPort, u32);
3435 break;
3436 case AC97_Master_Volume_Mute:
3437 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3438 {
3439 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_LOSEL)
3440 break; /* Register controls surround (rear), do nothing. */
3441 }
3442#ifdef IN_RING3
3443 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_VOLUME_MASTER, u32);
3444#else
3445 rc = VINF_IOM_R3_IOPORT_WRITE;
3446#endif
3447 break;
3448 case AC97_Headphone_Volume_Mute:
3449 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3450 {
3451 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_HPSEL)
3452 {
3453 /* Register controls PCM (front) outputs. */
3454#ifdef IN_RING3
3455 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_VOLUME_MASTER, u32);
3456#else
3457 rc = VINF_IOM_R3_IOPORT_WRITE;
3458#endif
3459 }
3460 }
3461 break;
3462 case AC97_PCM_Out_Volume_Mute:
3463#ifdef IN_RING3
3464 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_FRONT, u32);
3465#else
3466 rc = VINF_IOM_R3_IOPORT_WRITE;
3467#endif
3468 break;
3469 case AC97_Line_In_Volume_Mute:
3470#ifdef IN_RING3
3471 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_LINE_IN, u32);
3472#else
3473 rc = VINF_IOM_R3_IOPORT_WRITE;
3474#endif
3475 break;
3476 case AC97_Record_Select:
3477#ifdef IN_RING3
3478 ichac97R3MixerRecordSelect(pThis, u32);
3479#else
3480 rc = VINF_IOM_R3_IOPORT_WRITE;
3481#endif
3482 break;
3483 case AC97_Record_Gain_Mute:
3484#ifdef IN_RING3
3485 /* Newer Ubuntu guests rely on that when controlling gain and muting
3486 * the recording (capturing) levels. */
3487 ichac97R3MixerSetGain(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_LINE_IN, u32);
3488#else
3489 rc = VINF_IOM_R3_IOPORT_WRITE;
3490#endif
3491 break;
3492 case AC97_Record_Gain_Mic_Mute:
3493#ifdef IN_RING3
3494 /* Ditto; see note above. */
3495 ichac97R3MixerSetGain(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_MIC_IN, u32);
3496#else
3497 rc = VINF_IOM_R3_IOPORT_WRITE;
3498#endif
3499 break;
3500 case AC97_Vendor_ID1:
3501 case AC97_Vendor_ID2:
3502 LogFunc(("Attempt to write vendor ID to %#x\n", u32));
3503 break;
3504 case AC97_Extended_Audio_ID:
3505 LogFunc(("Attempt to write extended audio ID to %#x\n", u32));
3506 break;
3507 case AC97_Extended_Audio_Ctrl_Stat:
3508#ifdef IN_RING3
3509 /*
3510 * Handle VRA bits.
3511 */
3512 if (!(u32 & AC97_EACS_VRA)) /* Check if VRA bit is not set. */
3513 {
3514 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate, 0xbb80); /* Set default (48000 Hz). */
3515 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3516 * actually used? */
3517 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX],
3518 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX], true /* fForce */);
3519
3520 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate, 0xbb80); /* Set default (48000 Hz). */
3521 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3522 * actually used? */
3523 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX],
3524 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX], true /* fForce */);
3525 }
3526 else
3527 LogRel2(("AC97: Variable rate audio (VRA) is not supported\n"));
3528
3529 /*
3530 * Handle VRM bits.
3531 */
3532 if (!(u32 & AC97_EACS_VRM)) /* Check if VRM bit is not set. */
3533 {
3534 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate, 0xbb80); /* Set default (48000 Hz). */
3535 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3536 * actually used? */
3537 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX],
3538 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX], true /* fForce */);
3539 }
3540 else
3541 LogRel2(("AC97: Variable rate microphone audio (VRM) is not supported\n"));
3542
3543 LogRel2(("AC97: Setting extended audio control to %#x\n", u32));
3544 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, u32);
3545#else /* !IN_RING3 */
3546 rc = VINF_IOM_R3_IOPORT_WRITE;
3547#endif
3548 break;
3549 case AC97_PCM_Front_DAC_Rate: /* Output slots 3, 4, 6. */
3550#ifdef IN_RING3
3551 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRA)
3552 {
3553 LogRel2(("AC97: Setting front DAC rate to 0x%x\n", u32));
3554 ichac97MixerSet(pThis, offPort, u32);
3555 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3556 * actually used? */
3557 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX],
3558 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX], true /* fForce */);
3559 }
3560 else
3561 LogRel2(("AC97: Setting front DAC rate (0x%x) when VRA is not set is forbidden, ignoring\n", u32));
3562#else
3563 rc = VINF_IOM_R3_IOPORT_WRITE;
3564#endif
3565 break;
3566 case AC97_MIC_ADC_Rate: /* Input slot 6. */
3567#ifdef IN_RING3
3568 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRM)
3569 {
3570 LogRel2(("AC97: Setting microphone ADC rate to 0x%x\n", u32));
3571 ichac97MixerSet(pThis, offPort, u32);
3572 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3573 * actually used? */
3574 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX],
3575 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX], true /* fForce */);
3576 }
3577 else
3578 LogRel2(("AC97: Setting microphone ADC rate (0x%x) when VRM is not set is forbidden, ignoring\n", u32));
3579#else
3580 rc = VINF_IOM_R3_IOPORT_WRITE;
3581#endif
3582 break;
3583 case AC97_PCM_LR_ADC_Rate: /* Input slots 3, 4. */
3584#ifdef IN_RING3
3585 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRA)
3586 {
3587 LogRel2(("AC97: Setting line-in ADC rate to 0x%x\n", u32));
3588 ichac97MixerSet(pThis, offPort, u32);
3589 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3590 * actually used? */
3591 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX],
3592 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX], true /* fForce */);
3593 }
3594 else
3595 LogRel2(("AC97: Setting line-in ADC rate (0x%x) when VRA is not set is forbidden, ignoring\n", u32));
3596#else
3597 rc = VINF_IOM_R3_IOPORT_WRITE;
3598#endif
3599 break;
3600 default:
3601 /* Most of these are to register we don't care about like AC97_CD_Volume_Mute
3602 and AC97_Master_Volume_Mono_Mute or things we don't need to handle specially.
3603 Thus this is not a 'warning' but an 'info log message. */
3604 LogRel2(("AC97: Info: Unimplemented NAM write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3605 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3606 ichac97MixerSet(pThis, offPort, u32);
3607 break;
3608 }
3609 break;
3610 }
3611
3612 case 4:
3613 LogRel2(("AC97: Warning: Unimplemented NAM write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3614 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3615 pThis->cas = 0;
3616 break;
3617
3618 default:
3619 AssertMsgFailed(("Unhandled NAM write offPort=%#x, cb=%u u32=%#x\n", offPort, cb, u32));
3620 break;
3621 }
3622
3623 DEVAC97_UNLOCK(pDevIns, pThis);
3624 return rc;
3625}
3626
3627#ifdef IN_RING3
3628
3629
3630/*********************************************************************************************************************************
3631* State Saving & Loading *
3632*********************************************************************************************************************************/
3633
3634/**
3635 * Saves (serializes) an AC'97 stream using SSM.
3636 *
3637 * @param pDevIns Device instance.
3638 * @param pSSM Saved state manager (SSM) handle to use.
3639 * @param pStream AC'97 stream to save.
3640 */
3641static void ichac97R3SaveStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStream)
3642{
3643 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3644
3645 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bdbar);
3646 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.civ);
3647 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.lvi);
3648 pHlp->pfnSSMPutU16(pSSM, pStream->Regs.sr);
3649 pHlp->pfnSSMPutU16(pSSM, pStream->Regs.picb);
3650 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.piv);
3651 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.cr);
3652 pHlp->pfnSSMPutS32(pSSM, pStream->Regs.bd_valid);
3653 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bd.addr);
3654 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bd.ctl_len);
3655}
3656
3657
3658/**
3659 * @callback_method_impl{FNSSMDEVSAVEEXEC}
3660 */
3661static DECLCALLBACK(int) ichac97R3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
3662{
3663 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3664 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3665 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3666 LogFlowFuncEnter();
3667
3668 pHlp->pfnSSMPutU32(pSSM, pThis->glob_cnt);
3669 pHlp->pfnSSMPutU32(pSSM, pThis->glob_sta);
3670 pHlp->pfnSSMPutU32(pSSM, pThis->cas);
3671
3672 /*
3673 * The order that the streams are saved here is fixed, so don't change.
3674 */
3675 /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
3676 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3677 ichac97R3SaveStream(pDevIns, pSSM, &pThis->aStreams[i]);
3678
3679 pHlp->pfnSSMPutMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
3680
3681 /* The stream order is against fixed and set in stone. */
3682 uint8_t afActiveStrms[AC97SOUNDSOURCE_MAX];
3683 afActiveStrms[AC97SOUNDSOURCE_PI_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX]);
3684 afActiveStrms[AC97SOUNDSOURCE_PO_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX]);
3685 afActiveStrms[AC97SOUNDSOURCE_MC_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX]);
3686 AssertCompile(RT_ELEMENTS(afActiveStrms) == 3);
3687 pHlp->pfnSSMPutMem(pSSM, afActiveStrms, sizeof(afActiveStrms));
3688
3689 LogFlowFuncLeaveRC(VINF_SUCCESS);
3690 return VINF_SUCCESS;
3691}
3692
3693
3694/**
3695 * Loads an AC'97 stream from SSM.
3696 *
3697 * @returns VBox status code.
3698 * @param pDevIns The device instance.
3699 * @param pSSM Saved state manager (SSM) handle to use.
3700 * @param pStream AC'97 stream to load.
3701 */
3702static int ichac97R3LoadStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStream)
3703{
3704 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3705
3706 pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bdbar);
3707 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.civ);
3708 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.lvi);
3709 pHlp->pfnSSMGetU16(pSSM, &pStream->Regs.sr);
3710 pHlp->pfnSSMGetU16(pSSM, &pStream->Regs.picb);
3711 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.piv);
3712 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.cr);
3713 pHlp->pfnSSMGetS32(pSSM, &pStream->Regs.bd_valid);
3714 pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bd.addr);
3715 return pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bd.ctl_len);
3716}
3717
3718
3719/**
3720 * @callback_method_impl{FNSSMDEVLOADEXEC}
3721 */
3722static DECLCALLBACK(int) ichac97R3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
3723{
3724 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3725 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3726 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3727
3728 LogRel2(("ichac97LoadExec: uVersion=%RU32, uPass=0x%x\n", uVersion, uPass));
3729
3730 AssertMsgReturn (uVersion == AC97_SAVED_STATE_VERSION, ("%RU32\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
3731 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
3732
3733 pHlp->pfnSSMGetU32(pSSM, &pThis->glob_cnt);
3734 pHlp->pfnSSMGetU32(pSSM, &pThis->glob_sta);
3735 pHlp->pfnSSMGetU32(pSSM, &pThis->cas);
3736
3737 /*
3738 * The order the streams are loaded here is critical (defined by
3739 * AC97SOUNDSOURCE_XX_INDEX), so don't touch!
3740 */
3741 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3742 {
3743 int rc = ichac97R3LoadStream(pDevIns, pSSM, &pThis->aStreams[i]);
3744 AssertRCReturn(rc, rc);
3745 }
3746
3747 pHlp->pfnSSMGetMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
3748
3749 ichac97R3MixerRecordSelect(pThis, ichac97MixerGet(pThis, AC97_Record_Select));
3750 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER,
3751 ichac97MixerGet(pThis, AC97_Master_Volume_Mute));
3752 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT,
3753 ichac97MixerGet(pThis, AC97_PCM_Out_Volume_Mute));
3754 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN,
3755 ichac97MixerGet(pThis, AC97_Line_In_Volume_Mute));
3756 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Mic_Volume_Mute, PDMAUDIOMIXERCTL_MIC_IN,
3757 ichac97MixerGet(pThis, AC97_Mic_Volume_Mute));
3758 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mic_Mute, PDMAUDIOMIXERCTL_MIC_IN,
3759 ichac97MixerGet(pThis, AC97_Record_Gain_Mic_Mute));
3760 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mute, PDMAUDIOMIXERCTL_LINE_IN,
3761 ichac97MixerGet(pThis, AC97_Record_Gain_Mute));
3762 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3763 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_HPSEL)
3764 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Headphone_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER,
3765 ichac97MixerGet(pThis, AC97_Headphone_Volume_Mute));
3766
3767 /*
3768 * Again the stream order is set is stone.
3769 */
3770 uint8_t afActiveStrms[AC97SOUNDSOURCE_MAX];
3771 int rc = pHlp->pfnSSMGetMem(pSSM, afActiveStrms, sizeof(afActiveStrms));
3772 AssertRCReturn(rc, rc);
3773
3774 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3775 {
3776 const bool fEnable = RT_BOOL(afActiveStrms[i]);
3777 const PAC97STREAM pStream = &pThis->aStreams[i];
3778 const PAC97STREAMR3 pStreamCC = &pThisCC->aStreams[i];
3779
3780 rc = ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, fEnable);
3781 AssertRC(rc);
3782 if ( fEnable
3783 && RT_SUCCESS(rc))
3784 {
3785 /* Re-arm the timer for this stream. */
3786 /** @todo r=aeichner This causes a VM hang upon saved state resume when NetBSD is used as a guest
3787 * Stopping the timer if cDmaPeriodTicks is 0 is a workaround but needs further investigation,
3788 * see @bugref{9759} for more information. */
3789 if (pStream->cDmaPeriodTicks)
3790 ichac97R3TimerSet(pDevIns, pStream, pStream->cDmaPeriodTicks);
3791 else
3792 PDMDevHlpTimerStop(pDevIns, pStream->hTimer);
3793 }
3794
3795 /* Keep going. */
3796 }
3797
3798 pThis->bup_flag = 0;
3799 pThis->last_samp = 0;
3800
3801 return VINF_SUCCESS;
3802}
3803
3804
3805/*********************************************************************************************************************************
3806* Debug Info Items *
3807*********************************************************************************************************************************/
3808
3809/** Used by ichac97R3DbgInfoStream and ichac97R3DbgInfoBDL. */
3810static int ichac97R3DbgLookupStrmIdx(PCDBGFINFOHLP pHlp, const char *pszArgs)
3811{
3812 if (pszArgs && *pszArgs)
3813 {
3814 int32_t idxStream;
3815 int rc = RTStrToInt32Full(pszArgs, 0, &idxStream);
3816 if (RT_SUCCESS(rc) && idxStream >= -1 && idxStream < AC97_MAX_STREAMS)
3817 return idxStream;
3818 pHlp->pfnPrintf(pHlp, "Argument '%s' is not a valid stream number!\n", pszArgs);
3819 }
3820 return -1;
3821}
3822
3823
3824/**
3825 * Generic buffer descriptor list dumper.
3826 */
3827static void ichac97R3DbgPrintBdl(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream,
3828 PCDBGFINFOHLP pHlp, const char *pszPrefix)
3829{
3830 uint8_t const bLvi = pStream->Regs.lvi;
3831 uint8_t const bCiv = pStream->Regs.civ;
3832 pHlp->pfnPrintf(pHlp, "%sBDL for stream #%u: @ %#RX32 LB 0x100; CIV=%#04x LVI=%#04x:\n",
3833 pszPrefix, pStream->u8SD, pStream->Regs.bdbar, bCiv, bLvi);
3834 if (pStream->Regs.bdbar != 0)
3835 {
3836 /* Read all in one go. */
3837 AC97BDLE aBdl[AC97_MAX_BDLE];
3838 RT_ZERO(aBdl);
3839 PDMDevHlpPCIPhysRead(pDevIns, pStream->Regs.bdbar, aBdl, sizeof(aBdl));
3840
3841 /* Get the audio props for the stream so we can translate the sizes correctly. */
3842 PDMAUDIOPCMPROPS Props;
3843 ichach97R3CalcStreamProps(pThis, pStream->u8SD, &Props);
3844
3845 /* Dump them. */
3846 uint64_t cbTotal = 0;
3847 uint64_t cbValid = 0;
3848 for (unsigned i = 0; i < RT_ELEMENTS(aBdl); i++)
3849 {
3850 aBdl[i].addr = RT_LE2H_U32(aBdl[i].addr);
3851 aBdl[i].ctl_len = RT_LE2H_U32(aBdl[i].ctl_len);
3852
3853 bool const fValid = bCiv <= bLvi
3854 ? i >= bCiv && i <= bLvi
3855 : i >= bCiv || i <= bLvi;
3856
3857 uint32_t const cb = (aBdl[i].ctl_len & AC97_BD_LEN_MASK) * PDMAudioPropsSampleSize(&Props); /** @todo or frame size? OSDev says frame... */
3858 cbTotal += cb;
3859 if (fValid)
3860 cbValid += cb;
3861
3862 char szFlags[64];
3863 szFlags[0] = '\0';
3864 if (aBdl[i].ctl_len & ~(AC97_BD_LEN_MASK | AC97_BD_IOC | AC97_BD_BUP))
3865 RTStrPrintf(szFlags, sizeof(szFlags), " !!fFlags=%#x!!\n", aBdl[i].ctl_len & ~AC97_BD_LEN_MASK);
3866
3867 pHlp->pfnPrintf(pHlp, "%s %cBDLE%02u: %#010RX32 L %#06x / LB %#RX32 / %RU64ms%s%s%s%s\n",
3868 pszPrefix, fValid ? ' ' : '?', i, aBdl[i].addr,
3869 aBdl[i].ctl_len & AC97_BD_LEN_MASK, cb, PDMAudioPropsBytesToMilli(&Props, cb),
3870 aBdl[i].ctl_len & AC97_BD_IOC ? " ioc" : "",
3871 aBdl[i].ctl_len & AC97_BD_BUP ? " bup" : "",
3872 szFlags, !(aBdl[i].addr & 3) ? "" : " !!Addr!!");
3873 }
3874
3875 pHlp->pfnPrintf(pHlp, "%sTotal: %#RX64 bytes (%RU64), %RU64 ms; Valid: %#RX64 bytes (%RU64), %RU64 ms\n", pszPrefix,
3876 cbTotal, cbTotal, PDMAudioPropsBytesToMilli(&Props, cbTotal),
3877 cbValid, cbValid, PDMAudioPropsBytesToMilli(&Props, cbValid) );
3878 }
3879}
3880
3881
3882/**
3883 * @callback_method_impl{FNDBGFHANDLERDEV, ac97bdl}
3884 */
3885static DECLCALLBACK(void) ichac97R3DbgInfoBDL(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3886{
3887 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3888 int idxStream = ichac97R3DbgLookupStrmIdx(pHlp, pszArgs);
3889 if (idxStream != -1)
3890 ichac97R3DbgPrintBdl(pDevIns, pThis, &pThis->aStreams[idxStream], pHlp, "");
3891 else
3892 for (idxStream = 0; idxStream < AC97_MAX_STREAMS; ++idxStream)
3893 ichac97R3DbgPrintBdl(pDevIns, pThis, &pThis->aStreams[idxStream], pHlp, "");
3894}
3895
3896
3897/** Worker for ichac97R3DbgInfoStream. */
3898static void ichac97R3DbgPrintStream(PCDBGFINFOHLP pHlp, PAC97STREAM pStream, PAC97STREAMR3 pStreamR3)
3899{
3900 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX];
3901 pHlp->pfnPrintf(pHlp, "Stream #%d: %s\n", pStream->u8SD,
3902 PDMAudioStrmCfgToString(&pStreamR3->State.Cfg, szTmp, sizeof(szTmp)));
3903 pHlp->pfnPrintf(pHlp, " BDBAR %#010RX32\n", pStream->Regs.bdbar);
3904 pHlp->pfnPrintf(pHlp, " CIV %#04RX8\n", pStream->Regs.civ);
3905 pHlp->pfnPrintf(pHlp, " LVI %#04RX8\n", pStream->Regs.lvi);
3906 pHlp->pfnPrintf(pHlp, " SR %#06RX16\n", pStream->Regs.sr);
3907 pHlp->pfnPrintf(pHlp, " PICB %#06RX16\n", pStream->Regs.picb);
3908 pHlp->pfnPrintf(pHlp, " PIV %#04RX8\n", pStream->Regs.piv);
3909 pHlp->pfnPrintf(pHlp, " CR %#04RX8\n", pStream->Regs.cr);
3910 if (pStream->Regs.bd_valid)
3911 {
3912 pHlp->pfnPrintf(pHlp, " BD.ADDR %#010RX32\n", pStream->Regs.bd.addr);
3913 pHlp->pfnPrintf(pHlp, " BD.LEN %#04RX16\n", (uint16_t)pStream->Regs.bd.ctl_len);
3914 pHlp->pfnPrintf(pHlp, " BD.CTL %#04RX16\n", (uint16_t)(pStream->Regs.bd.ctl_len >> 16));
3915 }
3916
3917 pHlp->pfnPrintf(pHlp, " offRead %#RX64\n", pStreamR3->State.offRead);
3918 pHlp->pfnPrintf(pHlp, " offWrite %#RX64\n", pStreamR3->State.offWrite);
3919 pHlp->pfnPrintf(pHlp, " uTimerHz %RU16\n", pStreamR3->State.uTimerHz);
3920 pHlp->pfnPrintf(pHlp, " cDmaPeriodTicks %RU64\n", pStream->cDmaPeriodTicks);
3921 pHlp->pfnPrintf(pHlp, " cbDmaPeriod %#RX32\n", pStream->cbDmaPeriod);
3922}
3923
3924
3925/**
3926 * @callback_method_impl{FNDBGFHANDLERDEV, ac97stream}
3927 */
3928static DECLCALLBACK(void) ichac97R3DbgInfoStream(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3929{
3930 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3931 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3932 int idxStream = ichac97R3DbgLookupStrmIdx(pHlp, pszArgs);
3933 if (idxStream != -1)
3934 ichac97R3DbgPrintStream(pHlp, &pThis->aStreams[idxStream], &pThisCC->aStreams[idxStream]);
3935 else
3936 for (idxStream = 0; idxStream < AC97_MAX_STREAMS; ++idxStream)
3937 ichac97R3DbgPrintStream(pHlp, &pThis->aStreams[idxStream], &pThisCC->aStreams[idxStream]);
3938}
3939
3940
3941/**
3942 * @callback_method_impl{FNDBGFHANDLERDEV, ac97mixer}
3943 */
3944static DECLCALLBACK(void) ichac97R3DbgInfoMixer(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3945{
3946 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3947 if (pThisCC->pMixer)
3948 AudioMixerDebug(pThisCC->pMixer, pHlp, pszArgs);
3949 else
3950 pHlp->pfnPrintf(pHlp, "Mixer not available\n");
3951}
3952
3953
3954/*********************************************************************************************************************************
3955* PDMIBASE *
3956*********************************************************************************************************************************/
3957
3958/**
3959 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3960 */
3961static DECLCALLBACK(void *) ichac97R3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
3962{
3963 PAC97STATER3 pThisCC = RT_FROM_MEMBER(pInterface, AC97STATER3, IBase);
3964 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
3965 return NULL;
3966}
3967
3968
3969/*********************************************************************************************************************************
3970* PDMDEVREG *
3971*********************************************************************************************************************************/
3972
3973/**
3974 * Destroys all AC'97 audio streams of the device.
3975 *
3976 * @param pDevIns The device AC'97 instance.
3977 * @param pThis The shared AC'97 state.
3978 * @param pThisCC The ring-3 AC'97 state.
3979 */
3980static void ichac97R3StreamsDestroy(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC)
3981{
3982 LogFlowFuncEnter();
3983
3984 /*
3985 * Destroy all AC'97 streams.
3986 */
3987 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3988 ichac97R3StreamDestroy(pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i]);
3989
3990 /*
3991 * Destroy all sinks.
3992 */
3993 if (pThisCC->pSinkLineIn)
3994 {
3995 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkLineIn, PDMAUDIODIR_IN, PDMAUDIOPATH_IN_LINE);
3996
3997 AudioMixerSinkDestroy(pThisCC->pSinkLineIn, pDevIns);
3998 pThisCC->pSinkLineIn = NULL;
3999 }
4000
4001 if (pThisCC->pSinkMicIn)
4002 {
4003 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkMicIn, PDMAUDIODIR_IN, PDMAUDIOPATH_IN_MIC);
4004
4005 AudioMixerSinkDestroy(pThisCC->pSinkMicIn, pDevIns);
4006 pThisCC->pSinkMicIn = NULL;
4007 }
4008
4009 if (pThisCC->pSinkOut)
4010 {
4011 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkOut, PDMAUDIODIR_OUT, PDMAUDIOPATH_OUT_FRONT);
4012
4013 AudioMixerSinkDestroy(pThisCC->pSinkOut, pDevIns);
4014 pThisCC->pSinkOut = NULL;
4015 }
4016}
4017
4018
4019/**
4020 * Powers off the device.
4021 *
4022 * @param pDevIns Device instance to power off.
4023 */
4024static DECLCALLBACK(void) ichac97R3PowerOff(PPDMDEVINS pDevIns)
4025{
4026 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4027 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4028
4029 LogRel2(("AC97: Powering off ...\n"));
4030
4031 /* Note: Involves mixer stream / sink destruction, so also do this here
4032 * instead of in ichac97R3Destruct(). */
4033 ichac97R3StreamsDestroy(pDevIns, pThis, pThisCC);
4034
4035 /*
4036 * Note: Destroy the mixer while powering off and *not* in ichac97R3Destruct,
4037 * giving the mixer the chance to release any references held to
4038 * PDM audio streams it maintains.
4039 */
4040 if (pThisCC->pMixer)
4041 {
4042 AudioMixerDestroy(pThisCC->pMixer, pDevIns);
4043 pThisCC->pMixer = NULL;
4044 }
4045}
4046
4047
4048/**
4049 * @interface_method_impl{PDMDEVREG,pfnReset}
4050 *
4051 * @remarks The original sources didn't install a reset handler, but it seems to
4052 * make sense to me so we'll do it.
4053 */
4054static DECLCALLBACK(void) ichac97R3Reset(PPDMDEVINS pDevIns)
4055{
4056 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4057 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4058
4059 LogRel(("AC97: Reset\n"));
4060
4061 /*
4062 * Reset the mixer too. The Windows XP driver seems to rely on
4063 * this. At least it wants to read the vendor id before it resets
4064 * the codec manually.
4065 */
4066 ichac97R3MixerReset(pThis, pThisCC);
4067
4068 /*
4069 * Reset all streams.
4070 */
4071 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4072 {
4073 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i], false /* fEnable */);
4074 ichac97R3StreamReset(pThis, &pThis->aStreams[i], &pThisCC->aStreams[i]);
4075 }
4076
4077 /*
4078 * Reset mixer sinks.
4079 *
4080 * Do the reset here instead of in ichac97R3StreamReset();
4081 * the mixer sink(s) might still have data to be processed when an audio stream gets reset.
4082 */
4083 AudioMixerSinkReset(pThisCC->pSinkLineIn);
4084 AudioMixerSinkReset(pThisCC->pSinkMicIn);
4085 AudioMixerSinkReset(pThisCC->pSinkOut);
4086}
4087
4088
4089/**
4090 * Adds a specific AC'97 driver to the driver chain.
4091 *
4092 * Only called from ichac97R3Attach().
4093 *
4094 * @returns VBox status code.
4095 * @param pDevIns The device instance.
4096 * @param pThisCC The ring-3 AC'97 device state.
4097 * @param pDrv The AC'97 driver to add.
4098 */
4099static int ichac97R3MixerAddDrv(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAC97DRIVER pDrv)
4100{
4101 int rc = VINF_SUCCESS;
4102
4103 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX].State.Cfg))
4104 rc = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkLineIn,
4105 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX].State.Cfg, pDrv);
4106
4107 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX].State.Cfg))
4108 {
4109 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkOut,
4110 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX].State.Cfg, pDrv);
4111 if (RT_SUCCESS(rc))
4112 rc = rc2;
4113 }
4114
4115 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX].State.Cfg))
4116 {
4117 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkMicIn,
4118 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX].State.Cfg, pDrv);
4119 if (RT_SUCCESS(rc))
4120 rc = rc2;
4121 }
4122
4123 return rc;
4124}
4125
4126
4127/**
4128 * Worker for ichac97R3Construct() and ichac97R3Attach().
4129 *
4130 * @returns VBox status code.
4131 * @param pDevIns The device instance.
4132 * @param pThisCC The ring-3 AC'97 device state.
4133 * @param iLun The logical unit which is being attached.
4134 * @param ppDrv Attached driver instance on success. Optional.
4135 */
4136static int ichac97R3AttachInternal(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, unsigned iLun, PAC97DRIVER *ppDrv)
4137{
4138 /*
4139 * Attach driver.
4140 */
4141 char *pszDesc = RTStrAPrintf2("Audio driver port (AC'97) for LUN #%u", iLun);
4142 AssertLogRelReturn(pszDesc, VERR_NO_STR_MEMORY);
4143
4144 PPDMIBASE pDrvBase;
4145 int rc = PDMDevHlpDriverAttach(pDevIns, iLun, &pThisCC->IBase, &pDrvBase, pszDesc);
4146 if (RT_SUCCESS(rc))
4147 {
4148 PAC97DRIVER pDrv = (PAC97DRIVER)RTMemAllocZ(sizeof(AC97DRIVER));
4149 if (pDrv)
4150 {
4151 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
4152 AssertPtr(pDrv->pConnector);
4153 if (RT_VALID_PTR(pDrv->pConnector))
4154 {
4155 pDrv->pDrvBase = pDrvBase;
4156 pDrv->uLUN = iLun;
4157 pDrv->pszDesc = pszDesc;
4158
4159 /* Attach to driver list if not attached yet. */
4160 if (!pDrv->fAttached)
4161 {
4162 RTListAppend(&pThisCC->lstDrv, &pDrv->Node);
4163 pDrv->fAttached = true;
4164 }
4165
4166 if (ppDrv)
4167 *ppDrv = pDrv;
4168
4169 /*
4170 * While we're here, give the windows backends a hint about our typical playback
4171 * configuration.
4172 */
4173 if ( pDrv->pConnector
4174 && pDrv->pConnector->pfnStreamConfigHint)
4175 {
4176 /* 48kHz */
4177 PDMAUDIOSTREAMCFG Cfg;
4178 RT_ZERO(Cfg);
4179 Cfg.enmDir = PDMAUDIODIR_OUT;
4180 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
4181 Cfg.Device.cMsSchedulingHint = 5;
4182 Cfg.Backend.cFramesPreBuffering = UINT32_MAX;
4183 PDMAudioPropsInit(&Cfg.Props, 2, true /*fSigned*/, 2, 48000);
4184 RTStrPrintf(Cfg.szName, sizeof(Cfg.szName), "output 48kHz 2ch S16 (HDA config hint)");
4185
4186 pDrv->pConnector->pfnStreamConfigHint(pDrv->pConnector, &Cfg); /* (may trash CfgReq) */
4187# if 0
4188 /* 44.1kHz */
4189 RT_ZERO(Cfg);
4190 Cfg.enmDir = PDMAUDIODIR_OUT;
4191 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
4192 Cfg.Device.cMsSchedulingHint = 10;
4193 Cfg.Backend.cFramesPreBuffering = UINT32_MAX;
4194 PDMAudioPropsInit(&Cfg.Props, 2, true /*fSigned*/, 2, 44100);
4195 RTStrPrintf(Cfg.szName, sizeof(Cfg.szName), "output 44.1kHz 2ch S16 (HDA config hint)");
4196
4197 pDrv->pConnector->pfnStreamConfigHint(pDrv->pConnector, &Cfg); /* (may trash CfgReq) */
4198# endif
4199 }
4200
4201 LogFunc(("LUN#%u: returns VINF_SUCCESS (pCon=%p)\n", iLun, pDrv->pConnector));
4202 return VINF_SUCCESS;
4203 }
4204 RTMemFree(pDrv);
4205 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
4206 }
4207 else
4208 rc = VERR_NO_MEMORY;
4209 }
4210 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4211 LogFunc(("No attached driver for LUN #%u\n", iLun));
4212 else
4213 LogFunc(("Attached driver for LUN #%u failed: %Rrc\n", iLun, rc));
4214
4215 RTStrFree(pszDesc);
4216 LogFunc(("LUN#%u: rc=%Rrc\n", iLun, rc));
4217 return rc;
4218}
4219
4220
4221/**
4222 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
4223 */
4224static DECLCALLBACK(int) ichac97R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4225{
4226 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4227 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4228 RT_NOREF(fFlags);
4229 LogFunc(("iLUN=%u, fFlags=%#x\n", iLUN, fFlags));
4230
4231 DEVAC97_LOCK(pDevIns, pThis);
4232
4233 PAC97DRIVER pDrv;
4234 int rc = ichac97R3AttachInternal(pDevIns, pThisCC, iLUN, &pDrv);
4235 if (RT_SUCCESS(rc))
4236 {
4237 int rc2 = ichac97R3MixerAddDrv(pDevIns, pThisCC, pDrv);
4238 if (RT_FAILURE(rc2))
4239 LogFunc(("ichac97R3MixerAddDrv failed with %Rrc (ignored)\n", rc2));
4240 }
4241
4242 DEVAC97_UNLOCK(pDevIns, pThis);
4243
4244 return rc;
4245}
4246
4247
4248/**
4249 * Removes a specific AC'97 driver from the driver chain and destroys its
4250 * associated streams.
4251 *
4252 * Only called from ichac97R3Detach().
4253 *
4254 * @param pDevIns The device instance.
4255 * @param pThisCC The ring-3 AC'97 device state.
4256 * @param pDrv AC'97 driver to remove.
4257 */
4258static void ichac97R3MixerRemoveDrv(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAC97DRIVER pDrv)
4259{
4260 if (pDrv->MicIn.pMixStrm)
4261 {
4262 AudioMixerSinkRemoveStream(pThisCC->pSinkMicIn, pDrv->MicIn.pMixStrm);
4263 AudioMixerStreamDestroy(pDrv->MicIn.pMixStrm, pDevIns, true /*fImmediate*/);
4264 pDrv->MicIn.pMixStrm = NULL;
4265 }
4266
4267 if (pDrv->LineIn.pMixStrm)
4268 {
4269 AudioMixerSinkRemoveStream(pThisCC->pSinkLineIn, pDrv->LineIn.pMixStrm);
4270 AudioMixerStreamDestroy(pDrv->LineIn.pMixStrm, pDevIns, true /*fImmediate*/);
4271 pDrv->LineIn.pMixStrm = NULL;
4272 }
4273
4274 if (pDrv->Out.pMixStrm)
4275 {
4276 AudioMixerSinkRemoveStream(pThisCC->pSinkOut, pDrv->Out.pMixStrm);
4277 AudioMixerStreamDestroy(pDrv->Out.pMixStrm, pDevIns, true /*fImmediate*/);
4278 pDrv->Out.pMixStrm = NULL;
4279 }
4280
4281 RTListNodeRemove(&pDrv->Node);
4282}
4283
4284
4285/**
4286 * @interface_method_impl{PDMDEVREG,pfnDetach}
4287 */
4288static DECLCALLBACK(void) ichac97R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4289{
4290 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4291 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4292 RT_NOREF(fFlags);
4293
4294 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
4295
4296 DEVAC97_LOCK(pDevIns, pThis);
4297
4298 PAC97DRIVER pDrv;
4299 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
4300 {
4301 if (pDrv->uLUN == iLUN)
4302 {
4303 /* Remove the driver from our list and destory it's associated streams.
4304 This also will un-set the driver as a recording source (if associated). */
4305 ichac97R3MixerRemoveDrv(pDevIns, pThisCC, pDrv);
4306 LogFunc(("Detached LUN#%u\n", pDrv->uLUN));
4307
4308 DEVAC97_UNLOCK(pDevIns, pThis);
4309
4310 RTStrFree(pDrv->pszDesc);
4311 RTMemFree(pDrv);
4312 return;
4313 }
4314 }
4315
4316 DEVAC97_UNLOCK(pDevIns, pThis);
4317 LogFunc(("LUN#%u was not found\n", iLUN));
4318}
4319
4320
4321/**
4322 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4323 */
4324static DECLCALLBACK(int) ichac97R3Destruct(PPDMDEVINS pDevIns)
4325{
4326 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
4327 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4328
4329 LogFlowFuncEnter();
4330
4331 PAC97DRIVER pDrv, pDrvNext;
4332 RTListForEachSafe(&pThisCC->lstDrv, pDrv, pDrvNext, AC97DRIVER, Node)
4333 {
4334 RTListNodeRemove(&pDrv->Node);
4335 RTMemFree(pDrv->pszDesc);
4336 RTMemFree(pDrv);
4337 }
4338
4339 /* Sanity. */
4340 Assert(RTListIsEmpty(&pThisCC->lstDrv));
4341
4342 /* We don't always go via PowerOff, so make sure the mixer is destroyed. */
4343 if (pThisCC->pMixer)
4344 {
4345 AudioMixerDestroy(pThisCC->pMixer, pDevIns);
4346 pThisCC->pMixer = NULL;
4347 }
4348
4349 return VINF_SUCCESS;
4350}
4351
4352
4353/**
4354 * @interface_method_impl{PDMDEVREG,pfnConstruct}
4355 */
4356static DECLCALLBACK(int) ichac97R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4357{
4358 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
4359 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4360 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4361 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4362 Assert(iInstance == 0); RT_NOREF(iInstance);
4363
4364 /*
4365 * Initialize data so we can run the destructor without scewing up.
4366 */
4367 pThisCC->pDevIns = pDevIns;
4368 pThisCC->IBase.pfnQueryInterface = ichac97R3QueryInterface;
4369 RTListInit(&pThisCC->lstDrv);
4370
4371 /*
4372 * Validate and read configuration.
4373 */
4374 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "BufSizeInMs|BufSizeOutMs|Codec|TimerHz|DebugEnabled|DebugPathOut", "");
4375
4376 /** @devcfgm{ac97,BufSizeInMs,uint16_t,0,2000,0,ms}
4377 * The size of the DMA buffer for input streams expressed in milliseconds. */
4378 int rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeInMs", &pThis->cMsCircBufIn, 0);
4379 if (RT_FAILURE(rc))
4380 return PDMDEV_SET_ERROR(pDevIns, rc,
4381 N_("AC97 configuration error: failed to read 'BufSizeInMs' as 16-bit unsigned integer"));
4382 if (pThis->cMsCircBufIn > 2000)
4383 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4384 N_("AC97 configuration error: 'BufSizeInMs' is out of bound, max 2000 ms"));
4385
4386 /** @devcfgm{ac97,BufSizeOutMs,uint16_t,0,2000,0,ms}
4387 * The size of the DMA buffer for output streams expressed in milliseconds. */
4388 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeOutMs", &pThis->cMsCircBufOut, 0);
4389 if (RT_FAILURE(rc))
4390 return PDMDEV_SET_ERROR(pDevIns, rc,
4391 N_("AC97 configuration error: failed to read 'BufSizeOutMs' as 16-bit unsigned integer"));
4392 if (pThis->cMsCircBufOut > 2000)
4393 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4394 N_("AC97 configuration error: 'BufSizeOutMs' is out of bound, max 2000 ms"));
4395
4396 /** @devcfgm{ac97,TimerHz,uint16_t,10,1000,100,ms}
4397 * Currently the approximate rate at which the asynchronous I/O threads move
4398 * data from/to the DMA buffer, thru the mixer and drivers stack, and
4399 * to/from the host device/whatever. (It does NOT govern any DMA timer rate any
4400 * more as might be hinted at by the name.) */
4401 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &pThis->uTimerHz, AC97_TIMER_HZ_DEFAULT);
4402 if (RT_FAILURE(rc))
4403 return PDMDEV_SET_ERROR(pDevIns, rc,
4404 N_("AC'97 configuration error: failed to read 'TimerHz' as a 16-bit unsigned integer"));
4405 if (pThis->uTimerHz < 10 || pThis->uTimerHz > 1000)
4406 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4407 N_("AC'97 configuration error: 'TimerHz' is out of range (10-1000 Hz)"));
4408
4409 if (pThis->uTimerHz != AC97_TIMER_HZ_DEFAULT)
4410 LogRel(("AC97: Using custom device timer rate: %RU16 Hz\n", pThis->uTimerHz));
4411
4412 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThisCC->Dbg.fEnabled, false);
4413 if (RT_FAILURE(rc))
4414 return PDMDEV_SET_ERROR(pDevIns, rc,
4415 N_("AC97 configuration error: failed to read debugging enabled flag as boolean"));
4416
4417 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DebugPathOut", &pThisCC->Dbg.pszOutPath, NULL);
4418 if (RT_FAILURE(rc))
4419 return PDMDEV_SET_ERROR(pDevIns, rc,
4420 N_("AC97 configuration error: failed to read debugging output path flag as string"));
4421
4422 if (pThisCC->Dbg.fEnabled)
4423 LogRel2(("AC97: Debug output will be saved to '%s'\n", pThisCC->Dbg.pszOutPath));
4424
4425 /*
4426 * The AD1980 codec (with corresponding PCI subsystem vendor ID) is whitelisted
4427 * in the Linux kernel; Linux makes no attempt to measure the data rate and assumes
4428 * 48 kHz rate, which is exactly what we need. Same goes for AD1981B.
4429 */
4430 char szCodec[20];
4431 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "Codec", &szCodec[0], sizeof(szCodec), "STAC9700");
4432 if (RT_FAILURE(rc))
4433 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4434 N_("AC'97 configuration error: Querying \"Codec\" as string failed"));
4435 if (!strcmp(szCodec, "STAC9700"))
4436 pThis->enmCodecModel = AC97CODEC_STAC9700;
4437 else if (!strcmp(szCodec, "AD1980"))
4438 pThis->enmCodecModel = AC97CODEC_AD1980;
4439 else if (!strcmp(szCodec, "AD1981B"))
4440 pThis->enmCodecModel = AC97CODEC_AD1981B;
4441 else
4442 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
4443 N_("AC'97 configuration error: The \"Codec\" value \"%s\" is unsupported"), szCodec);
4444
4445 LogRel(("AC97: Using codec '%s'\n", szCodec));
4446
4447 /*
4448 * Use an own critical section for the device instead of the default
4449 * one provided by PDM. This allows fine-grained locking in combination
4450 * with TM when timer-specific stuff is being called in e.g. the MMIO handlers.
4451 */
4452 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "AC'97");
4453 AssertRCReturn(rc, rc);
4454
4455 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
4456 AssertRCReturn(rc, rc);
4457
4458 /*
4459 * Initialize data (most of it anyway).
4460 */
4461 /* PCI Device */
4462 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
4463 PCIDevSetVendorId(pPciDev, 0x8086); /* 00 ro - intel. */ Assert(pPciDev->abConfig[0x00] == 0x86); Assert(pPciDev->abConfig[0x01] == 0x80);
4464 PCIDevSetDeviceId(pPciDev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */ Assert(pPciDev->abConfig[0x02] == 0x15); Assert(pPciDev->abConfig[0x03] == 0x24);
4465 PCIDevSetCommand(pPciDev, 0x0000); /* 04 rw,ro - pcicmd. */ Assert(pPciDev->abConfig[0x04] == 0x00); Assert(pPciDev->abConfig[0x05] == 0x00);
4466 PCIDevSetStatus(pPciDev, VBOX_PCI_STATUS_DEVSEL_MEDIUM | VBOX_PCI_STATUS_FAST_BACK); /* 06 rwc?,ro? - pcists. */ Assert(pPciDev->abConfig[0x06] == 0x80); Assert(pPciDev->abConfig[0x07] == 0x02);
4467 PCIDevSetRevisionId(pPciDev, 0x01); /* 08 ro - rid. */ Assert(pPciDev->abConfig[0x08] == 0x01);
4468 PCIDevSetClassProg(pPciDev, 0x00); /* 09 ro - pi. */ Assert(pPciDev->abConfig[0x09] == 0x00);
4469 PCIDevSetClassSub(pPciDev, 0x01); /* 0a ro - scc; 01 == Audio. */ Assert(pPciDev->abConfig[0x0a] == 0x01);
4470 PCIDevSetClassBase(pPciDev, 0x04); /* 0b ro - bcc; 04 == multimedia.*/Assert(pPciDev->abConfig[0x0b] == 0x04);
4471 PCIDevSetHeaderType(pPciDev, 0x00); /* 0e ro - headtyp. */ Assert(pPciDev->abConfig[0x0e] == 0x00);
4472 PCIDevSetBaseAddress(pPciDev, 0, /* 10 rw - nambar - native audio mixer base. */
4473 true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pPciDev->abConfig[0x10] == 0x01); Assert(pPciDev->abConfig[0x11] == 0x00); Assert(pPciDev->abConfig[0x12] == 0x00); Assert(pPciDev->abConfig[0x13] == 0x00);
4474 PCIDevSetBaseAddress(pPciDev, 1, /* 14 rw - nabmbar - native audio bus mastering. */
4475 true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pPciDev->abConfig[0x14] == 0x01); Assert(pPciDev->abConfig[0x15] == 0x00); Assert(pPciDev->abConfig[0x16] == 0x00); Assert(pPciDev->abConfig[0x17] == 0x00);
4476 PCIDevSetInterruptLine(pPciDev, 0x00); /* 3c rw. */ Assert(pPciDev->abConfig[0x3c] == 0x00);
4477 PCIDevSetInterruptPin(pPciDev, 0x01); /* 3d ro - INTA#. */ Assert(pPciDev->abConfig[0x3d] == 0x01);
4478
4479 if (pThis->enmCodecModel == AC97CODEC_AD1980)
4480 {
4481 PCIDevSetSubSystemVendorId(pPciDev, 0x1028); /* 2c ro - Dell.) */
4482 PCIDevSetSubSystemId(pPciDev, 0x0177); /* 2e ro. */
4483 }
4484 else if (pThis->enmCodecModel == AC97CODEC_AD1981B)
4485 {
4486 PCIDevSetSubSystemVendorId(pPciDev, 0x1028); /* 2c ro - Dell.) */
4487 PCIDevSetSubSystemId(pPciDev, 0x01ad); /* 2e ro. */
4488 }
4489 else
4490 {
4491 PCIDevSetSubSystemVendorId(pPciDev, 0x8086); /* 2c ro - Intel.) */
4492 PCIDevSetSubSystemId(pPciDev, 0x0000); /* 2e ro. */
4493 }
4494
4495 /*
4496 * Register the PCI device and associated I/O regions.
4497 */
4498 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
4499 if (RT_FAILURE(rc))
4500 return rc;
4501
4502 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 0 /*iPciRegion*/, 256 /*cPorts*/,
4503 ichac97IoPortNamWrite, ichac97IoPortNamRead, NULL /*pvUser*/,
4504 "ICHAC97 NAM", NULL /*paExtDescs*/, &pThis->hIoPortsNam);
4505 AssertRCReturn(rc, rc);
4506
4507 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 1 /*iPciRegion*/, 64 /*cPorts*/,
4508 ichac97IoPortNabmWrite, ichac97IoPortNabmRead, NULL /*pvUser*/,
4509 "ICHAC97 NABM", g_aNabmPorts, &pThis->hIoPortsNabm);
4510 AssertRCReturn(rc, rc);
4511
4512 /*
4513 * Saved state.
4514 */
4515 rc = PDMDevHlpSSMRegister(pDevIns, AC97_SAVED_STATE_VERSION, sizeof(*pThis), ichac97R3SaveExec, ichac97R3LoadExec);
4516 if (RT_FAILURE(rc))
4517 return rc;
4518
4519 /*
4520 * Attach drivers. We ASSUME they are configured consecutively without any
4521 * gaps, so we stop when we hit the first LUN w/o a driver configured.
4522 */
4523 for (unsigned iLun = 0; ; iLun++)
4524 {
4525 AssertBreak(iLun < UINT8_MAX);
4526 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
4527 rc = ichac97R3AttachInternal(pDevIns, pThisCC, iLun, NULL /* ppDrv */);
4528 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4529 {
4530 LogFunc(("cLUNs=%u\n", iLun));
4531 break;
4532 }
4533 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
4534 }
4535
4536 uint32_t fMixer = AUDMIXER_FLAGS_NONE;
4537 if (pThisCC->Dbg.fEnabled)
4538 fMixer |= AUDMIXER_FLAGS_DEBUG;
4539
4540 rc = AudioMixerCreate("AC'97 Mixer", 0 /* uFlags */, &pThisCC->pMixer);
4541 AssertRCReturn(rc, rc);
4542
4543 rc = AudioMixerCreateSink(pThisCC->pMixer, "Line In",
4544 PDMAUDIODIR_IN, pDevIns, &pThisCC->pSinkLineIn);
4545 AssertRCReturn(rc, rc);
4546 rc = AudioMixerCreateSink(pThisCC->pMixer, "Microphone In",
4547 PDMAUDIODIR_IN, pDevIns, &pThisCC->pSinkMicIn);
4548 AssertRCReturn(rc, rc);
4549 rc = AudioMixerCreateSink(pThisCC->pMixer, "PCM Output",
4550 PDMAUDIODIR_OUT, pDevIns, &pThisCC->pSinkOut);
4551 AssertRCReturn(rc, rc);
4552
4553 /*
4554 * Create all hardware streams.
4555 */
4556 AssertCompile(RT_ELEMENTS(pThis->aStreams) == AC97_MAX_STREAMS);
4557 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4558 {
4559 rc = ichac97R3StreamConstruct(pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i], i /* SD# */);
4560 AssertRCReturn(rc, rc);
4561 }
4562
4563 /*
4564 * Create the emulation timers (one per stream).
4565 *
4566 * We must the critical section for the timers as the device has a
4567 * noop section associated with it.
4568 *
4569 * Note: Use TMCLOCK_VIRTUAL_SYNC here, as the guest's AC'97 driver
4570 * relies on exact (virtual) DMA timing and uses DMA Position Buffers
4571 * instead of the LPIB registers.
4572 */
4573 /** @todo r=bird: The need to use virtual sync is perhaps because TM
4574 * doesn't schedule regular TMCLOCK_VIRTUAL timers as accurately as it
4575 * should (VT-x preemption timer, etc). Hope to address that before
4576 * long. @bugref{9943}. */
4577 static const char * const s_apszNames[] = { "AC97 PI", "AC97 PO", "AC97 MC" };
4578 AssertCompile(RT_ELEMENTS(s_apszNames) == AC97_MAX_STREAMS);
4579 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4580 {
4581 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, ichac97R3Timer, &pThis->aStreams[i],
4582 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, s_apszNames[i], &pThis->aStreams[i].hTimer);
4583 AssertRCReturn(rc, rc);
4584
4585 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->aStreams[i].hTimer, &pThis->CritSect);
4586 AssertRCReturn(rc, rc);
4587 }
4588
4589 ichac97R3Reset(pDevIns);
4590
4591 /*
4592 * Info items.
4593 */
4594 //PDMDevHlpDBGFInfoRegister(pDevIns, "ac97", "AC'97 registers. (ac97 [register case-insensitive])", ichac97R3DbgInfo);
4595 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97bdl", "AC'97 buffer descriptor list (BDL). (ac97bdl [stream number])",
4596 ichac97R3DbgInfoBDL);
4597 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97stream", "AC'97 stream info. (ac97stream [stream number])", ichac97R3DbgInfoStream);
4598 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97mixer", "AC'97 mixer state.", ichac97R3DbgInfoMixer);
4599
4600 /*
4601 * Register statistics.
4602 */
4603 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNabmReads, STAMTYPE_COUNTER, "UnimplementedNabmReads", STAMUNIT_OCCURENCES, "Unimplemented NABM register reads.");
4604 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNabmWrites, STAMTYPE_COUNTER, "UnimplementedNabmWrites", STAMUNIT_OCCURENCES, "Unimplemented NABM register writes.");
4605 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNamReads, STAMTYPE_COUNTER, "UnimplementedNamReads", STAMUNIT_OCCURENCES, "Unimplemented NAM register reads.");
4606 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNamWrites, STAMTYPE_COUNTER, "UnimplementedNamWrites", STAMUNIT_OCCURENCES, "Unimplemented NAM register writes.");
4607# ifdef VBOX_WITH_STATISTICS
4608 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ichac97Timer.");
4609# endif
4610 for (unsigned idxStream = 0; idxStream < RT_ELEMENTS(pThis->aStreams); idxStream++)
4611 {
4612 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].cbDmaPeriod, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4613 "Bytes to transfer in the current DMA period.", "Stream%u/cbTransferChunk", idxStream);
4614 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].Regs.cr, STAMTYPE_X8, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
4615 "Control register (CR), bit 0 is the run bit.", "Stream%u/reg-CR", idxStream);
4616 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].Regs.sr, STAMTYPE_X16, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
4617 "Status register (SR).", "Stream%u/reg-SR", idxStream);
4618 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.Cfg.Props.uHz, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_HZ,
4619 "The stream frequency.", "Stream%u/Hz", idxStream);
4620 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.Cfg.Props.cbFrame, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4621 "The frame size.", "Stream%u/FrameSize", idxStream);
4622 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offRead, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4623 "Virtual internal buffer read position.", "Stream%u/offRead", idxStream);
4624 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offWrite, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4625 "Virtual internal buffer write position.", "Stream%u/offWrite", idxStream);
4626 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaBufSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4627 "Size of the internal DMA buffer.", "Stream%u/DMABufSize", idxStream);
4628 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaBufUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4629 "Number of bytes used in the internal DMA buffer.", "Stream%u/DMABufUsed", idxStream);
4630 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowProblems, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4631 "Number of internal DMA buffer problems.", "Stream%u/DMABufferProblems", idxStream);
4632 if (ichac97R3GetDirFromSD(idxStream) == PDMAUDIODIR_OUT)
4633 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrors, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4634 "Number of internal DMA buffer overflows.", "Stream%u/DMABufferOverflows", idxStream);
4635 else
4636 {
4637 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrors, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4638 "Number of internal DMA buffer underuns.", "Stream%u/DMABufferUnderruns", idxStream);
4639 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrorBytes, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4640 "Number of bytes of silence added to cope with underruns.", "Stream%u/DMABufferSilence", idxStream);
4641 }
4642 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaSkippedDch, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4643 "DMA transfer period skipped, controller halted (DCH).", "Stream%u/DMASkippedDch", idxStream);
4644 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaSkippedPendingBcis, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4645 "DMA transfer period skipped because of BCIS pending.", "Stream%u/DMASkippedPendingBCIS", idxStream);
4646
4647 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatStart, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4648 "Starting the stream.", "Stream%u/Start", idxStream);
4649 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatStop, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4650 "Stopping the stream.", "Stream%u/Stop", idxStream);
4651 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReset, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4652 "Resetting the stream.", "Stream%u/Reset", idxStream);
4653 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReSetUpChanged, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4654 "ichac97R3StreamReSetUp when recreating the streams.", "Stream%u/ReSetUp-Change", idxStream);
4655 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReSetUpSame, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4656 "ichac97R3StreamReSetUp when no change.", "Stream%u/ReSetUp-NoChange", idxStream);
4657 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatWriteCr, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4658 "CR register writes.", "Stream%u/WriteCr", idxStream);
4659 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatWriteLviRecover, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4660 "LVI register writes recovering from underflow.", "Stream%u/WriteLviRecover", idxStream);
4661 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteLvi, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4662 "LVI register writes (non-recoving).", "Stream%u/WriteLvi", idxStream);
4663 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteSr1, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4664 "SR register 1-byte writes.", "Stream%u/WriteSr-1byte", idxStream);
4665 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteSr2, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4666 "SR register 2-byte writes.", "Stream%u/WriteSr-2byte", idxStream);
4667 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteBdBar, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4668 "BDBAR register writes.", "Stream%u/WriteBdBar", idxStream);
4669 }
4670
4671 LogFlowFuncLeaveRC(VINF_SUCCESS);
4672 return VINF_SUCCESS;
4673}
4674
4675#else /* !IN_RING3 */
4676
4677/**
4678 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
4679 */
4680static DECLCALLBACK(int) ichac97RZConstruct(PPDMDEVINS pDevIns)
4681{
4682 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4683 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4684
4685 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
4686 AssertRCReturn(rc, rc);
4687
4688 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNam, ichac97IoPortNamWrite, ichac97IoPortNamRead, NULL /*pvUser*/);
4689 AssertRCReturn(rc, rc);
4690 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNabm, ichac97IoPortNabmWrite, ichac97IoPortNabmRead, NULL /*pvUser*/);
4691 AssertRCReturn(rc, rc);
4692
4693 return VINF_SUCCESS;
4694}
4695
4696#endif /* !IN_RING3 */
4697
4698/**
4699 * The device registration structure.
4700 */
4701const PDMDEVREG g_DeviceICHAC97 =
4702{
4703 /* .u32Version = */ PDM_DEVREG_VERSION,
4704 /* .uReserved0 = */ 0,
4705 /* .szName = */ "ichac97",
4706 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
4707 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION /* stream clearnup with working drivers */,
4708 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
4709 /* .cMaxInstances = */ 1,
4710 /* .uSharedVersion = */ 42,
4711 /* .cbInstanceShared = */ sizeof(AC97STATE),
4712 /* .cbInstanceCC = */ CTX_EXPR(sizeof(AC97STATER3), 0, 0),
4713 /* .cbInstanceRC = */ 0,
4714 /* .cMaxPciDevices = */ 1,
4715 /* .cMaxMsixVectors = */ 0,
4716 /* .pszDescription = */ "ICH AC'97 Audio Controller",
4717#if defined(IN_RING3)
4718 /* .pszRCMod = */ "VBoxDDRC.rc",
4719 /* .pszR0Mod = */ "VBoxDDR0.r0",
4720 /* .pfnConstruct = */ ichac97R3Construct,
4721 /* .pfnDestruct = */ ichac97R3Destruct,
4722 /* .pfnRelocate = */ NULL,
4723 /* .pfnMemSetup = */ NULL,
4724 /* .pfnPowerOn = */ NULL,
4725 /* .pfnReset = */ ichac97R3Reset,
4726 /* .pfnSuspend = */ NULL,
4727 /* .pfnResume = */ NULL,
4728 /* .pfnAttach = */ ichac97R3Attach,
4729 /* .pfnDetach = */ ichac97R3Detach,
4730 /* .pfnQueryInterface = */ NULL,
4731 /* .pfnInitComplete = */ NULL,
4732 /* .pfnPowerOff = */ ichac97R3PowerOff,
4733 /* .pfnSoftReset = */ NULL,
4734 /* .pfnReserved0 = */ NULL,
4735 /* .pfnReserved1 = */ NULL,
4736 /* .pfnReserved2 = */ NULL,
4737 /* .pfnReserved3 = */ NULL,
4738 /* .pfnReserved4 = */ NULL,
4739 /* .pfnReserved5 = */ NULL,
4740 /* .pfnReserved6 = */ NULL,
4741 /* .pfnReserved7 = */ NULL,
4742#elif defined(IN_RING0)
4743 /* .pfnEarlyConstruct = */ NULL,
4744 /* .pfnConstruct = */ ichac97RZConstruct,
4745 /* .pfnDestruct = */ NULL,
4746 /* .pfnFinalDestruct = */ NULL,
4747 /* .pfnRequest = */ NULL,
4748 /* .pfnReserved0 = */ NULL,
4749 /* .pfnReserved1 = */ NULL,
4750 /* .pfnReserved2 = */ NULL,
4751 /* .pfnReserved3 = */ NULL,
4752 /* .pfnReserved4 = */ NULL,
4753 /* .pfnReserved5 = */ NULL,
4754 /* .pfnReserved6 = */ NULL,
4755 /* .pfnReserved7 = */ NULL,
4756#elif defined(IN_RC)
4757 /* .pfnConstruct = */ ichac97RZConstruct,
4758 /* .pfnReserved0 = */ NULL,
4759 /* .pfnReserved1 = */ NULL,
4760 /* .pfnReserved2 = */ NULL,
4761 /* .pfnReserved3 = */ NULL,
4762 /* .pfnReserved4 = */ NULL,
4763 /* .pfnReserved5 = */ NULL,
4764 /* .pfnReserved6 = */ NULL,
4765 /* .pfnReserved7 = */ NULL,
4766#else
4767# error "Not in IN_RING3, IN_RING0 or IN_RC!"
4768#endif
4769 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
4770};
4771
4772#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
4773
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