VirtualBox

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

Last change on this file since 98405 was 98405, checked in by vboxsync, 2 years ago

Audio: Fixed race condition between announcing remaining device DMA data on draining and fetching it in the async I/O mixer thread via AudioMixerSinkTransferFromCircBuf(). This is due to the device's DMA buffer being reset via its device-specific functions (i.e. ichac97R3StreamReset()) immediately after disabling the stream (i.e. ichac97R3StreamDisable()). See comment 2 for more details. bugref:10354

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette