VirtualBox

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

Last change on this file since 103137 was 103134, checked in by vboxsync, 10 months ago

Audio: More locking needed to avoid debug assertions when draining a stream on stream disable. This debug assertion will happen because EMT and the mixer's async I/O thread work on the same circular buffer. There can be situations, if no locking is being used, that the async I/O thread just has consumed the remaining data just a tad before EMT looks for remaining data within the same buffer when disabling the stream. Also, don't immediately (hard) reset the circular buffer when a stream gets disabled, as this will wipe the already announced amount of remaining data when draining a stream. bugref:10354

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 203.0 KB
Line 
1/* $Id: DevIchAc97.cpp 103134 2024-01-31 09:21:06Z 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 * @param pStreamCC The AC'97 stream to lock (ring-3).
1627 */
1628DECLINLINE(void) ichac97R3StreamLock(PAC97STREAMR3 pStreamCC)
1629{
1630 int rc2 = RTCritSectEnter(&pStreamCC->State.CritSect);
1631 AssertRC(rc2);
1632}
1633
1634/**
1635 * Unlocks a formerly locked AC'97 stream.
1636 *
1637 * @param pStreamCC The AC'97 stream to unlock (ring-3).
1638 */
1639DECLINLINE(void) ichac97R3StreamUnlock(PAC97STREAMR3 pStreamCC)
1640{
1641 int rc2 = RTCritSectLeave(&pStreamCC->State.CritSect);
1642 AssertRC(rc2);
1643}
1644
1645#endif /* IN_RING3 */
1646
1647/**
1648 * Updates the status register (SR) of an AC'97 audio stream.
1649 *
1650 * @param pDevIns The device instance.
1651 * @param pThis The shared AC'97 state.
1652 * @param pStream AC'97 stream to update SR for.
1653 * @param new_sr New value for status register (SR).
1654 */
1655static void ichac97StreamUpdateSR(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream, uint32_t new_sr)
1656{
1657 bool fSignal = false;
1658 int iIRQL = 0;
1659
1660 uint32_t new_mask = new_sr & AC97_SR_INT_MASK;
1661 uint32_t old_mask = pStream->Regs.sr & AC97_SR_INT_MASK;
1662
1663 if (new_mask ^ old_mask)
1664 {
1665 /** @todo Is IRQ deasserted when only one of status bits is cleared? */
1666 if (!new_mask)
1667 {
1668 fSignal = true;
1669 iIRQL = 0;
1670 }
1671 else if ((new_mask & AC97_SR_LVBCI) && (pStream->Regs.cr & AC97_CR_LVBIE))
1672 {
1673 fSignal = true;
1674 iIRQL = 1;
1675 }
1676 else if ((new_mask & AC97_SR_BCIS) && (pStream->Regs.cr & AC97_CR_IOCE))
1677 {
1678 fSignal = true;
1679 iIRQL = 1;
1680 }
1681 }
1682
1683 pStream->Regs.sr = new_sr;
1684
1685 LogFlowFunc(("IOC%d, LVB%d, sr=%#x, fSignal=%RTbool, IRQL=%d\n",
1686 pStream->Regs.sr & AC97_SR_BCIS, pStream->Regs.sr & AC97_SR_LVBCI, pStream->Regs.sr, fSignal, iIRQL));
1687
1688 if (fSignal)
1689 {
1690 static uint32_t const s_aMasks[] = { AC97_GS_PIINT, AC97_GS_POINT, AC97_GS_MINT };
1691 Assert(pStream->u8SD < AC97_MAX_STREAMS);
1692 if (iIRQL)
1693 pThis->glob_sta |= s_aMasks[pStream->u8SD];
1694 else
1695 pThis->glob_sta &= ~s_aMasks[pStream->u8SD];
1696
1697 LogFlowFunc(("Setting IRQ level=%d\n", iIRQL));
1698 PDMDevHlpPCISetIrq(pDevIns, 0, iIRQL);
1699 }
1700}
1701
1702/**
1703 * Writes a new value to a stream's status register (SR).
1704 *
1705 * @param pDevIns The device instance.
1706 * @param pThis The shared AC'97 device state.
1707 * @param pStream Stream to update SR for.
1708 * @param u32Val New value to set the stream's SR to.
1709 */
1710static void ichac97StreamWriteSR(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream, uint32_t u32Val)
1711{
1712 Log3Func(("[SD%RU8] SR <- %#x (sr %#x)\n", pStream->u8SD, u32Val, pStream->Regs.sr));
1713
1714 pStream->Regs.sr |= u32Val & ~(AC97_SR_RO_MASK | AC97_SR_WCLEAR_MASK);
1715 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr & ~(u32Val & AC97_SR_WCLEAR_MASK));
1716}
1717
1718#ifdef IN_RING3
1719
1720/**
1721 * Resets an AC'97 stream.
1722 *
1723 * @param pThis The shared AC'97 state.
1724 * @param pStream The AC'97 stream to reset (shared).
1725 * @param pStreamCC The AC'97 stream to reset (ring-3).
1726 */
1727static void ichac97R3StreamReset(PAC97STATE pThis, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
1728{
1729 ichac97R3StreamLock(pStreamCC);
1730
1731 LogFunc(("[SD%RU8]\n", pStream->u8SD));
1732
1733 /* Note: Do *not* reset the stream's circular buffer here, as the audio mixer still relies on
1734 * previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
1735 * Resetting the buffer here will cause a race condition. See @bugref{10354}. */
1736
1737 pStream->Regs.bdbar = 0;
1738 pStream->Regs.civ = 0;
1739 pStream->Regs.lvi = 0;
1740
1741 pStream->Regs.picb = 0;
1742 pStream->Regs.piv = 0; /* Note! Because this is also zero, we will actually start transferring with BDLE00. */
1743 pStream->Regs.cr &= AC97_CR_DONT_CLEAR_MASK;
1744 pStream->Regs.bd_valid = 0;
1745
1746 RT_ZERO(pThis->silence);
1747
1748 ichac97R3StreamUnlock(pStreamCC);
1749}
1750
1751/**
1752 * Retrieves a specific driver stream of a AC'97 driver.
1753 *
1754 * @returns Pointer to driver stream if found, or NULL if not found.
1755 * @param pDrv Driver to retrieve driver stream for.
1756 * @param enmDir Stream direction to retrieve.
1757 * @param enmPath Stream destination / source to retrieve.
1758 */
1759static PAC97DRIVERSTREAM ichac97R3MixerGetDrvStream(PAC97DRIVER pDrv, PDMAUDIODIR enmDir, PDMAUDIOPATH enmPath)
1760{
1761 if (enmDir == PDMAUDIODIR_IN)
1762 {
1763 LogFunc(("enmRecSource=%d\n", enmPath));
1764 switch (enmPath)
1765 {
1766 case PDMAUDIOPATH_IN_LINE:
1767 return &pDrv->LineIn;
1768 case PDMAUDIOPATH_IN_MIC:
1769 return &pDrv->MicIn;
1770 default:
1771 AssertFailedBreak();
1772 }
1773 }
1774 else if (enmDir == PDMAUDIODIR_OUT)
1775 {
1776 LogFunc(("enmPlaybackDst=%d\n", enmPath));
1777 switch (enmPath)
1778 {
1779 case PDMAUDIOPATH_OUT_FRONT:
1780 return &pDrv->Out;
1781 default:
1782 AssertFailedBreak();
1783 }
1784 }
1785 else
1786 AssertFailed();
1787
1788 return NULL;
1789}
1790
1791/**
1792 * Adds a driver stream to a specific mixer sink.
1793 *
1794 * Called by ichac97R3MixerAddDrvStreams() and ichac97R3MixerAddDrv().
1795 *
1796 * @returns VBox status code.
1797 * @param pDevIns The device instance.
1798 * @param pMixSink Mixer sink to add driver stream to.
1799 * @param pCfg Stream configuration to use.
1800 * @param pDrv Driver stream to add.
1801 */
1802static int ichac97R3MixerAddDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PCPDMAUDIOSTREAMCFG pCfg, PAC97DRIVER pDrv)
1803{
1804 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
1805 LogFunc(("[LUN#%RU8] %s\n", pDrv->uLUN, pCfg->szName));
1806
1807 int rc;
1808 PAC97DRIVERSTREAM pDrvStream = ichac97R3MixerGetDrvStream(pDrv, pCfg->enmDir, pCfg->enmPath);
1809 if (pDrvStream)
1810 {
1811 AssertMsg(pDrvStream->pMixStrm == NULL, ("[LUN#%RU8] Driver stream already present when it must not\n", pDrv->uLUN));
1812
1813 PAUDMIXSTREAM pMixStrm;
1814 rc = AudioMixerSinkCreateStream(pMixSink, pDrv->pConnector, pCfg, pDevIns, &pMixStrm);
1815 LogFlowFunc(("LUN#%RU8: Created stream \"%s\" for sink, rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
1816 if (RT_SUCCESS(rc))
1817 {
1818 rc = AudioMixerSinkAddStream(pMixSink, pMixStrm);
1819 LogFlowFunc(("LUN#%RU8: Added stream \"%s\" to sink, rc=%Rrc\n", pDrv->uLUN, pCfg->szName, rc));
1820 if (RT_SUCCESS(rc))
1821 pDrvStream->pMixStrm = pMixStrm;
1822 else
1823 AudioMixerStreamDestroy(pMixStrm, pDevIns, true /*fImmediate*/);
1824 }
1825 }
1826 else
1827 rc = VERR_INVALID_PARAMETER;
1828
1829 LogFlowFuncLeaveRC(rc);
1830 return rc;
1831}
1832
1833
1834/**
1835 * Adds all current driver streams to a specific mixer sink.
1836 *
1837 * Called by ichac97R3StreamSetUp().
1838 *
1839 * @returns VBox status code.
1840 * @param pDevIns The device instance.
1841 * @param pThisCC The ring-3 AC'97 state.
1842 * @param pMixSink Mixer sink to add stream to.
1843 * @param pCfg Stream configuration to use.
1844 */
1845static int ichac97R3MixerAddDrvStreams(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAUDMIXSINK pMixSink, PCPDMAUDIOSTREAMCFG pCfg)
1846{
1847 AssertPtrReturn(pMixSink, VERR_INVALID_POINTER);
1848
1849 int rc;
1850 if (AudioHlpStreamCfgIsValid(pCfg))
1851 {
1852 rc = AudioMixerSinkSetFormat(pMixSink, &pCfg->Props, pCfg->Device.cMsSchedulingHint);
1853 if (RT_SUCCESS(rc))
1854 {
1855 PAC97DRIVER pDrv;
1856 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
1857 {
1858 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pMixSink, pCfg, pDrv);
1859 if (RT_FAILURE(rc2))
1860 LogFunc(("Attaching stream failed with %Rrc\n", rc2));
1861
1862 /* Do not pass failure to rc here, as there might be drivers which aren't
1863 configured / ready yet. */
1864 }
1865 }
1866 }
1867 else
1868 rc = VERR_INVALID_PARAMETER;
1869
1870 LogFlowFuncLeaveRC(rc);
1871 return rc;
1872}
1873
1874
1875/**
1876 * Removes a driver stream from a specific mixer sink.
1877 *
1878 * Worker for ichac97R3MixerRemoveDrvStreams.
1879 *
1880 * @param pDevIns The device instance.
1881 * @param pMixSink Mixer sink to remove audio streams from.
1882 * @param enmDir Stream direction to remove.
1883 * @param enmPath Stream destination / source to remove.
1884 * @param pDrv Driver stream to remove.
1885 */
1886static void ichac97R3MixerRemoveDrvStream(PPDMDEVINS pDevIns, PAUDMIXSINK pMixSink, PDMAUDIODIR enmDir,
1887 PDMAUDIOPATH enmPath, PAC97DRIVER pDrv)
1888{
1889 PAC97DRIVERSTREAM pDrvStream = ichac97R3MixerGetDrvStream(pDrv, enmDir, enmPath);
1890 if (pDrvStream)
1891 {
1892 if (pDrvStream->pMixStrm)
1893 {
1894 AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm);
1895
1896 AudioMixerStreamDestroy(pDrvStream->pMixStrm, pDevIns, false /*fImmediate*/);
1897 pDrvStream->pMixStrm = NULL;
1898 }
1899 }
1900}
1901
1902/**
1903 * Removes all driver streams from a specific mixer sink.
1904 *
1905 * Called by ichac97R3StreamSetUp() and ichac97R3StreamsDestroy().
1906 *
1907 * @param pDevIns The device instance.
1908 * @param pThisCC The ring-3 AC'97 state.
1909 * @param pMixSink Mixer sink to remove audio streams from.
1910 * @param enmDir Stream direction to remove.
1911 * @param enmPath Stream destination / source to remove.
1912 */
1913static void ichac97R3MixerRemoveDrvStreams(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAUDMIXSINK pMixSink,
1914 PDMAUDIODIR enmDir, PDMAUDIOPATH enmPath)
1915{
1916 AssertPtrReturnVoid(pMixSink);
1917
1918 PAC97DRIVER pDrv;
1919 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
1920 {
1921 ichac97R3MixerRemoveDrvStream(pDevIns, pMixSink, enmDir, enmPath, pDrv);
1922 }
1923}
1924
1925
1926/**
1927 * Gets the frequency of a given stream.
1928 *
1929 * @returns The frequency. Zero if invalid stream index.
1930 * @param pThis The shared AC'97 device state.
1931 * @param idxStream The stream.
1932 */
1933DECLINLINE(uint32_t) ichach97R3CalcStreamHz(PAC97STATE pThis, uint8_t idxStream)
1934{
1935 switch (idxStream)
1936 {
1937 case AC97SOUNDSOURCE_PI_INDEX:
1938 return ichac97MixerGet(pThis, AC97_PCM_LR_ADC_Rate);
1939
1940 case AC97SOUNDSOURCE_MC_INDEX:
1941 return ichac97MixerGet(pThis, AC97_MIC_ADC_Rate);
1942
1943 case AC97SOUNDSOURCE_PO_INDEX:
1944 return ichac97MixerGet(pThis, AC97_PCM_Front_DAC_Rate);
1945
1946 default:
1947 AssertMsgFailedReturn(("%d\n", idxStream), 0);
1948 }
1949}
1950
1951
1952/**
1953 * Gets the PCM properties for a given stream.
1954 *
1955 * @returns pProps.
1956 * @param pThis The shared AC'97 device state.
1957 * @param idxStream Which stream
1958 * @param pProps Where to return the stream properties.
1959 */
1960DECLINLINE(PPDMAUDIOPCMPROPS) ichach97R3CalcStreamProps(PAC97STATE pThis, uint8_t idxStream, PPDMAUDIOPCMPROPS pProps)
1961{
1962 PDMAudioPropsInit(pProps, 2 /*16-bit*/, true /*signed*/, 2 /*stereo*/, ichach97R3CalcStreamHz(pThis, idxStream));
1963 return pProps;
1964}
1965
1966
1967/**
1968 * Sets up an AC'97 stream with its current mixer settings.
1969 *
1970 * This will set up an AC'97 stream with 2 (stereo) channels, 16-bit samples and
1971 * the last set sample rate in the AC'97 mixer for this stream.
1972 *
1973 * @returns VBox status code.
1974 * @retval VINF_NO_CHANGE if the streams weren't re-created.
1975 *
1976 * @param pDevIns The device instance.
1977 * @param pThis The shared AC'97 device state (shared).
1978 * @param pThisCC The shared AC'97 device state (ring-3).
1979 * @param pStream The AC'97 stream to open (shared).
1980 * @param pStreamCC The AC'97 stream to open (ring-3).
1981 * @param fForce Whether to force re-opening the stream or not.
1982 * Otherwise re-opening only will happen if the PCM properties have changed.
1983 *
1984 * @remarks This is called holding:
1985 * -# The AC'97 device lock.
1986 * -# The AC'97 stream lock.
1987 * -# The mixer sink lock (to prevent racing AIO thread).
1988 */
1989static int ichac97R3StreamSetUp(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC, PAC97STREAM pStream,
1990 PAC97STREAMR3 pStreamCC, bool fForce)
1991{
1992 /*
1993 * Assemble the stream config and get the associated mixer sink.
1994 */
1995 PDMAUDIOPCMPROPS PropsTmp;
1996 PDMAUDIOSTREAMCFG Cfg;
1997 PDMAudioStrmCfgInitWithProps(&Cfg, ichach97R3CalcStreamProps(pThis, pStream->u8SD, &PropsTmp));
1998 Assert(Cfg.enmDir != PDMAUDIODIR_UNKNOWN);
1999
2000 PAUDMIXSINK pMixSink;
2001 switch (pStream->u8SD)
2002 {
2003 case AC97SOUNDSOURCE_PI_INDEX:
2004 Cfg.enmDir = PDMAUDIODIR_IN;
2005 Cfg.enmPath = PDMAUDIOPATH_IN_LINE;
2006 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Line-In");
2007
2008 pMixSink = pThisCC->pSinkLineIn;
2009 break;
2010
2011 case AC97SOUNDSOURCE_MC_INDEX:
2012 Cfg.enmDir = PDMAUDIODIR_IN;
2013 Cfg.enmPath = PDMAUDIOPATH_IN_MIC;
2014 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Mic-In");
2015
2016 pMixSink = pThisCC->pSinkMicIn;
2017 break;
2018
2019 case AC97SOUNDSOURCE_PO_INDEX:
2020 Cfg.enmDir = PDMAUDIODIR_OUT;
2021 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
2022 RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output");
2023
2024 pMixSink = pThisCC->pSinkOut;
2025 break;
2026
2027 default:
2028 AssertMsgFailedReturn(("u8SD=%d\n", pStream->u8SD), VERR_INTERNAL_ERROR_3);
2029 }
2030
2031 /* Validate locks -- see @bugref{10350}. */
2032 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
2033 Assert(RTCritSectIsOwned(&pStreamCC->State.CritSect));
2034 Assert(AudioMixerSinkLockIsOwner(pMixSink));
2035
2036 /*
2037 * Don't continue if the frequency is out of range (the rest of the
2038 * properties should be okay).
2039 * Note! Don't assert on this as we may easily end up here with Hz=0.
2040 */
2041 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX];
2042 if (AudioHlpStreamCfgIsValid(&Cfg))
2043 { }
2044 else
2045 {
2046 LogFunc(("Invalid stream #%u rate: %s\n", pStreamCC->u8SD, PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp)) ));
2047 return VERR_OUT_OF_RANGE;
2048 }
2049
2050 /*
2051 * Read the buffer descriptors and check what the max distance between
2052 * interrupts are, so we can more correctly size the internal DMA buffer.
2053 *
2054 * Note! The buffer list are not fixed once the stream starts running as
2055 * with HDA, so this is just a general idea of what the guest is
2056 * up to and we cannot really make much of a plan out of it.
2057 */
2058 uint8_t const bLvi = pStream->Regs.lvi % AC97_MAX_BDLE /* paranoia */;
2059 uint8_t const bCiv = pStream->Regs.civ % AC97_MAX_BDLE /* paranoia */;
2060 uint32_t const uAddrBdl = pStream->Regs.bdbar;
2061
2062 /* Linux does this a number of times while probing/whatever the device. The
2063 IOMMU usually does allow us to read address zero, so let's skip and hope
2064 for a better config before the guest actually wants to play/record.
2065 (Note that bLvi and bCiv are also zero then, but I'm not entirely sure if
2066 that can be taken to mean anything as such, as it still indicates that
2067 BDLE00 is valid (LVI == last valid index).) */
2068 /** @todo Instead of refusing to read address zero, we should probably allow
2069 * reading address zero if explicitly programmed. But, too much work now. */
2070 if (uAddrBdl != 0)
2071 LogFlowFunc(("bdbar=%#x bLvi=%#x bCiv=%#x\n", uAddrBdl, bLvi, bCiv));
2072 else
2073 {
2074 LogFunc(("Invalid stream #%u: bdbar=%#x bLvi=%#x bCiv=%#x (%s)\n", pStreamCC->u8SD, uAddrBdl, bLvi, bCiv,
2075 PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp))));
2076 return VERR_OUT_OF_RANGE;
2077 }
2078
2079 AC97BDLE aBdl[AC97_MAX_BDLE];
2080 RT_ZERO(aBdl);
2081 PDMDevHlpPCIPhysRead(pDevIns, uAddrBdl, aBdl, sizeof(aBdl));
2082
2083 uint32_t cSamplesMax = 0;
2084 uint32_t cSamplesMin = UINT32_MAX;
2085 uint32_t cSamplesCur = 0;
2086 uint32_t cSamplesTotal = 0;
2087 uint32_t cBuffers = 1;
2088 for (uintptr_t i = bCiv; ; cBuffers++)
2089 {
2090 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));
2091 cSamplesTotal += aBdl[i].ctl_len & AC97_BD_LEN_MASK;
2092 cSamplesCur += aBdl[i].ctl_len & AC97_BD_LEN_MASK;
2093 if (aBdl[i].ctl_len & AC97_BD_IOC)
2094 {
2095 if (cSamplesCur > cSamplesMax)
2096 cSamplesMax = cSamplesCur;
2097 if (cSamplesCur < cSamplesMin)
2098 cSamplesMin = cSamplesCur;
2099 cSamplesCur = 0;
2100 }
2101
2102 /* Advance. */
2103 if (i != bLvi)
2104 i = (i + 1) % RT_ELEMENTS(aBdl);
2105 else
2106 break;
2107 }
2108 if (!cSamplesCur)
2109 { /* likely */ }
2110 else if (!cSamplesMax)
2111 {
2112 LogFlowFunc(("%u buffers without IOC set, assuming %#x samples as the IOC period.\n", cBuffers, cSamplesMax));
2113 cSamplesMin = cSamplesMax = cSamplesCur;
2114 }
2115 else if (cSamplesCur > cSamplesMax)
2116 {
2117 LogFlowFunc(("final buffer is without IOC, using open period as max (%#x vs current max %#x).\n", cSamplesCur, cSamplesMax));
2118 cSamplesMax = cSamplesCur;
2119 }
2120 else
2121 LogFlowFunc(("final buffer is without IOC, ignoring (%#x vs current max %#x).\n", cSamplesCur, cSamplesMax));
2122
2123 uint32_t const cbDmaMinBuf = cSamplesMax * PDMAudioPropsSampleSize(&Cfg.Props) * 3; /* see further down */
2124 uint32_t const cMsDmaMinBuf = PDMAudioPropsBytesToMilli(&Cfg.Props, cbDmaMinBuf);
2125 LogRel3(("AC97: [SD%RU8] buffer length stats: total=%#x in %u buffers, min=%#x, max=%#x => min DMA buffer %u ms / %#x bytes\n",
2126 pStream->u8SD, cSamplesTotal, cBuffers, cSamplesMin, cSamplesMax, cMsDmaMinBuf, cbDmaMinBuf));
2127
2128 /*
2129 * Calculate the timer Hz / scheduling hint based on the stream frame rate.
2130 */
2131 uint32_t uTimerHz;
2132 if (pThis->uTimerHz == AC97_TIMER_HZ_DEFAULT) /* Make sure that we don't have any custom Hz rate set we want to enforce */
2133 {
2134 if (Cfg.Props.uHz > 44100) /* E.g. 48000 Hz. */
2135 uTimerHz = 200;
2136 else
2137 uTimerHz = AC97_TIMER_HZ_DEFAULT;
2138 }
2139 else
2140 uTimerHz = pThis->uTimerHz;
2141
2142 if ( uTimerHz >= 10
2143 && uTimerHz <= 500)
2144 { /* likely */ }
2145 else
2146 {
2147 LogFunc(("[SD%RU8] Adjusting uTimerHz=%u to %u\n", pStream->u8SD, uTimerHz,
2148 Cfg.Props.uHz > 44100 ? 200 : AC97_TIMER_HZ_DEFAULT));
2149 uTimerHz = Cfg.Props.uHz > 44100 ? 200 : AC97_TIMER_HZ_DEFAULT;
2150 }
2151
2152 /* Translate it to a scheduling hint. */
2153 uint32_t const cMsSchedulingHint = RT_MS_1SEC / uTimerHz;
2154
2155 /*
2156 * Calculate the circular buffer size so we can decide whether to recreate
2157 * the stream or not.
2158 *
2159 * As mentioned in the HDA code, this should be at least able to hold the
2160 * data transferred in three DMA periods and in three AIO period (whichever
2161 * is higher). However, if we assume that the DMA code will engage the DMA
2162 * timer thread (currently EMT) if the AIO thread isn't getting schduled to
2163 * transfer data thru the stack, we don't need to go overboard and double
2164 * the minimums here. The less buffer the less possible delay can build when
2165 * TM is doing catch up.
2166 */
2167 uint32_t cMsCircBuf = Cfg.enmDir == PDMAUDIODIR_IN ? pThis->cMsCircBufIn : pThis->cMsCircBufOut;
2168 cMsCircBuf = RT_MAX(cMsCircBuf, cMsDmaMinBuf);
2169 cMsCircBuf = RT_MAX(cMsCircBuf, cMsSchedulingHint * 3);
2170 cMsCircBuf = RT_MIN(cMsCircBuf, RT_MS_1SEC * 2);
2171 uint32_t const cbCircBuf = PDMAudioPropsMilliToBytes(&Cfg.Props, cMsCircBuf);
2172
2173 LogFlowFunc(("Stream %u: uTimerHz: %u -> %u; cMsSchedulingHint: %u -> %u; cbCircBuf: %#zx -> %#x (%u ms, cMsDmaMinBuf=%u)%s\n",
2174 pStreamCC->u8SD, pStreamCC->State.uTimerHz, uTimerHz,
2175 pStreamCC->State.Cfg.Device.cMsSchedulingHint, cMsSchedulingHint,
2176 pStreamCC->State.pCircBuf ? RTCircBufSize(pStreamCC->State.pCircBuf) : 0, cbCircBuf, cMsCircBuf, cMsDmaMinBuf,
2177 !pStreamCC->State.pCircBuf || RTCircBufSize(pStreamCC->State.pCircBuf) != cbCircBuf ? " - re-creating DMA buffer" : ""));
2178
2179 /*
2180 * Update the stream's timer rate and scheduling hint, re-registering the AIO
2181 * update job if necessary.
2182 */
2183 if ( pStreamCC->State.Cfg.Device.cMsSchedulingHint != cMsSchedulingHint
2184 || !pStreamCC->State.fRegisteredAsyncUpdateJob)
2185 {
2186 if (pStreamCC->State.fRegisteredAsyncUpdateJob)
2187 AudioMixerSinkRemoveUpdateJob(pMixSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC);
2188 int rc2 = AudioMixerSinkAddUpdateJob(pMixSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC,
2189 pStreamCC->State.Cfg.Device.cMsSchedulingHint);
2190 AssertRC(rc2);
2191 pStreamCC->State.fRegisteredAsyncUpdateJob = RT_SUCCESS(rc2) || rc2 == VERR_ALREADY_EXISTS;
2192 }
2193
2194 pStreamCC->State.uTimerHz = uTimerHz;
2195 Cfg.Device.cMsSchedulingHint = cMsSchedulingHint;
2196
2197 /*
2198 * Re-create the circular buffer if necessary, resetting if not.
2199 */
2200 if ( pStreamCC->State.pCircBuf
2201 && RTCircBufSize(pStreamCC->State.pCircBuf) == cbCircBuf)
2202 {
2203 /* Note: Do *not* reset the stream's circular buffer here, as the audio mixer still relies on
2204 * previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
2205 * Resetting the buffer here will cause a race condition. See @bugref{10354}. */
2206 }
2207 else
2208 {
2209 if (pStreamCC->State.pCircBuf)
2210 RTCircBufDestroy(pStreamCC->State.pCircBuf);
2211
2212 int rc = RTCircBufCreate(&pStreamCC->State.pCircBuf, cbCircBuf);
2213 AssertRCReturnStmt(rc, pStreamCC->State.pCircBuf = NULL, rc);
2214
2215 pStreamCC->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStreamCC->State.pCircBuf);
2216 }
2217 Assert(pStreamCC->State.StatDmaBufSize == cbCircBuf);
2218
2219 /*
2220 * Only (re-)create the stream (and driver chain) if we really have to.
2221 * Otherwise avoid this and just reuse it, as this costs performance.
2222 */
2223 int rc = VINF_SUCCESS;
2224 if ( fForce
2225 || !PDMAudioStrmCfgMatchesProps(&Cfg, &pStreamCC->State.Cfg.Props)
2226 || (pStreamCC->State.nsRetrySetup && RTTimeNanoTS() >= pStreamCC->State.nsRetrySetup))
2227 {
2228 LogRel2(("AC97: Setting up stream #%u: %s\n", pStreamCC->u8SD, PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp)) ));
2229
2230 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pMixSink, Cfg.enmDir, Cfg.enmPath);
2231
2232 rc = ichac97R3MixerAddDrvStreams(pDevIns, pThisCC, pMixSink, &Cfg);
2233 if (RT_SUCCESS(rc))
2234 {
2235 PDMAudioStrmCfgCopy(&pStreamCC->State.Cfg, &Cfg);
2236 pStreamCC->State.nsRetrySetup = 0;
2237 LogFlowFunc(("[SD%RU8] success (uHz=%u)\n", pStreamCC->u8SD, PDMAudioPropsHz(&Cfg.Props)));
2238 }
2239 else
2240 {
2241 LogFunc(("[SD%RU8] ichac97R3MixerAddDrvStreams failed: %Rrc (uHz=%u)\n",
2242 pStreamCC->u8SD, rc, PDMAudioPropsHz(&Cfg.Props)));
2243 pStreamCC->State.nsRetrySetup = RTTimeNanoTS() + 5*RT_NS_1SEC_64; /* retry in 5 seconds, unless config changes. */
2244 }
2245 }
2246 else
2247 {
2248 LogFlowFunc(("[SD%RU8] Skipping set-up (unchanged: %s)\n",
2249 pStreamCC->u8SD, PDMAudioStrmCfgToString(&Cfg, szTmp, sizeof(szTmp))));
2250 rc = VINF_NO_CHANGE;
2251 }
2252 return rc;
2253}
2254
2255
2256/**
2257 * Tears down an AC'97 stream (counter part to ichac97R3StreamSetUp).
2258 *
2259 * Empty stub at present, nothing to do here as we reuse streams and only really
2260 * re-open them if parameters changed (seldom).
2261 *
2262 * @param pStream The AC'97 stream to close (shared).
2263 */
2264static void ichac97R3StreamTearDown(PAC97STREAM pStream)
2265{
2266 RT_NOREF(pStream);
2267 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2268}
2269
2270
2271/**
2272 * Tears down and sets up an AC'97 stream on the backend side with the current
2273 * AC'97 mixer settings for this stream.
2274 *
2275 * @returns VBox status code.
2276 * @param pDevIns The device instance.
2277 * @param pThis The shared AC'97 device state.
2278 * @param pThisCC The ring-3 AC'97 device state.
2279 * @param pStream The AC'97 stream to re-open (shared).
2280 * @param pStreamCC The AC'97 stream to re-open (ring-3).
2281 * @param fForce Whether to force re-opening the stream or not.
2282 * Otherwise re-opening only will happen if the PCM properties have changed.
2283 *
2284 * @remarks This is called holding:
2285 * -# The AC'97 device lock.
2286 *
2287 * Will acquire the stream and mixer sink locks. See @bugref{10350}
2288 */
2289static int ichac97R3StreamReSetUp(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC,
2290 PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, bool fForce)
2291{
2292 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatReSetUpChanged, r);
2293 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2294 Assert(pStream->u8SD == pStreamCC->u8SD);
2295 Assert(pStream - &pThis->aStreams[0] == pStream->u8SD);
2296 Assert(pStreamCC - &pThisCC->aStreams[0] == pStream->u8SD);
2297
2298 ichac97R3StreamLock(pStreamCC);
2299 PAUDMIXSINK const pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2300 if (pSink)
2301 AudioMixerSinkLock(pSink);
2302
2303 ichac97R3StreamTearDown(pStream);
2304 int rc = ichac97R3StreamSetUp(pDevIns, pThis, pThisCC, pStream, pStreamCC, fForce);
2305 if (rc == VINF_NO_CHANGE)
2306 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReSetUpSame, r);
2307 else
2308 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReSetUpChanged, r);
2309
2310 if (pSink)
2311 AudioMixerSinkUnlock(pSink);
2312 ichac97R3StreamUnlock(pStreamCC);
2313
2314 return rc;
2315}
2316
2317
2318/**
2319 * Enables or disables an AC'97 audio stream.
2320 *
2321 * @returns VBox status code.
2322 * @param pDevIns The device instance.
2323 * @param pThis The shared AC'97 state.
2324 * @param pThisCC The ring-3 AC'97 state.
2325 * @param pStream The AC'97 stream to enable or disable (shared state).
2326 * @param pStreamCC The ring-3 stream state (matching to @a pStream).
2327 * @param fEnable Whether to enable or disable the stream.
2328 *
2329 */
2330static int ichac97R3StreamEnable(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC,
2331 PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, bool fEnable)
2332{
2333 ichac97R3StreamLock(pStreamCC);
2334 PAUDMIXSINK const pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2335 if (pSink)
2336 AudioMixerSinkLock(pSink);
2337
2338 int rc = VINF_SUCCESS;
2339 /*
2340 * Enable.
2341 */
2342 if (fEnable)
2343 {
2344 /* Reset the input pre-buffering state and DMA period counter. */
2345 pStreamCC->State.fInputPreBuffered = false;
2346 pStream->uDmaPeriod = 0;
2347
2348 /* Set up (update) the AC'97 stream as needed. */
2349 rc = ichac97R3StreamSetUp(pDevIns, pThis, pThisCC, pStream, pStreamCC, false /* fForce */);
2350 if (RT_SUCCESS(rc))
2351 {
2352 /* Open debug files. */
2353 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2354 { /* likely */ }
2355 else
2356 {
2357 if (!AudioHlpFileIsOpen(pStreamCC->Dbg.Runtime.pFileStream))
2358 AudioHlpFileOpen(pStreamCC->Dbg.Runtime.pFileStream, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2359 &pStreamCC->State.Cfg.Props);
2360 if (!AudioHlpFileIsOpen(pStreamCC->Dbg.Runtime.pFileDMA))
2361 AudioHlpFileOpen(pStreamCC->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
2362 &pStreamCC->State.Cfg.Props);
2363 }
2364
2365 /* Do the actual enabling (won't fail as long as pSink is valid). */
2366 if (pSink)
2367 rc = AudioMixerSinkStart(pSink);
2368 }
2369 }
2370 /*
2371 * Disable
2372 */
2373 else
2374 {
2375 rc = AudioMixerSinkDrainAndStop(pSink, pStreamCC->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamCC->State.pCircBuf) : 0);
2376 ichac97R3StreamTearDown(pStream);
2377 }
2378
2379 /* Make sure to leave the lock before (eventually) starting the timer. */
2380 if (pSink)
2381 AudioMixerSinkUnlock(pSink);
2382 ichac97R3StreamUnlock(pStreamCC);
2383 LogFunc(("[SD%RU8] fEnable=%RTbool, rc=%Rrc\n", pStream->u8SD, fEnable, rc));
2384 return rc;
2385}
2386
2387
2388/**
2389 * Returns whether an AC'97 stream is enabled or not.
2390 *
2391 * Only used by ichac97R3SaveExec().
2392 *
2393 * @returns VBox status code.
2394 * @param pThisCC The ring-3 AC'97 device state.
2395 * @param pStream Stream to return status for.
2396 */
2397static bool ichac97R3StreamIsEnabled(PAC97STATER3 pThisCC, PAC97STREAM pStream)
2398{
2399 PAUDMIXSINK pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2400 bool fIsEnabled = pSink && (AudioMixerSinkGetStatus(pSink) & AUDMIXSINK_STS_RUNNING);
2401
2402 LogFunc(("[SD%RU8] fIsEnabled=%RTbool\n", pStream->u8SD, fIsEnabled));
2403 return fIsEnabled;
2404}
2405
2406
2407/**
2408 * Terminates an AC'97 audio stream (VM destroy).
2409 *
2410 * This is called by ichac97R3StreamsDestroy during VM poweroff & destruction.
2411 *
2412 * @param pThisCC The ring-3 AC'97 state.
2413 * @param pStream The AC'97 stream to destroy (shared).
2414 * @param pStreamCC The AC'97 stream to destroy (ring-3).
2415 * @sa ichac97R3StreamConstruct
2416 */
2417static void ichac97R3StreamDestroy(PAC97STATER3 pThisCC, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC)
2418{
2419 LogFlowFunc(("[SD%RU8]\n", pStream->u8SD));
2420
2421 ichac97R3StreamTearDown(pStream);
2422
2423 int rc2 = RTCritSectDelete(&pStreamCC->State.CritSect);
2424 AssertRC(rc2);
2425
2426 if (pStreamCC->State.fRegisteredAsyncUpdateJob)
2427 {
2428 PAUDMIXSINK pSink = ichac97R3IndexToSink(pThisCC, pStream->u8SD);
2429 if (pSink)
2430 AudioMixerSinkRemoveUpdateJob(pSink, ichac97R3StreamUpdateAsyncIoJob, pStreamCC);
2431 pStreamCC->State.fRegisteredAsyncUpdateJob = false;
2432 }
2433
2434 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2435 { /* likely */ }
2436 else
2437 {
2438 AudioHlpFileDestroy(pStreamCC->Dbg.Runtime.pFileStream);
2439 pStreamCC->Dbg.Runtime.pFileStream = NULL;
2440
2441 AudioHlpFileDestroy(pStreamCC->Dbg.Runtime.pFileDMA);
2442 pStreamCC->Dbg.Runtime.pFileDMA = NULL;
2443 }
2444
2445 if (pStreamCC->State.pCircBuf)
2446 {
2447 RTCircBufDestroy(pStreamCC->State.pCircBuf);
2448 pStreamCC->State.pCircBuf = NULL;
2449 }
2450
2451 LogFlowFuncLeave();
2452}
2453
2454
2455/**
2456 * Initializes an AC'97 audio stream (VM construct).
2457 *
2458 * This is only called by ichac97R3Construct.
2459 *
2460 * @returns VBox status code.
2461 * @param pThisCC The ring-3 AC'97 state.
2462 * @param pStream The AC'97 stream to create (shared).
2463 * @param pStreamCC The AC'97 stream to create (ring-3).
2464 * @param u8SD Stream descriptor number to assign.
2465 * @sa ichac97R3StreamDestroy
2466 */
2467static int ichac97R3StreamConstruct(PAC97STATER3 pThisCC, PAC97STREAM pStream, PAC97STREAMR3 pStreamCC, uint8_t u8SD)
2468{
2469 LogFunc(("[SD%RU8] pStream=%p\n", u8SD, pStream));
2470
2471 AssertReturn(u8SD < AC97_MAX_STREAMS, VERR_INVALID_PARAMETER);
2472 pStream->u8SD = u8SD;
2473 pStreamCC->u8SD = u8SD;
2474
2475 int rc = RTCritSectInit(&pStreamCC->State.CritSect);
2476 AssertRCReturn(rc, rc);
2477
2478 pStreamCC->Dbg.Runtime.fEnabled = pThisCC->Dbg.fEnabled;
2479
2480 if (RT_LIKELY(!pStreamCC->Dbg.Runtime.fEnabled))
2481 { /* likely */ }
2482 else
2483 {
2484 int rc2 = AudioHlpFileCreateF(&pStreamCC->Dbg.Runtime.pFileStream, AUDIOHLPFILE_FLAGS_NONE, AUDIOHLPFILETYPE_WAV,
2485 pThisCC->Dbg.pszOutPath, AUDIOHLPFILENAME_FLAGS_NONE, 0 /*uInstance*/,
2486 ichac97R3GetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN
2487 ? "ac97StreamWriteSD%RU8" : "ac97StreamReadSD%RU8", pStream->u8SD);
2488 AssertRC(rc2);
2489
2490 rc2 = AudioHlpFileCreateF(&pStreamCC->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_FLAGS_NONE, AUDIOHLPFILETYPE_WAV,
2491 pThisCC->Dbg.pszOutPath, AUDIOHLPFILENAME_FLAGS_NONE, 0 /*uInstance*/,
2492 ichac97R3GetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN
2493 ? "ac97DMAWriteSD%RU8" : "ac97DMAReadSD%RU8", pStream->u8SD);
2494 AssertRC(rc2);
2495
2496 /* Delete stale debugging files from a former run. */
2497 AudioHlpFileDelete(pStreamCC->Dbg.Runtime.pFileStream);
2498 AudioHlpFileDelete(pStreamCC->Dbg.Runtime.pFileDMA);
2499 }
2500
2501 return rc;
2502}
2503
2504#endif /* IN_RING3 */
2505
2506
2507/*********************************************************************************************************************************
2508* NABM I/O Port Handlers (Global + Stream) *
2509*********************************************************************************************************************************/
2510
2511/**
2512 * @callback_method_impl{FNIOMIOPORTNEWIN}
2513 */
2514static DECLCALLBACK(VBOXSTRICTRC)
2515ichac97IoPortNabmRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2516{
2517 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
2518 RT_NOREF(pvUser);
2519
2520 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
2521
2522 /* Get the index of the NABMBAR port. */
2523 if ( AC97_PORT2IDX_UNMASKED(offPort) < AC97_MAX_STREAMS
2524 && offPort != AC97_GLOB_CNT)
2525 {
2526 PAC97STREAM pStream = &pThis->aStreams[AC97_PORT2IDX(offPort)];
2527
2528 switch (cb)
2529 {
2530 case 1:
2531 switch (offPort & AC97_NABM_OFF_MASK)
2532 {
2533 case AC97_NABM_OFF_CIV:
2534 /* Current Index Value Register */
2535 *pu32 = pStream->Regs.civ;
2536 Log3Func(("CIV[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2537 break;
2538 case AC97_NABM_OFF_LVI:
2539 /* Last Valid Index Register */
2540 *pu32 = pStream->Regs.lvi;
2541 Log3Func(("LVI[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2542 break;
2543 case AC97_NABM_OFF_PIV:
2544 /* Prefetched Index Value Register */
2545 *pu32 = pStream->Regs.piv;
2546 Log3Func(("PIV[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2547 break;
2548 case AC97_NABM_OFF_CR:
2549 /* Control Register */
2550 *pu32 = pStream->Regs.cr;
2551 Log3Func(("CR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2552 break;
2553 case AC97_NABM_OFF_SR:
2554 /* Status Register (lower part) */
2555 *pu32 = RT_LO_U8(pStream->Regs.sr);
2556 Log3Func(("SRb[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2557 break;
2558 default:
2559 *pu32 = UINT32_MAX;
2560 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
2561 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2562 break;
2563 }
2564 break;
2565
2566 case 2:
2567 switch (offPort & AC97_NABM_OFF_MASK)
2568 {
2569 case AC97_NABM_OFF_SR:
2570 /* Status Register */
2571 *pu32 = pStream->Regs.sr;
2572 Log3Func(("SR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2573 break;
2574 case AC97_NABM_OFF_PICB:
2575 /* Position in Current Buffer
2576 * ---
2577 * We can do DMA work here if we want to give the guest a better impression of
2578 * the DMA engine of a real device. For ring-0 we'd have to add some buffering
2579 * to AC97STREAM (4K or so), only going to ring-3 if full. Ring-3 would commit
2580 * that buffer and write directly to the internal DMA pCircBuf.
2581 *
2582 * Checking a Linux guest (knoppix 8.6.2), I see some PIC reads each DMA cycle,
2583 * however most of these happen very very early, 1-10% into the buffer. So, I'm
2584 * not sure if it's worth it, as it'll be a big complication... */
2585#if 1
2586 *pu32 = pStream->Regs.picb;
2587# ifdef LOG_ENABLED
2588 if (LogIs3Enabled())
2589 {
2590 uint64_t offPeriod = PDMDevHlpTimerGet(pDevIns, pStream->hTimer) - pStream->uArmedTs;
2591 Log3Func(("PICB[%d] -> %#x (%RU64 of %RU64 ticks / %RU64%% into DMA period #%RU32)\n",
2592 AC97_PORT2IDX(offPort), *pu32, offPeriod, pStream->cDmaPeriodTicks,
2593 pStream->cDmaPeriodTicks ? offPeriod * 100 / pStream->cDmaPeriodTicks : 0,
2594 pStream->uDmaPeriod));
2595 }
2596# endif
2597#else /* For trying out sub-buffer PICB. Will cause distortions, but can be helpful to see if it help eliminate other issues. */
2598 if ( (pStream->Regs.cr & AC97_CR_RPBM)
2599 && !(pStream->Regs.sr & AC97_SR_DCH)
2600 && pStream->uArmedTs > 0
2601 && pStream->cDmaPeriodTicks > 0)
2602 {
2603 uint64_t const offPeriod = PDMDevHlpTimerGet(pDevIns, pStream->hTimer) - pStream->uArmedTs;
2604 uint32_t cSamples;
2605 if (offPeriod < pStream->cDmaPeriodTicks)
2606 cSamples = pStream->Regs.picb * offPeriod / pStream->cDmaPeriodTicks;
2607 else
2608 cSamples = pStream->Regs.picb;
2609 if (cSamples + 8 < pStream->Regs.picb)
2610 { /* likely */ }
2611 else if (pStream->Regs.picb > 8)
2612 cSamples = pStream->Regs.picb - 8;
2613 else
2614 cSamples = 0;
2615 *pu32 = pStream->Regs.picb - cSamples;
2616 Log3Func(("PICB[%d] -> %#x (PICB=%#x cSamples=%#x offPeriod=%RU64 of %RU64 / %RU64%%)\n",
2617 AC97_PORT2IDX(offPort), *pu32, pStream->Regs.picb, cSamples, offPeriod,
2618 pStream->cDmaPeriodTicks, offPeriod * 100 / pStream->cDmaPeriodTicks));
2619 }
2620 else
2621 {
2622 *pu32 = pStream->Regs.picb;
2623 Log3Func(("PICB[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2624 }
2625#endif
2626 break;
2627 default:
2628 *pu32 = UINT32_MAX;
2629 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort));
2630 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2631 break;
2632 }
2633 break;
2634
2635 case 4:
2636 switch (offPort & AC97_NABM_OFF_MASK)
2637 {
2638 case AC97_NABM_OFF_BDBAR:
2639 /* Buffer Descriptor Base Address Register */
2640 *pu32 = pStream->Regs.bdbar;
2641 Log3Func(("BMADDR[%d] -> %#x\n", AC97_PORT2IDX(offPort), *pu32));
2642 break;
2643 case AC97_NABM_OFF_CIV:
2644 /* 32-bit access: Current Index Value Register +
2645 * Last Valid Index Register +
2646 * Status Register */
2647 *pu32 = pStream->Regs.civ | ((uint32_t)pStream->Regs.lvi << 8) | ((uint32_t)pStream->Regs.sr << 16);
2648 Log3Func(("CIV LVI SR[%d] -> %#x, %#x, %#x\n",
2649 AC97_PORT2IDX(offPort), pStream->Regs.civ, pStream->Regs.lvi, pStream->Regs.sr));
2650 break;
2651 case AC97_NABM_OFF_PICB:
2652 /* 32-bit access: Position in Current Buffer Register +
2653 * Prefetched Index Value Register +
2654 * Control Register */
2655 *pu32 = pStream->Regs.picb | ((uint32_t)pStream->Regs.piv << 16) | ((uint32_t)pStream->Regs.cr << 24);
2656 Log3Func(("PICB PIV CR[%d] -> %#x %#x %#x %#x\n",
2657 AC97_PORT2IDX(offPort), *pu32, pStream->Regs.picb, pStream->Regs.piv, pStream->Regs.cr));
2658 break;
2659
2660 default:
2661 *pu32 = UINT32_MAX;
2662 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
2663 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2664 break;
2665 }
2666 break;
2667
2668 default:
2669 DEVAC97_UNLOCK(pDevIns, pThis);
2670 AssertFailed();
2671 return VERR_IOM_IOPORT_UNUSED;
2672 }
2673 }
2674 else
2675 {
2676 switch (cb)
2677 {
2678 case 1:
2679 switch (offPort)
2680 {
2681 case AC97_CAS:
2682 /* Codec Access Semaphore Register */
2683 Log3Func(("CAS %d\n", pThis->cas));
2684 *pu32 = pThis->cas;
2685 pThis->cas = 1;
2686 break;
2687 default:
2688 *pu32 = UINT32_MAX;
2689 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
2690 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2691 break;
2692 }
2693 break;
2694
2695 case 2:
2696 *pu32 = UINT32_MAX;
2697 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort));
2698 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2699 break;
2700
2701 case 4:
2702 switch (offPort)
2703 {
2704 case AC97_GLOB_CNT:
2705 /* Global Control */
2706 *pu32 = pThis->glob_cnt;
2707 Log3Func(("glob_cnt -> %#x\n", *pu32));
2708 break;
2709 case AC97_GLOB_STA:
2710 /* Global Status */
2711 *pu32 = pThis->glob_sta | AC97_GS_S0CR;
2712 Log3Func(("glob_sta -> %#x\n", *pu32));
2713 break;
2714 default:
2715 *pu32 = UINT32_MAX;
2716 LogRel2(("AC97: Warning: Unimplemented NAMB read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
2717 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmReads);
2718 break;
2719 }
2720 break;
2721
2722 default:
2723 DEVAC97_UNLOCK(pDevIns, pThis);
2724 AssertFailed();
2725 return VERR_IOM_IOPORT_UNUSED;
2726 }
2727 }
2728
2729 DEVAC97_UNLOCK(pDevIns, pThis);
2730 return VINF_SUCCESS;
2731}
2732
2733
2734/**
2735 * @callback_method_impl{FNIOMIOPORTNEWOUT}
2736 */
2737static DECLCALLBACK(VBOXSTRICTRC)
2738ichac97IoPortNabmWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2739{
2740 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
2741#ifdef IN_RING3
2742 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
2743#endif
2744 RT_NOREF(pvUser);
2745
2746 VBOXSTRICTRC rc = VINF_SUCCESS;
2747 if ( AC97_PORT2IDX_UNMASKED(offPort) < AC97_MAX_STREAMS
2748 && offPort != AC97_GLOB_CNT)
2749 {
2750#ifdef IN_RING3
2751 PAC97STREAMR3 pStreamCC = &pThisCC->aStreams[AC97_PORT2IDX(offPort)];
2752#endif
2753 PAC97STREAM pStream = &pThis->aStreams[AC97_PORT2IDX(offPort)];
2754
2755 switch (cb)
2756 {
2757 case 1:
2758 switch (offPort & AC97_NABM_OFF_MASK)
2759 {
2760 /*
2761 * Last Valid Index.
2762 */
2763 case AC97_NABM_OFF_LVI:
2764 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2765
2766 if ( !(pStream->Regs.sr & AC97_SR_DCH)
2767 || !(pStream->Regs.cr & AC97_CR_RPBM))
2768 {
2769 pStream->Regs.lvi = u32 % AC97_MAX_BDLE;
2770 STAM_REL_COUNTER_INC(&pStream->StatWriteLvi);
2771 DEVAC97_UNLOCK(pDevIns, pThis);
2772 Log3Func(("[SD%RU8] LVI <- %#x\n", pStream->u8SD, u32));
2773 }
2774 else
2775 {
2776#ifdef IN_RING3
2777 /* Recover from underflow situation where CIV caught up with LVI
2778 and the DMA processing stopped. We clear the status condition,
2779 update LVI and then try to load the next BDLE. Unfortunately,
2780 we cannot do this from ring-0 as much of the BDLE state is
2781 ring-3 only. */
2782 pStream->Regs.sr &= ~(AC97_SR_DCH | AC97_SR_CELV);
2783 pStream->Regs.lvi = u32 % AC97_MAX_BDLE;
2784 if (ichac97R3StreamFetchNextBdle(pDevIns, pStream, pStreamCC))
2785 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr | AC97_SR_BCIS);
2786
2787 /* We now have to re-arm the DMA timer according to the new BDLE length.
2788 This means leaving the device lock to avoid virtual sync lock order issues. */
2789 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
2790 uint64_t const cTicksToDeadline = pStream->cDmaPeriodTicks;
2791
2792 /** @todo Stop the DMA timer when we get into the AC97_SR_CELV situation to
2793 * avoid potential race here. */
2794 STAM_REL_COUNTER_INC(&pStreamCC->State.StatWriteLviRecover);
2795 DEVAC97_UNLOCK(pDevIns, pThis);
2796
2797 LogFunc(("[SD%RU8] LVI <- %#x; CIV=%#x PIV=%#x SR=%#x cTicksToDeadline=%#RX64 [recovering]\n",
2798 pStream->u8SD, u32, pStream->Regs.civ, pStream->Regs.piv, pStream->Regs.sr, cTicksToDeadline));
2799
2800 int rc2 = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimer, cTicksToDeadline, &pStream->uArmedTs);
2801 AssertRC(rc2);
2802#else
2803 DEVAC97_UNLOCK(pDevIns, pThis);
2804 rc = VINF_IOM_R3_IOPORT_WRITE;
2805#endif
2806 }
2807 break;
2808
2809 /*
2810 * Control Registers.
2811 */
2812 case AC97_NABM_OFF_CR:
2813 {
2814#ifdef IN_RING3
2815 DEVAC97_LOCK(pDevIns, pThis);
2816 STAM_REL_COUNTER_INC(&pStreamCC->State.StatWriteCr);
2817
2818 uint32_t const fCrChanged = pStream->Regs.cr ^ u32;
2819 Log3Func(("[SD%RU8] CR <- %#x (was %#x; changed %#x)\n", pStream->u8SD, u32, pStream->Regs.cr, fCrChanged));
2820
2821 /*
2822 * Busmaster reset.
2823 */
2824 if (u32 & AC97_CR_RR)
2825 {
2826 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatReset, r);
2827 LogFunc(("[SD%RU8] Reset\n", pStream->u8SD));
2828
2829 /* Make sure that Run/Pause Bus Master bit (RPBM) is cleared (0).
2830 3.2.7 in 302349-003 says RPBM be must be clear when resetting
2831 and that behavior is undefined if it's set. */
2832 ASSERT_GUEST_STMT((pStream->Regs.cr & AC97_CR_RPBM) == 0,
2833 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream,
2834 pStreamCC, false /* fEnable */));
2835
2836 ichac97R3StreamReset(pThis, pStream, pStreamCC);
2837
2838 ichac97StreamUpdateSR(pDevIns, pThis, pStream, AC97_SR_DCH); /** @todo Do we need to do that? */
2839
2840 DEVAC97_UNLOCK(pDevIns, pThis);
2841 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatReset, r);
2842 break;
2843 }
2844
2845 /*
2846 * Write the new value to the register and if RPBM didn't change we're done.
2847 */
2848 pStream->Regs.cr = u32 & AC97_CR_VALID_MASK;
2849
2850 if (!(fCrChanged & AC97_CR_RPBM))
2851 DEVAC97_UNLOCK(pDevIns, pThis); /* Probably not so likely, but avoid one extra intentation level. */
2852 /*
2853 * Pause busmaster.
2854 */
2855 else if (!(pStream->Regs.cr & AC97_CR_RPBM))
2856 {
2857 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatStop, p);
2858 LogFunc(("[SD%RU8] Pause busmaster (disable stream) SR=%#x -> %#x\n",
2859 pStream->u8SD, pStream->Regs.sr, pStream->Regs.sr | AC97_SR_DCH));
2860 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, false /* fEnable */);
2861 pStream->Regs.sr |= AC97_SR_DCH;
2862
2863 DEVAC97_UNLOCK(pDevIns, pThis);
2864 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatStop, p);
2865 }
2866 /*
2867 * Run busmaster.
2868 */
2869 else
2870 {
2871 STAM_REL_PROFILE_START_NS(&pStreamCC->State.StatStart, r);
2872 LogFunc(("[SD%RU8] Run busmaster (enable stream) SR=%#x -> %#x\n",
2873 pStream->u8SD, pStream->Regs.sr, pStream->Regs.sr & ~AC97_SR_DCH));
2874 pStream->Regs.sr &= ~AC97_SR_DCH;
2875
2876 if (ichac97R3StreamFetchNextBdle(pDevIns, pStream, pStreamCC))
2877 ichac97StreamUpdateSR(pDevIns, pThis, pStream, pStream->Regs.sr | AC97_SR_BCIS);
2878# ifdef LOG_ENABLED
2879 if (LogIsFlowEnabled())
2880 ichac97R3DbgPrintBdl(pDevIns, pThis, pStream, PDMDevHlpDBGFInfoLogHlp(pDevIns), "ichac97IoPortNabmWrite: ");
2881# endif
2882 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, true /* fEnable */);
2883
2884 /*
2885 * Arm the DMA timer. Must drop the AC'97 device lock first as it would
2886 * create a lock order violation with the virtual sync time lock otherwise.
2887 */
2888 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
2889 uint64_t const cTicksToDeadline = pStream->cDmaPeriodTicks;
2890
2891 DEVAC97_UNLOCK(pDevIns, pThis);
2892
2893 /** @todo for output streams we could probably service this a little bit
2894 * earlier if we push it, just to reduce the lag... For HDA we do a
2895 * DMA run immediately after the stream is enabled. */
2896 int rc2 = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimer, cTicksToDeadline, &pStream->uArmedTs);
2897 AssertRC(rc2);
2898
2899 STAM_REL_PROFILE_STOP_NS(&pStreamCC->State.StatStart, r);
2900 }
2901#else /* !IN_RING3 */
2902 rc = VINF_IOM_R3_IOPORT_WRITE;
2903#endif
2904 break;
2905 }
2906
2907 /*
2908 * Status Registers.
2909 */
2910 case AC97_NABM_OFF_SR:
2911 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2912 ichac97StreamWriteSR(pDevIns, pThis, pStream, u32);
2913 STAM_REL_COUNTER_INC(&pStream->StatWriteSr1);
2914 DEVAC97_UNLOCK(pDevIns, pThis);
2915 break;
2916
2917 default:
2918 /* Linux tries to write CIV. */
2919 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x%s <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n",
2920 offPort, (offPort & AC97_NABM_OFF_MASK) == AC97_NABM_OFF_CIV ? " (CIV)" : "" , u32));
2921 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2922 break;
2923 }
2924 break;
2925
2926 case 2:
2927 switch (offPort & AC97_NABM_OFF_MASK)
2928 {
2929 case AC97_NABM_OFF_SR:
2930 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2931 ichac97StreamWriteSR(pDevIns, pThis, pStream, u32);
2932 STAM_REL_COUNTER_INC(&pStream->StatWriteSr2);
2933 DEVAC97_UNLOCK(pDevIns, pThis);
2934 break;
2935 default:
2936 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2937 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2938 break;
2939 }
2940 break;
2941
2942 case 4:
2943 switch (offPort & AC97_NABM_OFF_MASK)
2944 {
2945 case AC97_NABM_OFF_BDBAR:
2946 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2947 /* Buffer Descriptor list Base Address Register */
2948 pStream->Regs.bdbar = u32 & ~(uint32_t)3;
2949 Log3Func(("[SD%RU8] BDBAR <- %#x (bdbar %#x)\n", AC97_PORT2IDX(offPort), u32, pStream->Regs.bdbar));
2950 STAM_REL_COUNTER_INC(&pStream->StatWriteBdBar);
2951 DEVAC97_UNLOCK(pDevIns, pThis);
2952 break;
2953 default:
2954 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2955 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2956 break;
2957 }
2958 break;
2959
2960 default:
2961 AssertMsgFailed(("offPort=%#x <- %#x LB %u\n", offPort, u32, cb));
2962 break;
2963 }
2964 }
2965 else
2966 {
2967 switch (cb)
2968 {
2969 case 1:
2970 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2971 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2972 break;
2973
2974 case 2:
2975 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
2976 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
2977 break;
2978
2979 case 4:
2980 switch (offPort)
2981 {
2982 case AC97_GLOB_CNT:
2983 /* Global Control */
2984 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2985 if (u32 & AC97_GC_WR)
2986 ichac97WarmReset(pThis);
2987 if (u32 & AC97_GC_CR)
2988 ichac97ColdReset(pThis);
2989 if (!(u32 & (AC97_GC_WR | AC97_GC_CR)))
2990 pThis->glob_cnt = u32 & AC97_GC_VALID_MASK;
2991 Log3Func(("glob_cnt <- %#x (glob_cnt %#x)\n", u32, pThis->glob_cnt));
2992 DEVAC97_UNLOCK(pDevIns, pThis);
2993 break;
2994 case AC97_GLOB_STA:
2995 /* Global Status */
2996 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
2997 pThis->glob_sta &= ~(u32 & AC97_GS_WCLEAR_MASK);
2998 pThis->glob_sta |= (u32 & ~(AC97_GS_WCLEAR_MASK | AC97_GS_RO_MASK)) & AC97_GS_VALID_MASK;
2999 Log3Func(("glob_sta <- %#x (glob_sta %#x)\n", u32, pThis->glob_sta));
3000 DEVAC97_UNLOCK(pDevIns, pThis);
3001 break;
3002 default:
3003 LogRel2(("AC97: Warning: Unimplemented NAMB write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3004 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNabmWrites);
3005 break;
3006 }
3007 break;
3008
3009 default:
3010 AssertMsgFailed(("offPort=%#x <- %#x LB %u\n", offPort, u32, cb));
3011 break;
3012 }
3013 }
3014
3015 return rc;
3016}
3017
3018
3019/*********************************************************************************************************************************
3020* Mixer & NAM I/O handlers *
3021*********************************************************************************************************************************/
3022
3023/**
3024 * Sets a AC'97 mixer control to a specific value.
3025 *
3026 * @param pThis The shared AC'97 state.
3027 * @param uMixerIdx Mixer control to set value for.
3028 * @param uVal Value to set.
3029 */
3030static void ichac97MixerSet(PAC97STATE pThis, uint8_t uMixerIdx, uint16_t uVal)
3031{
3032 AssertMsgReturnVoid(uMixerIdx + 2U <= sizeof(pThis->mixer_data),
3033 ("Index %RU8 out of bounds (%zu)\n", uMixerIdx, sizeof(pThis->mixer_data)));
3034
3035 LogRel2(("AC97: Setting mixer index #%RU8 to %RU16 (%RU8 %RU8)\n", uMixerIdx, uVal, RT_HI_U8(uVal), RT_LO_U8(uVal)));
3036
3037 pThis->mixer_data[uMixerIdx + 0] = RT_LO_U8(uVal);
3038 pThis->mixer_data[uMixerIdx + 1] = RT_HI_U8(uVal);
3039}
3040
3041
3042/**
3043 * Gets a value from a specific AC'97 mixer control.
3044 *
3045 * @returns Retrieved mixer control value.
3046 * @param pThis The shared AC'97 state.
3047 * @param uMixerIdx Mixer control to get value for.
3048 */
3049static uint16_t ichac97MixerGet(PAC97STATE pThis, uint32_t uMixerIdx)
3050{
3051 AssertMsgReturn(uMixerIdx + 2U <= sizeof(pThis->mixer_data),
3052 ("Index %RU8 out of bounds (%zu)\n", uMixerIdx, sizeof(pThis->mixer_data)),
3053 UINT16_MAX);
3054 return RT_MAKE_U16(pThis->mixer_data[uMixerIdx + 0], pThis->mixer_data[uMixerIdx + 1]);
3055}
3056
3057#ifdef IN_RING3
3058
3059/**
3060 * Sets the volume of a specific AC'97 mixer control.
3061 *
3062 * This currently only supports attenuation -- gain support is currently not implemented.
3063 *
3064 * @returns VBox status code.
3065 * @param pThis The shared AC'97 state.
3066 * @param pThisCC The ring-3 AC'97 state.
3067 * @param index AC'97 mixer index to set volume for.
3068 * @param enmMixerCtl Corresponding audio mixer sink.
3069 * @param uVal Volume value to set.
3070 */
3071static int ichac97R3MixerSetVolume(PAC97STATE pThis, PAC97STATER3 pThisCC, int index, PDMAUDIOMIXERCTL enmMixerCtl, uint32_t uVal)
3072{
3073 /*
3074 * From AC'97 SoundMax Codec AD1981A/AD1981B:
3075 * "Because AC '97 defines 6-bit volume registers, to maintain compatibility whenever the
3076 * D5 or D13 bits are set to 1, their respective lower five volume bits are automatically
3077 * set to 1 by the Codec logic. On readback, all lower 5 bits will read ones whenever
3078 * these bits are set to 1."
3079 *
3080 * Linux ALSA depends on this behavior to detect that only 5 bits are used for volume
3081 * control and the optional 6th bit is not used. Note that this logic only applies to the
3082 * master volume controls.
3083 */
3084 if ( index == AC97_Master_Volume_Mute
3085 || index == AC97_Headphone_Volume_Mute
3086 || index == AC97_Master_Volume_Mono_Mute)
3087 {
3088 if (uVal & RT_BIT(5)) /* D5 bit set? */
3089 uVal |= RT_BIT(4) | RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0);
3090 if (uVal & RT_BIT(13)) /* D13 bit set? */
3091 uVal |= RT_BIT(12) | RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8);
3092 }
3093
3094 const bool fCtlMuted = (uVal >> AC97_BARS_VOL_MUTE_SHIFT) & 1;
3095 uint8_t uCtlAttLeft = (uVal >> 8) & AC97_BARS_VOL_MASK;
3096 uint8_t uCtlAttRight = uVal & AC97_BARS_VOL_MASK;
3097
3098 /* For the master and headphone volume, 0 corresponds to 0dB attenuation. For the other
3099 * volume controls, 0 means 12dB gain and 8 means unity gain.
3100 */
3101 if (index != AC97_Master_Volume_Mute && index != AC97_Headphone_Volume_Mute)
3102 {
3103# ifndef VBOX_WITH_AC97_GAIN_SUPPORT
3104 /* NB: Currently there is no gain support, only attenuation. */
3105 uCtlAttLeft = uCtlAttLeft < 8 ? 0 : uCtlAttLeft - 8;
3106 uCtlAttRight = uCtlAttRight < 8 ? 0 : uCtlAttRight - 8;
3107# endif
3108 }
3109 Assert(uCtlAttLeft <= 255 / AC97_DB_FACTOR);
3110 Assert(uCtlAttRight <= 255 / AC97_DB_FACTOR);
3111
3112 LogFunc(("index=0x%x, uVal=%RU32, enmMixerCtl=%RU32\n", index, uVal, enmMixerCtl));
3113 LogFunc(("uCtlAttLeft=%RU8, uCtlAttRight=%RU8 ", uCtlAttLeft, uCtlAttRight));
3114
3115 /*
3116 * For AC'97 volume controls, each additional step means -1.5dB attenuation with
3117 * zero being maximum. In contrast, we're internally using 255 (PDMAUDIO_VOLUME_MAX)
3118 * steps, each -0.375dB, where 0 corresponds to -96dB and 255 corresponds to 0dB.
3119 */
3120 uint8_t lVol = PDMAUDIO_VOLUME_MAX - uCtlAttLeft * AC97_DB_FACTOR;
3121 uint8_t rVol = PDMAUDIO_VOLUME_MAX - uCtlAttRight * AC97_DB_FACTOR;
3122
3123 Log(("-> fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n", fCtlMuted, lVol, rVol));
3124
3125 int rc = VINF_SUCCESS;
3126
3127 if (pThisCC->pMixer) /* Device can be in reset state, so no mixer available. */
3128 {
3129 PDMAUDIOVOLUME Vol;
3130 PDMAudioVolumeInitFromStereo(&Vol, fCtlMuted, lVol, rVol);
3131
3132 PAUDMIXSINK pSink = NULL;
3133 switch (enmMixerCtl)
3134 {
3135 case PDMAUDIOMIXERCTL_VOLUME_MASTER:
3136 rc = AudioMixerSetMasterVolume(pThisCC->pMixer, &Vol);
3137 break;
3138
3139 case PDMAUDIOMIXERCTL_FRONT:
3140 pSink = pThisCC->pSinkOut;
3141 break;
3142
3143 case PDMAUDIOMIXERCTL_MIC_IN:
3144 case PDMAUDIOMIXERCTL_LINE_IN:
3145 /* These are recognized but do nothing. */
3146 break;
3147
3148 default:
3149 AssertFailed();
3150 rc = VERR_NOT_SUPPORTED;
3151 break;
3152 }
3153
3154 if (pSink)
3155 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3156 }
3157
3158 ichac97MixerSet(pThis, index, uVal);
3159
3160 if (RT_FAILURE(rc))
3161 LogFlowFunc(("Failed with %Rrc\n", rc));
3162
3163 return rc;
3164}
3165
3166/**
3167 * Sets the gain of a specific AC'97 recording control.
3168 *
3169 * @note Gain support is currently not implemented in PDM audio.
3170 *
3171 * @returns VBox status code.
3172 * @param pThis The shared AC'97 state.
3173 * @param pThisCC The ring-3 AC'97 state.
3174 * @param index AC'97 mixer index to set volume for.
3175 * @param enmMixerCtl Corresponding audio mixer sink.
3176 * @param uVal Volume value to set.
3177 */
3178static int ichac97R3MixerSetGain(PAC97STATE pThis, PAC97STATER3 pThisCC, int index, PDMAUDIOMIXERCTL enmMixerCtl, uint32_t uVal)
3179{
3180 /*
3181 * For AC'97 recording controls, each additional step means +1.5dB gain with
3182 * zero being 0dB gain and 15 being +22.5dB gain.
3183 */
3184 bool const fCtlMuted = (uVal >> AC97_BARS_VOL_MUTE_SHIFT) & 1;
3185 uint8_t uCtlGainLeft = (uVal >> 8) & AC97_BARS_GAIN_MASK;
3186 uint8_t uCtlGainRight = uVal & AC97_BARS_GAIN_MASK;
3187
3188 Assert(uCtlGainLeft <= 255 / AC97_DB_FACTOR);
3189 Assert(uCtlGainRight <= 255 / AC97_DB_FACTOR);
3190
3191 LogFunc(("index=0x%x, uVal=%RU32, enmMixerCtl=%RU32\n", index, uVal, enmMixerCtl));
3192 LogFunc(("uCtlGainLeft=%RU8, uCtlGainRight=%RU8 ", uCtlGainLeft, uCtlGainRight));
3193
3194 uint8_t lVol = PDMAUDIO_VOLUME_MAX + uCtlGainLeft * AC97_DB_FACTOR;
3195 uint8_t rVol = PDMAUDIO_VOLUME_MAX + uCtlGainRight * AC97_DB_FACTOR;
3196
3197 /* We do not currently support gain. Since AC'97 does not support attenuation
3198 * for the recording input, the best we can do is set the maximum volume.
3199 */
3200# ifndef VBOX_WITH_AC97_GAIN_SUPPORT
3201 /* NB: Currently there is no gain support, only attenuation. Since AC'97 does not
3202 * support attenuation for the recording inputs, the best we can do is set the
3203 * maximum volume.
3204 */
3205 lVol = rVol = PDMAUDIO_VOLUME_MAX;
3206# endif
3207
3208 Log(("-> fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n", fCtlMuted, lVol, rVol));
3209
3210 int rc = VINF_SUCCESS;
3211
3212 if (pThisCC->pMixer) /* Device can be in reset state, so no mixer available. */
3213 {
3214 PDMAUDIOVOLUME Vol;
3215 PDMAudioVolumeInitFromStereo(&Vol, fCtlMuted, lVol, rVol);
3216
3217 PAUDMIXSINK pSink = NULL;
3218 switch (enmMixerCtl)
3219 {
3220 case PDMAUDIOMIXERCTL_MIC_IN:
3221 pSink = pThisCC->pSinkMicIn;
3222 break;
3223
3224 case PDMAUDIOMIXERCTL_LINE_IN:
3225 pSink = pThisCC->pSinkLineIn;
3226 break;
3227
3228 default:
3229 AssertFailed();
3230 rc = VERR_NOT_SUPPORTED;
3231 break;
3232 }
3233
3234 if (pSink)
3235 {
3236 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3237 /* There is only one AC'97 recording gain control. If line in
3238 * is changed, also update the microphone. If the optional dedicated
3239 * microphone is changed, only change that.
3240 * NB: The codecs we support do not have the dedicated microphone control.
3241 */
3242 if (pSink == pThisCC->pSinkLineIn && pThisCC->pSinkMicIn)
3243 rc = AudioMixerSinkSetVolume(pSink, &Vol);
3244 }
3245 }
3246
3247 ichac97MixerSet(pThis, index, uVal);
3248
3249 if (RT_FAILURE(rc))
3250 LogFlowFunc(("Failed with %Rrc\n", rc));
3251
3252 return rc;
3253}
3254
3255
3256/**
3257 * Converts an AC'97 recording source index to a PDM audio recording source.
3258 *
3259 * @returns PDM audio recording source.
3260 * @param uIdx AC'97 index to convert.
3261 */
3262static PDMAUDIOPATH ichac97R3IdxToRecSource(uint8_t uIdx)
3263{
3264 switch (uIdx)
3265 {
3266 case AC97_REC_MIC: return PDMAUDIOPATH_IN_MIC;
3267 case AC97_REC_CD: return PDMAUDIOPATH_IN_CD;
3268 case AC97_REC_VIDEO: return PDMAUDIOPATH_IN_VIDEO;
3269 case AC97_REC_AUX: return PDMAUDIOPATH_IN_AUX;
3270 case AC97_REC_LINE_IN: return PDMAUDIOPATH_IN_LINE;
3271 case AC97_REC_PHONE: return PDMAUDIOPATH_IN_PHONE;
3272 default:
3273 break;
3274 }
3275
3276 LogFlowFunc(("Unknown record source %d, using MIC\n", uIdx));
3277 return PDMAUDIOPATH_IN_MIC;
3278}
3279
3280
3281/**
3282 * Converts a PDM audio recording source to an AC'97 recording source index.
3283 *
3284 * @returns AC'97 recording source index.
3285 * @param enmRecSrc PDM audio recording source to convert.
3286 */
3287static uint8_t ichac97R3RecSourceToIdx(PDMAUDIOPATH enmRecSrc)
3288{
3289 switch (enmRecSrc)
3290 {
3291 case PDMAUDIOPATH_IN_MIC: return AC97_REC_MIC;
3292 case PDMAUDIOPATH_IN_CD: return AC97_REC_CD;
3293 case PDMAUDIOPATH_IN_VIDEO: return AC97_REC_VIDEO;
3294 case PDMAUDIOPATH_IN_AUX: return AC97_REC_AUX;
3295 case PDMAUDIOPATH_IN_LINE: return AC97_REC_LINE_IN;
3296 case PDMAUDIOPATH_IN_PHONE: return AC97_REC_PHONE;
3297 default:
3298 AssertMsgFailedBreak(("%d\n", enmRecSrc));
3299 }
3300
3301 LogFlowFunc(("Unknown audio recording source %d using MIC\n", enmRecSrc));
3302 return AC97_REC_MIC;
3303}
3304
3305
3306/**
3307 * Performs an AC'97 mixer record select to switch to a different recording
3308 * source.
3309 *
3310 * @param pThis The shared AC'97 state.
3311 * @param val AC'97 recording source index to set.
3312 */
3313static void ichac97R3MixerRecordSelect(PAC97STATE pThis, uint32_t val)
3314{
3315 uint8_t rs = val & AC97_REC_MASK;
3316 uint8_t ls = (val >> 8) & AC97_REC_MASK;
3317
3318 PDMAUDIOPATH const ars = ichac97R3IdxToRecSource(rs);
3319 PDMAUDIOPATH const als = ichac97R3IdxToRecSource(ls);
3320
3321 rs = ichac97R3RecSourceToIdx(ars);
3322 ls = ichac97R3RecSourceToIdx(als);
3323
3324 LogRel(("AC97: Record select to left=%s, right=%s\n", PDMAudioPathGetName(ars), PDMAudioPathGetName(als)));
3325
3326 ichac97MixerSet(pThis, AC97_Record_Select, rs | (ls << 8));
3327}
3328
3329/**
3330 * Resets the AC'97 mixer.
3331 *
3332 * @returns VBox status code.
3333 * @param pThis The shared AC'97 state.
3334 * @param pThisCC The ring-3 AC'97 state.
3335 */
3336static int ichac97R3MixerReset(PAC97STATE pThis, PAC97STATER3 pThisCC)
3337{
3338 LogFlowFuncEnter();
3339
3340 RT_ZERO(pThis->mixer_data);
3341
3342 /* Note: Make sure to reset all registers first before bailing out on error. */
3343
3344 ichac97MixerSet(pThis, AC97_Reset , 0x0000); /* 6940 */
3345 ichac97MixerSet(pThis, AC97_Master_Volume_Mono_Mute , 0x8000);
3346 ichac97MixerSet(pThis, AC97_PC_BEEP_Volume_Mute , 0x0000);
3347
3348 ichac97MixerSet(pThis, AC97_Phone_Volume_Mute , 0x8008);
3349 ichac97MixerSet(pThis, AC97_Mic_Volume_Mute , 0x8008);
3350 ichac97MixerSet(pThis, AC97_CD_Volume_Mute , 0x8808);
3351 ichac97MixerSet(pThis, AC97_Aux_Volume_Mute , 0x8808);
3352 ichac97MixerSet(pThis, AC97_Record_Gain_Mic_Mute , 0x8000);
3353 ichac97MixerSet(pThis, AC97_General_Purpose , 0x0000);
3354 ichac97MixerSet(pThis, AC97_3D_Control , 0x0000);
3355 ichac97MixerSet(pThis, AC97_Powerdown_Ctrl_Stat , 0x000f);
3356
3357 /* Configure Extended Audio ID (EAID) + Control & Status (EACS) registers. */
3358 const uint16_t fEAID = AC97_EAID_REV1 | AC97_EACS_VRA | AC97_EACS_VRM; /* Our hardware is AC'97 rev2.3 compliant. */
3359 const uint16_t fEACS = AC97_EACS_VRA | AC97_EACS_VRM; /* Variable Rate PCM Audio (VRA) + Mic-In (VRM) capable. */
3360
3361 LogRel(("AC97: Mixer reset (EAID=0x%x, EACS=0x%x)\n", fEAID, fEACS));
3362
3363 ichac97MixerSet(pThis, AC97_Extended_Audio_ID, fEAID);
3364 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, fEACS);
3365 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3366 ichac97MixerSet(pThis, AC97_PCM_Surround_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3367 ichac97MixerSet(pThis, AC97_PCM_LFE_DAC_Rate , 0xbb80 /* 48000 Hz by default */);
3368 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate , 0xbb80 /* 48000 Hz by default */);
3369 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate , 0xbb80 /* 48000 Hz by default */);
3370
3371 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3372 {
3373 /* Analog Devices 1980 (AD1980) */
3374 ichac97MixerSet(pThis, AC97_Reset , 0x0010); /* Headphones. */
3375 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
3376 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5370);
3377 ichac97MixerSet(pThis, AC97_Headphone_Volume_Mute , 0x8000);
3378 }
3379 else if (pThis->enmCodecModel == AC97CODEC_AD1981B)
3380 {
3381 /* Analog Devices 1981B (AD1981B) */
3382 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
3383 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5374);
3384 }
3385 else
3386 {
3387 /* Sigmatel 9700 (STAC9700) */
3388 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x8384);
3389 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x7600); /* 7608 */
3390 }
3391 ichac97R3MixerRecordSelect(pThis, 0);
3392
3393 /* The default value is 8000h, which corresponds to 0 dB attenuation with mute on. */
3394 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER, 0x8000);
3395
3396 /* The default value for stereo registers is 8808h, which corresponds to 0 dB gain with mute on.*/
3397 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT, 0x8808);
3398 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8808);
3399 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Mic_Volume_Mute, PDMAUDIOMIXERCTL_MIC_IN, 0x8008);
3400
3401 /* The default for record controls is 0 dB gain with mute on. */
3402 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8000);
3403 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mic_Mute, PDMAUDIOMIXERCTL_MIC_IN, 0x8000);
3404
3405 return VINF_SUCCESS;
3406}
3407
3408#endif /* IN_RING3 */
3409
3410/**
3411 * @callback_method_impl{FNIOMIOPORTNEWIN}
3412 */
3413static DECLCALLBACK(VBOXSTRICTRC)
3414ichac97IoPortNamRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3415{
3416 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3417 RT_NOREF(pvUser);
3418 Assert(offPort < 256);
3419
3420 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_READ);
3421
3422 VBOXSTRICTRC rc = VINF_SUCCESS;
3423 switch (cb)
3424 {
3425 case 1:
3426 LogRel2(("AC97: Warning: Unimplemented NAM read offPort=%#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort));
3427 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamReads);
3428 pThis->cas = 0;
3429 *pu32 = UINT32_MAX;
3430 break;
3431
3432 case 2:
3433 pThis->cas = 0;
3434 *pu32 = ichac97MixerGet(pThis, offPort);
3435 break;
3436
3437 case 4:
3438 LogRel2(("AC97: Warning: Unimplemented NAM read offPort=%#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort));
3439 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamReads);
3440 pThis->cas = 0;
3441 *pu32 = UINT32_MAX;
3442 break;
3443
3444 default:
3445 AssertFailed();
3446 rc = VERR_IOM_IOPORT_UNUSED;
3447 break;
3448 }
3449
3450 DEVAC97_UNLOCK(pDevIns, pThis);
3451 return rc;
3452}
3453
3454/**
3455 * @callback_method_impl{FNIOMIOPORTNEWOUT}
3456 */
3457static DECLCALLBACK(VBOXSTRICTRC)
3458ichac97IoPortNamWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3459{
3460 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3461#ifdef IN_RING3
3462 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3463#endif
3464 RT_NOREF(pvUser);
3465
3466 DEVAC97_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_IOPORT_WRITE);
3467
3468 VBOXSTRICTRC rc = VINF_SUCCESS;
3469 switch (cb)
3470 {
3471 case 1:
3472 LogRel2(("AC97: Warning: Unimplemented NAM write offPort=%#x <- %#x LB 1 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3473 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3474 pThis->cas = 0;
3475 break;
3476
3477 case 2:
3478 {
3479 pThis->cas = 0;
3480 switch (offPort)
3481 {
3482 case AC97_Reset:
3483#ifdef IN_RING3
3484 ichac97R3Reset(pDevIns);
3485#else
3486 rc = VINF_IOM_R3_IOPORT_WRITE;
3487#endif
3488 break;
3489 case AC97_Powerdown_Ctrl_Stat:
3490 u32 &= ~0xf;
3491 u32 |= ichac97MixerGet(pThis, offPort) & 0xf;
3492 ichac97MixerSet(pThis, offPort, u32);
3493 break;
3494 case AC97_Master_Volume_Mute:
3495 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3496 {
3497 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_LOSEL)
3498 break; /* Register controls surround (rear), do nothing. */
3499 }
3500#ifdef IN_RING3
3501 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_VOLUME_MASTER, u32);
3502#else
3503 rc = VINF_IOM_R3_IOPORT_WRITE;
3504#endif
3505 break;
3506 case AC97_Headphone_Volume_Mute:
3507 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3508 {
3509 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_HPSEL)
3510 {
3511 /* Register controls PCM (front) outputs. */
3512#ifdef IN_RING3
3513 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_VOLUME_MASTER, u32);
3514#else
3515 rc = VINF_IOM_R3_IOPORT_WRITE;
3516#endif
3517 }
3518 }
3519 break;
3520 case AC97_PCM_Out_Volume_Mute:
3521#ifdef IN_RING3
3522 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_FRONT, u32);
3523#else
3524 rc = VINF_IOM_R3_IOPORT_WRITE;
3525#endif
3526 break;
3527 case AC97_Line_In_Volume_Mute:
3528#ifdef IN_RING3
3529 ichac97R3MixerSetVolume(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_LINE_IN, u32);
3530#else
3531 rc = VINF_IOM_R3_IOPORT_WRITE;
3532#endif
3533 break;
3534 case AC97_Record_Select:
3535#ifdef IN_RING3
3536 ichac97R3MixerRecordSelect(pThis, u32);
3537#else
3538 rc = VINF_IOM_R3_IOPORT_WRITE;
3539#endif
3540 break;
3541 case AC97_Record_Gain_Mute:
3542#ifdef IN_RING3
3543 /* Newer Ubuntu guests rely on that when controlling gain and muting
3544 * the recording (capturing) levels. */
3545 ichac97R3MixerSetGain(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_LINE_IN, u32);
3546#else
3547 rc = VINF_IOM_R3_IOPORT_WRITE;
3548#endif
3549 break;
3550 case AC97_Record_Gain_Mic_Mute:
3551#ifdef IN_RING3
3552 /* Ditto; see note above. */
3553 ichac97R3MixerSetGain(pThis, pThisCC, offPort, PDMAUDIOMIXERCTL_MIC_IN, u32);
3554#else
3555 rc = VINF_IOM_R3_IOPORT_WRITE;
3556#endif
3557 break;
3558 case AC97_Vendor_ID1:
3559 case AC97_Vendor_ID2:
3560 LogFunc(("Attempt to write vendor ID to %#x\n", u32));
3561 break;
3562 case AC97_Extended_Audio_ID:
3563 LogFunc(("Attempt to write extended audio ID to %#x\n", u32));
3564 break;
3565 case AC97_Extended_Audio_Ctrl_Stat:
3566#ifdef IN_RING3
3567 /*
3568 * Handle VRA bits.
3569 */
3570 if (!(u32 & AC97_EACS_VRA)) /* Check if VRA bit is not set. */
3571 {
3572 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate, 0xbb80); /* Set default (48000 Hz). */
3573 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3574 * actually used? */
3575 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX],
3576 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX], true /* fForce */);
3577
3578 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate, 0xbb80); /* Set default (48000 Hz). */
3579 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3580 * actually used? */
3581 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX],
3582 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX], true /* fForce */);
3583 }
3584 else
3585 LogRel2(("AC97: Variable rate audio (VRA) is not supported\n"));
3586
3587 /*
3588 * Handle VRM bits.
3589 */
3590 if (!(u32 & AC97_EACS_VRM)) /* Check if VRM bit is not set. */
3591 {
3592 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate, 0xbb80); /* Set default (48000 Hz). */
3593 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3594 * actually used? */
3595 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX],
3596 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX], true /* fForce */);
3597 }
3598 else
3599 LogRel2(("AC97: Variable rate microphone audio (VRM) is not supported\n"));
3600
3601 LogRel2(("AC97: Setting extended audio control to %#x\n", u32));
3602 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, u32);
3603#else /* !IN_RING3 */
3604 rc = VINF_IOM_R3_IOPORT_WRITE;
3605#endif
3606 break;
3607 case AC97_PCM_Front_DAC_Rate: /* Output slots 3, 4, 6. */
3608#ifdef IN_RING3
3609 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRA)
3610 {
3611 LogRel2(("AC97: Setting front DAC rate to 0x%x\n", u32));
3612 ichac97MixerSet(pThis, offPort, u32);
3613 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3614 * actually used? */
3615 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX],
3616 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX], true /* fForce */);
3617 }
3618 else
3619 LogRel2(("AC97: Setting front DAC rate (0x%x) when VRA is not set is forbidden, ignoring\n", u32));
3620#else
3621 rc = VINF_IOM_R3_IOPORT_WRITE;
3622#endif
3623 break;
3624 case AC97_MIC_ADC_Rate: /* Input slot 6. */
3625#ifdef IN_RING3
3626 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRM)
3627 {
3628 LogRel2(("AC97: Setting microphone ADC rate to 0x%x\n", u32));
3629 ichac97MixerSet(pThis, offPort, u32);
3630 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3631 * actually used? */
3632 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX],
3633 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX], true /* fForce */);
3634 }
3635 else
3636 LogRel2(("AC97: Setting microphone ADC rate (0x%x) when VRM is not set is forbidden, ignoring\n", u32));
3637#else
3638 rc = VINF_IOM_R3_IOPORT_WRITE;
3639#endif
3640 break;
3641 case AC97_PCM_LR_ADC_Rate: /* Input slots 3, 4. */
3642#ifdef IN_RING3
3643 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & AC97_EACS_VRA)
3644 {
3645 LogRel2(("AC97: Setting line-in ADC rate to 0x%x\n", u32));
3646 ichac97MixerSet(pThis, offPort, u32);
3647 /** @todo r=bird: Why reopen it now? Can't we put that off till it's
3648 * actually used? */
3649 ichac97R3StreamReSetUp(pDevIns, pThis, pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX],
3650 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX], true /* fForce */);
3651 }
3652 else
3653 LogRel2(("AC97: Setting line-in ADC rate (0x%x) when VRA is not set is forbidden, ignoring\n", u32));
3654#else
3655 rc = VINF_IOM_R3_IOPORT_WRITE;
3656#endif
3657 break;
3658 default:
3659 /* Most of these are to register we don't care about like AC97_CD_Volume_Mute
3660 and AC97_Master_Volume_Mono_Mute or things we don't need to handle specially.
3661 Thus this is not a 'warning' but an 'info log message. */
3662 LogRel2(("AC97: Info: Unimplemented NAM write offPort=%#x <- %#x LB 2 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3663 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3664 ichac97MixerSet(pThis, offPort, u32);
3665 break;
3666 }
3667 break;
3668 }
3669
3670 case 4:
3671 LogRel2(("AC97: Warning: Unimplemented NAM write offPort=%#x <- %#x LB 4 (line " RT_XSTR(__LINE__) ")\n", offPort, u32));
3672 STAM_REL_COUNTER_INC(&pThis->StatUnimplementedNamWrites);
3673 pThis->cas = 0;
3674 break;
3675
3676 default:
3677 AssertMsgFailed(("Unhandled NAM write offPort=%#x, cb=%u u32=%#x\n", offPort, cb, u32));
3678 break;
3679 }
3680
3681 DEVAC97_UNLOCK(pDevIns, pThis);
3682 return rc;
3683}
3684
3685#ifdef IN_RING3
3686
3687
3688/*********************************************************************************************************************************
3689* State Saving & Loading *
3690*********************************************************************************************************************************/
3691
3692/**
3693 * Saves (serializes) an AC'97 stream using SSM.
3694 *
3695 * @param pDevIns Device instance.
3696 * @param pSSM Saved state manager (SSM) handle to use.
3697 * @param pStream AC'97 stream to save.
3698 */
3699static void ichac97R3SaveStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStream)
3700{
3701 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3702
3703 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bdbar);
3704 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.civ);
3705 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.lvi);
3706 pHlp->pfnSSMPutU16(pSSM, pStream->Regs.sr);
3707 pHlp->pfnSSMPutU16(pSSM, pStream->Regs.picb);
3708 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.piv);
3709 pHlp->pfnSSMPutU8( pSSM, pStream->Regs.cr);
3710 pHlp->pfnSSMPutS32(pSSM, pStream->Regs.bd_valid);
3711 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bd.addr);
3712 pHlp->pfnSSMPutU32(pSSM, pStream->Regs.bd.ctl_len);
3713}
3714
3715
3716/**
3717 * @callback_method_impl{FNSSMDEVSAVEEXEC}
3718 */
3719static DECLCALLBACK(int) ichac97R3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
3720{
3721 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3722 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3723 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3724 LogFlowFuncEnter();
3725
3726 pHlp->pfnSSMPutU32(pSSM, pThis->glob_cnt);
3727 pHlp->pfnSSMPutU32(pSSM, pThis->glob_sta);
3728 pHlp->pfnSSMPutU32(pSSM, pThis->cas);
3729
3730 /*
3731 * The order that the streams are saved here is fixed, so don't change.
3732 */
3733 /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
3734 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3735 ichac97R3SaveStream(pDevIns, pSSM, &pThis->aStreams[i]);
3736
3737 pHlp->pfnSSMPutMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
3738
3739 /* The stream order is against fixed and set in stone. */
3740 uint8_t afActiveStrms[AC97SOUNDSOURCE_MAX];
3741 afActiveStrms[AC97SOUNDSOURCE_PI_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PI_INDEX]);
3742 afActiveStrms[AC97SOUNDSOURCE_PO_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_PO_INDEX]);
3743 afActiveStrms[AC97SOUNDSOURCE_MC_INDEX] = ichac97R3StreamIsEnabled(pThisCC, &pThis->aStreams[AC97SOUNDSOURCE_MC_INDEX]);
3744 AssertCompile(RT_ELEMENTS(afActiveStrms) == 3);
3745 pHlp->pfnSSMPutMem(pSSM, afActiveStrms, sizeof(afActiveStrms));
3746
3747 LogFlowFuncLeaveRC(VINF_SUCCESS);
3748 return VINF_SUCCESS;
3749}
3750
3751
3752/**
3753 * Loads an AC'97 stream from SSM.
3754 *
3755 * @returns VBox status code.
3756 * @param pDevIns The device instance.
3757 * @param pSSM Saved state manager (SSM) handle to use.
3758 * @param pStream AC'97 stream to load.
3759 */
3760static int ichac97R3LoadStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStream)
3761{
3762 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3763
3764 pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bdbar);
3765 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.civ);
3766 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.lvi);
3767 pHlp->pfnSSMGetU16(pSSM, &pStream->Regs.sr);
3768 pHlp->pfnSSMGetU16(pSSM, &pStream->Regs.picb);
3769 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.piv);
3770 pHlp->pfnSSMGetU8( pSSM, &pStream->Regs.cr);
3771 pHlp->pfnSSMGetS32(pSSM, &pStream->Regs.bd_valid);
3772 pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bd.addr);
3773 return pHlp->pfnSSMGetU32(pSSM, &pStream->Regs.bd.ctl_len);
3774}
3775
3776
3777/**
3778 * @callback_method_impl{FNSSMDEVLOADEXEC}
3779 */
3780static DECLCALLBACK(int) ichac97R3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
3781{
3782 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3783 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
3784 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
3785
3786 LogRel2(("ichac97LoadExec: uVersion=%RU32, uPass=0x%x\n", uVersion, uPass));
3787
3788 AssertMsgReturn (uVersion == AC97_SAVED_STATE_VERSION, ("%RU32\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
3789 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
3790
3791 pHlp->pfnSSMGetU32(pSSM, &pThis->glob_cnt);
3792 pHlp->pfnSSMGetU32(pSSM, &pThis->glob_sta);
3793 pHlp->pfnSSMGetU32(pSSM, &pThis->cas);
3794
3795 /*
3796 * The order the streams are loaded here is critical (defined by
3797 * AC97SOUNDSOURCE_XX_INDEX), so don't touch!
3798 */
3799 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3800 {
3801 int rc = ichac97R3LoadStream(pDevIns, pSSM, &pThis->aStreams[i]);
3802 AssertRCReturn(rc, rc);
3803 }
3804
3805 pHlp->pfnSSMGetMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
3806
3807 ichac97R3MixerRecordSelect(pThis, ichac97MixerGet(pThis, AC97_Record_Select));
3808 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER,
3809 ichac97MixerGet(pThis, AC97_Master_Volume_Mute));
3810 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT,
3811 ichac97MixerGet(pThis, AC97_PCM_Out_Volume_Mute));
3812 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN,
3813 ichac97MixerGet(pThis, AC97_Line_In_Volume_Mute));
3814 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Mic_Volume_Mute, PDMAUDIOMIXERCTL_MIC_IN,
3815 ichac97MixerGet(pThis, AC97_Mic_Volume_Mute));
3816 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mic_Mute, PDMAUDIOMIXERCTL_MIC_IN,
3817 ichac97MixerGet(pThis, AC97_Record_Gain_Mic_Mute));
3818 ichac97R3MixerSetGain(pThis, pThisCC, AC97_Record_Gain_Mute, PDMAUDIOMIXERCTL_LINE_IN,
3819 ichac97MixerGet(pThis, AC97_Record_Gain_Mute));
3820 if (pThis->enmCodecModel == AC97CODEC_AD1980)
3821 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AC97_AD_MISC_HPSEL)
3822 ichac97R3MixerSetVolume(pThis, pThisCC, AC97_Headphone_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME_MASTER,
3823 ichac97MixerGet(pThis, AC97_Headphone_Volume_Mute));
3824
3825 /*
3826 * Again the stream order is set is stone.
3827 */
3828 uint8_t afActiveStrms[AC97SOUNDSOURCE_MAX];
3829 int rc = pHlp->pfnSSMGetMem(pSSM, afActiveStrms, sizeof(afActiveStrms));
3830 AssertRCReturn(rc, rc);
3831
3832 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
3833 {
3834 const bool fEnable = RT_BOOL(afActiveStrms[i]);
3835 const PAC97STREAM pStream = &pThis->aStreams[i];
3836 const PAC97STREAMR3 pStreamCC = &pThisCC->aStreams[i];
3837
3838 rc = ichac97R3StreamEnable(pDevIns, pThis, pThisCC, pStream, pStreamCC, fEnable);
3839 AssertRC(rc);
3840 if ( fEnable
3841 && RT_SUCCESS(rc))
3842 {
3843 /*
3844 * We need to make sure to update the stream's next transfer (if any) when
3845 * restoring from a saved state.
3846 *
3847 * Otherwise pStream->cDmaPeriodTicks always will be 0 and thus streams won't
3848 * resume when running while the saved state has been taken.
3849 *
3850 * Also see oem2ticketref:52.
3851 */
3852 ichac97R3StreamTransferUpdate(pDevIns, pStream, pStreamCC);
3853
3854 /* Re-arm the timer for this stream. */
3855 /** @todo r=aeichner This causes a VM hang upon saved state resume when NetBSD is used as a guest
3856 * Stopping the timer if cDmaPeriodTicks is 0 is a workaround but needs further investigation,
3857 * see @bugref{9759} for more information. */
3858 if (pStream->cDmaPeriodTicks)
3859 ichac97R3TimerSet(pDevIns, pStream, pStream->cDmaPeriodTicks);
3860 else
3861 PDMDevHlpTimerStop(pDevIns, pStream->hTimer);
3862 }
3863
3864 /* Keep going. */
3865 }
3866
3867 pThis->bup_flag = 0;
3868 pThis->last_samp = 0;
3869
3870 return VINF_SUCCESS;
3871}
3872
3873
3874/*********************************************************************************************************************************
3875* Debug Info Items *
3876*********************************************************************************************************************************/
3877
3878/** Used by ichac97R3DbgInfoStream and ichac97R3DbgInfoBDL. */
3879static int ichac97R3DbgLookupStrmIdx(PCDBGFINFOHLP pHlp, const char *pszArgs)
3880{
3881 if (pszArgs && *pszArgs)
3882 {
3883 int32_t idxStream;
3884 int rc = RTStrToInt32Full(pszArgs, 0, &idxStream);
3885 if (RT_SUCCESS(rc) && idxStream >= -1 && idxStream < AC97_MAX_STREAMS)
3886 return idxStream;
3887 pHlp->pfnPrintf(pHlp, "Argument '%s' is not a valid stream number!\n", pszArgs);
3888 }
3889 return -1;
3890}
3891
3892
3893/**
3894 * Generic buffer descriptor list dumper.
3895 */
3896static void ichac97R3DbgPrintBdl(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STREAM pStream,
3897 PCDBGFINFOHLP pHlp, const char *pszPrefix)
3898{
3899 uint8_t const bLvi = pStream->Regs.lvi;
3900 uint8_t const bCiv = pStream->Regs.civ;
3901 pHlp->pfnPrintf(pHlp, "%sBDL for stream #%u: @ %#RX32 LB 0x100; CIV=%#04x LVI=%#04x:\n",
3902 pszPrefix, pStream->u8SD, pStream->Regs.bdbar, bCiv, bLvi);
3903 if (pStream->Regs.bdbar != 0)
3904 {
3905 /* Read all in one go. */
3906 AC97BDLE aBdl[AC97_MAX_BDLE];
3907 RT_ZERO(aBdl);
3908 PDMDevHlpPCIPhysRead(pDevIns, pStream->Regs.bdbar, aBdl, sizeof(aBdl));
3909
3910 /* Get the audio props for the stream so we can translate the sizes correctly. */
3911 PDMAUDIOPCMPROPS Props;
3912 ichach97R3CalcStreamProps(pThis, pStream->u8SD, &Props);
3913
3914 /* Dump them. */
3915 uint64_t cbTotal = 0;
3916 uint64_t cbValid = 0;
3917 for (unsigned i = 0; i < RT_ELEMENTS(aBdl); i++)
3918 {
3919 aBdl[i].addr = RT_LE2H_U32(aBdl[i].addr);
3920 aBdl[i].ctl_len = RT_LE2H_U32(aBdl[i].ctl_len);
3921
3922 bool const fValid = bCiv <= bLvi
3923 ? i >= bCiv && i <= bLvi
3924 : i >= bCiv || i <= bLvi;
3925
3926 uint32_t const cb = (aBdl[i].ctl_len & AC97_BD_LEN_MASK) * PDMAudioPropsSampleSize(&Props); /** @todo or frame size? OSDev says frame... */
3927 cbTotal += cb;
3928 if (fValid)
3929 cbValid += cb;
3930
3931 char szFlags[64];
3932 szFlags[0] = '\0';
3933 if (aBdl[i].ctl_len & ~(AC97_BD_LEN_MASK | AC97_BD_IOC | AC97_BD_BUP))
3934 RTStrPrintf(szFlags, sizeof(szFlags), " !!fFlags=%#x!!\n", aBdl[i].ctl_len & ~AC97_BD_LEN_MASK);
3935
3936 pHlp->pfnPrintf(pHlp, "%s %cBDLE%02u: %#010RX32 L %#06x / LB %#RX32 / %RU64ms%s%s%s%s\n",
3937 pszPrefix, fValid ? ' ' : '?', i, aBdl[i].addr,
3938 aBdl[i].ctl_len & AC97_BD_LEN_MASK, cb, PDMAudioPropsBytesToMilli(&Props, cb),
3939 aBdl[i].ctl_len & AC97_BD_IOC ? " ioc" : "",
3940 aBdl[i].ctl_len & AC97_BD_BUP ? " bup" : "",
3941 szFlags, !(aBdl[i].addr & 3) ? "" : " !!Addr!!");
3942 }
3943
3944 pHlp->pfnPrintf(pHlp, "%sTotal: %#RX64 bytes (%RU64), %RU64 ms; Valid: %#RX64 bytes (%RU64), %RU64 ms\n", pszPrefix,
3945 cbTotal, cbTotal, PDMAudioPropsBytesToMilli(&Props, cbTotal),
3946 cbValid, cbValid, PDMAudioPropsBytesToMilli(&Props, cbValid) );
3947 }
3948}
3949
3950
3951/**
3952 * @callback_method_impl{FNDBGFHANDLERDEV, ac97bdl}
3953 */
3954static DECLCALLBACK(void) ichac97R3DbgInfoBDL(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3955{
3956 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
3957 int idxStream = ichac97R3DbgLookupStrmIdx(pHlp, pszArgs);
3958 if (idxStream != -1)
3959 ichac97R3DbgPrintBdl(pDevIns, pThis, &pThis->aStreams[idxStream], pHlp, "");
3960 else
3961 for (idxStream = 0; idxStream < AC97_MAX_STREAMS; ++idxStream)
3962 ichac97R3DbgPrintBdl(pDevIns, pThis, &pThis->aStreams[idxStream], pHlp, "");
3963}
3964
3965
3966/** Worker for ichac97R3DbgInfoStream. */
3967static void ichac97R3DbgPrintStream(PCDBGFINFOHLP pHlp, PAC97STREAM pStream, PAC97STREAMR3 pStreamR3)
3968{
3969 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX];
3970 pHlp->pfnPrintf(pHlp, "Stream #%d: %s\n", pStream->u8SD,
3971 PDMAudioStrmCfgToString(&pStreamR3->State.Cfg, szTmp, sizeof(szTmp)));
3972 pHlp->pfnPrintf(pHlp, " BDBAR %#010RX32\n", pStream->Regs.bdbar);
3973 pHlp->pfnPrintf(pHlp, " CIV %#04RX8\n", pStream->Regs.civ);
3974 pHlp->pfnPrintf(pHlp, " LVI %#04RX8\n", pStream->Regs.lvi);
3975 pHlp->pfnPrintf(pHlp, " SR %#06RX16\n", pStream->Regs.sr);
3976 pHlp->pfnPrintf(pHlp, " PICB %#06RX16\n", pStream->Regs.picb);
3977 pHlp->pfnPrintf(pHlp, " PIV %#04RX8\n", pStream->Regs.piv);
3978 pHlp->pfnPrintf(pHlp, " CR %#04RX8\n", pStream->Regs.cr);
3979 if (pStream->Regs.bd_valid)
3980 {
3981 pHlp->pfnPrintf(pHlp, " BD.ADDR %#010RX32\n", pStream->Regs.bd.addr);
3982 pHlp->pfnPrintf(pHlp, " BD.LEN %#04RX16\n", (uint16_t)pStream->Regs.bd.ctl_len);
3983 pHlp->pfnPrintf(pHlp, " BD.CTL %#04RX16\n", (uint16_t)(pStream->Regs.bd.ctl_len >> 16));
3984 }
3985
3986 pHlp->pfnPrintf(pHlp, " offRead %#RX64\n", pStreamR3->State.offRead);
3987 pHlp->pfnPrintf(pHlp, " offWrite %#RX64\n", pStreamR3->State.offWrite);
3988 pHlp->pfnPrintf(pHlp, " uTimerHz %RU16\n", pStreamR3->State.uTimerHz);
3989 pHlp->pfnPrintf(pHlp, " cDmaPeriodTicks %RU64\n", pStream->cDmaPeriodTicks);
3990 pHlp->pfnPrintf(pHlp, " cbDmaPeriod %#RX32\n", pStream->cbDmaPeriod);
3991}
3992
3993
3994/**
3995 * @callback_method_impl{FNDBGFHANDLERDEV, ac97stream}
3996 */
3997static DECLCALLBACK(void) ichac97R3DbgInfoStream(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3998{
3999 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4000 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4001 int idxStream = ichac97R3DbgLookupStrmIdx(pHlp, pszArgs);
4002 if (idxStream != -1)
4003 ichac97R3DbgPrintStream(pHlp, &pThis->aStreams[idxStream], &pThisCC->aStreams[idxStream]);
4004 else
4005 for (idxStream = 0; idxStream < AC97_MAX_STREAMS; ++idxStream)
4006 ichac97R3DbgPrintStream(pHlp, &pThis->aStreams[idxStream], &pThisCC->aStreams[idxStream]);
4007}
4008
4009
4010/**
4011 * @callback_method_impl{FNDBGFHANDLERDEV, ac97mixer}
4012 */
4013static DECLCALLBACK(void) ichac97R3DbgInfoMixer(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4014{
4015 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4016 if (pThisCC->pMixer)
4017 AudioMixerDebug(pThisCC->pMixer, pHlp, pszArgs);
4018 else
4019 pHlp->pfnPrintf(pHlp, "Mixer not available\n");
4020}
4021
4022
4023/*********************************************************************************************************************************
4024* PDMIBASE *
4025*********************************************************************************************************************************/
4026
4027/**
4028 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4029 */
4030static DECLCALLBACK(void *) ichac97R3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
4031{
4032 PAC97STATER3 pThisCC = RT_FROM_MEMBER(pInterface, AC97STATER3, IBase);
4033 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4034 return NULL;
4035}
4036
4037
4038/*********************************************************************************************************************************
4039* PDMDEVREG *
4040*********************************************************************************************************************************/
4041
4042/**
4043 * Destroys all AC'97 audio streams of the device.
4044 *
4045 * @param pDevIns The device AC'97 instance.
4046 * @param pThis The shared AC'97 state.
4047 * @param pThisCC The ring-3 AC'97 state.
4048 */
4049static void ichac97R3StreamsDestroy(PPDMDEVINS pDevIns, PAC97STATE pThis, PAC97STATER3 pThisCC)
4050{
4051 LogFlowFuncEnter();
4052
4053 /*
4054 * Destroy all AC'97 streams.
4055 */
4056 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4057 ichac97R3StreamDestroy(pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i]);
4058
4059 /*
4060 * Destroy all sinks.
4061 */
4062 if (pThisCC->pSinkLineIn)
4063 {
4064 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkLineIn, PDMAUDIODIR_IN, PDMAUDIOPATH_IN_LINE);
4065
4066 AudioMixerSinkDestroy(pThisCC->pSinkLineIn, pDevIns);
4067 pThisCC->pSinkLineIn = NULL;
4068 }
4069
4070 if (pThisCC->pSinkMicIn)
4071 {
4072 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkMicIn, PDMAUDIODIR_IN, PDMAUDIOPATH_IN_MIC);
4073
4074 AudioMixerSinkDestroy(pThisCC->pSinkMicIn, pDevIns);
4075 pThisCC->pSinkMicIn = NULL;
4076 }
4077
4078 if (pThisCC->pSinkOut)
4079 {
4080 ichac97R3MixerRemoveDrvStreams(pDevIns, pThisCC, pThisCC->pSinkOut, PDMAUDIODIR_OUT, PDMAUDIOPATH_OUT_FRONT);
4081
4082 AudioMixerSinkDestroy(pThisCC->pSinkOut, pDevIns);
4083 pThisCC->pSinkOut = NULL;
4084 }
4085}
4086
4087
4088/**
4089 * Powers off the device.
4090 *
4091 * @param pDevIns Device instance to power off.
4092 */
4093static DECLCALLBACK(void) ichac97R3PowerOff(PPDMDEVINS pDevIns)
4094{
4095 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4096 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4097
4098 LogRel2(("AC97: Powering off ...\n"));
4099
4100 /* Note: Involves mixer stream / sink destruction, so also do this here
4101 * instead of in ichac97R3Destruct(). */
4102 ichac97R3StreamsDestroy(pDevIns, pThis, pThisCC);
4103
4104 /*
4105 * Note: Destroy the mixer while powering off and *not* in ichac97R3Destruct,
4106 * giving the mixer the chance to release any references held to
4107 * PDM audio streams it maintains.
4108 */
4109 if (pThisCC->pMixer)
4110 {
4111 AudioMixerDestroy(pThisCC->pMixer, pDevIns);
4112 pThisCC->pMixer = NULL;
4113 }
4114}
4115
4116
4117/**
4118 * @interface_method_impl{PDMDEVREG,pfnReset}
4119 *
4120 * @remarks The original sources didn't install a reset handler, but it seems to
4121 * make sense to me so we'll do it.
4122 */
4123static DECLCALLBACK(void) ichac97R3Reset(PPDMDEVINS pDevIns)
4124{
4125 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4126 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4127
4128 LogRel(("AC97: Reset\n"));
4129
4130 /*
4131 * Reset the mixer too. The Windows XP driver seems to rely on
4132 * this. At least it wants to read the vendor id before it resets
4133 * the codec manually.
4134 */
4135 ichac97R3MixerReset(pThis, pThisCC);
4136
4137 /*
4138 * Reset all streams.
4139 */
4140 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4141 {
4142 ichac97R3StreamEnable(pDevIns, pThis, pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i], false /* fEnable */);
4143 ichac97R3StreamReset(pThis, &pThis->aStreams[i], &pThisCC->aStreams[i]);
4144 }
4145
4146 /*
4147 * Reset mixer sinks.
4148 *
4149 * Do the reset here instead of in ichac97R3StreamReset();
4150 * the mixer sink(s) might still have data to be processed when an audio stream gets reset.
4151 */
4152 AudioMixerSinkReset(pThisCC->pSinkLineIn);
4153 AudioMixerSinkReset(pThisCC->pSinkMicIn);
4154 AudioMixerSinkReset(pThisCC->pSinkOut);
4155}
4156
4157
4158/**
4159 * Adds a specific AC'97 driver to the driver chain.
4160 *
4161 * Only called from ichac97R3Attach().
4162 *
4163 * @returns VBox status code.
4164 * @param pDevIns The device instance.
4165 * @param pThisCC The ring-3 AC'97 device state.
4166 * @param pDrv The AC'97 driver to add.
4167 */
4168static int ichac97R3MixerAddDrv(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAC97DRIVER pDrv)
4169{
4170 int rc = VINF_SUCCESS;
4171
4172 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX].State.Cfg))
4173 rc = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkLineIn,
4174 &pThisCC->aStreams[AC97SOUNDSOURCE_PI_INDEX].State.Cfg, pDrv);
4175
4176 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX].State.Cfg))
4177 {
4178 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkOut,
4179 &pThisCC->aStreams[AC97SOUNDSOURCE_PO_INDEX].State.Cfg, pDrv);
4180 if (RT_SUCCESS(rc))
4181 rc = rc2;
4182 }
4183
4184 if (AudioHlpStreamCfgIsValid(&pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX].State.Cfg))
4185 {
4186 int rc2 = ichac97R3MixerAddDrvStream(pDevIns, pThisCC->pSinkMicIn,
4187 &pThisCC->aStreams[AC97SOUNDSOURCE_MC_INDEX].State.Cfg, pDrv);
4188 if (RT_SUCCESS(rc))
4189 rc = rc2;
4190 }
4191
4192 return rc;
4193}
4194
4195
4196/**
4197 * Worker for ichac97R3Construct() and ichac97R3Attach().
4198 *
4199 * @returns VBox status code.
4200 * @param pDevIns The device instance.
4201 * @param pThisCC The ring-3 AC'97 device state.
4202 * @param uLUN The logical unit which is being attached.
4203 * @param ppDrv Attached driver instance on success. Optional.
4204 */
4205static int ichac97R3AttachInternal(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, unsigned uLUN, PAC97DRIVER *ppDrv)
4206{
4207 /*
4208 * Allocate a new driver structure and try attach the driver.
4209 */
4210 PAC97DRIVER pDrv = (PAC97DRIVER)RTMemAllocZ(sizeof(AC97DRIVER));
4211 AssertPtrReturn(pDrv, VERR_NO_MEMORY);
4212 RTStrPrintf(pDrv->szDesc, sizeof(pDrv->szDesc), "Audio driver port (AC'97) for LUN #%u", uLUN);
4213
4214 PPDMIBASE pDrvBase;
4215 int rc = PDMDevHlpDriverAttach(pDevIns, uLUN, &pThisCC->IBase, &pDrvBase, pDrv->szDesc);
4216 if (RT_SUCCESS(rc))
4217 {
4218 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
4219 AssertPtr(pDrv->pConnector);
4220 if (RT_VALID_PTR(pDrv->pConnector))
4221 {
4222 pDrv->pDrvBase = pDrvBase;
4223 pDrv->uLUN = uLUN;
4224
4225 /* Attach to driver list if not attached yet. */
4226 if (!pDrv->fAttached)
4227 {
4228 RTListAppend(&pThisCC->lstDrv, &pDrv->Node);
4229 pDrv->fAttached = true;
4230 }
4231
4232 if (ppDrv)
4233 *ppDrv = pDrv;
4234
4235 /*
4236 * While we're here, give the windows backends a hint about our typical playback
4237 * configuration.
4238 */
4239 if ( pDrv->pConnector
4240 && pDrv->pConnector->pfnStreamConfigHint)
4241 {
4242 /* 48kHz */
4243 PDMAUDIOSTREAMCFG Cfg;
4244 RT_ZERO(Cfg);
4245 Cfg.enmDir = PDMAUDIODIR_OUT;
4246 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
4247 Cfg.Device.cMsSchedulingHint = 5;
4248 Cfg.Backend.cFramesPreBuffering = UINT32_MAX;
4249 PDMAudioPropsInit(&Cfg.Props, 2, true /*fSigned*/, 2, 48000);
4250 RTStrPrintf(Cfg.szName, sizeof(Cfg.szName), "output 48kHz 2ch S16 (HDA config hint)");
4251
4252 pDrv->pConnector->pfnStreamConfigHint(pDrv->pConnector, &Cfg); /* (may trash CfgReq) */
4253# if 0
4254 /* 44.1kHz */
4255 RT_ZERO(Cfg);
4256 Cfg.enmDir = PDMAUDIODIR_OUT;
4257 Cfg.enmPath = PDMAUDIOPATH_OUT_FRONT;
4258 Cfg.Device.cMsSchedulingHint = 10;
4259 Cfg.Backend.cFramesPreBuffering = UINT32_MAX;
4260 PDMAudioPropsInit(&Cfg.Props, 2, true /*fSigned*/, 2, 44100);
4261 RTStrPrintf(Cfg.szName, sizeof(Cfg.szName), "output 44.1kHz 2ch S16 (HDA config hint)");
4262
4263 pDrv->pConnector->pfnStreamConfigHint(pDrv->pConnector, &Cfg); /* (may trash CfgReq) */
4264# endif
4265 }
4266
4267 LogFunc(("LUN#%u: returns VINF_SUCCESS (pCon=%p)\n", uLUN, pDrv->pConnector));
4268 return VINF_SUCCESS;
4269 }
4270 RTMemFree(pDrv);
4271 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
4272 }
4273 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4274 LogFunc(("No attached driver for LUN #%u\n", uLUN));
4275 else
4276 LogFunc(("Attached driver for LUN #%u failed: %Rrc\n", uLUN, rc));
4277 RTMemFree(pDrv);
4278
4279 LogFunc(("LUN#%u: rc=%Rrc\n", uLUN, rc));
4280 return rc;
4281}
4282
4283
4284/**
4285 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
4286 */
4287static DECLCALLBACK(int) ichac97R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4288{
4289 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4290 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4291 RT_NOREF(fFlags);
4292 LogFunc(("iLUN=%u, fFlags=%#x\n", iLUN, fFlags));
4293
4294 DEVAC97_LOCK(pDevIns, pThis);
4295
4296 PAC97DRIVER pDrv;
4297 int rc = ichac97R3AttachInternal(pDevIns, pThisCC, iLUN, &pDrv);
4298 if (RT_SUCCESS(rc))
4299 {
4300 int rc2 = ichac97R3MixerAddDrv(pDevIns, pThisCC, pDrv);
4301 if (RT_FAILURE(rc2))
4302 LogFunc(("ichac97R3MixerAddDrv failed with %Rrc (ignored)\n", rc2));
4303 }
4304
4305 DEVAC97_UNLOCK(pDevIns, pThis);
4306
4307 return rc;
4308}
4309
4310
4311/**
4312 * Removes a specific AC'97 driver from the driver chain and destroys its
4313 * associated streams.
4314 *
4315 * Only called from ichac97R3Detach().
4316 *
4317 * @param pDevIns The device instance.
4318 * @param pThisCC The ring-3 AC'97 device state.
4319 * @param pDrv AC'97 driver to remove.
4320 */
4321static void ichac97R3MixerRemoveDrv(PPDMDEVINS pDevIns, PAC97STATER3 pThisCC, PAC97DRIVER pDrv)
4322{
4323 if (pDrv->MicIn.pMixStrm)
4324 {
4325 AudioMixerSinkRemoveStream(pThisCC->pSinkMicIn, pDrv->MicIn.pMixStrm);
4326 AudioMixerStreamDestroy(pDrv->MicIn.pMixStrm, pDevIns, true /*fImmediate*/);
4327 pDrv->MicIn.pMixStrm = NULL;
4328 }
4329
4330 if (pDrv->LineIn.pMixStrm)
4331 {
4332 AudioMixerSinkRemoveStream(pThisCC->pSinkLineIn, pDrv->LineIn.pMixStrm);
4333 AudioMixerStreamDestroy(pDrv->LineIn.pMixStrm, pDevIns, true /*fImmediate*/);
4334 pDrv->LineIn.pMixStrm = NULL;
4335 }
4336
4337 if (pDrv->Out.pMixStrm)
4338 {
4339 AudioMixerSinkRemoveStream(pThisCC->pSinkOut, pDrv->Out.pMixStrm);
4340 AudioMixerStreamDestroy(pDrv->Out.pMixStrm, pDevIns, true /*fImmediate*/);
4341 pDrv->Out.pMixStrm = NULL;
4342 }
4343
4344 RTListNodeRemove(&pDrv->Node);
4345}
4346
4347
4348/**
4349 * @interface_method_impl{PDMDEVREG,pfnDetach}
4350 */
4351static DECLCALLBACK(void) ichac97R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4352{
4353 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4354 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4355 RT_NOREF(fFlags);
4356
4357 LogFunc(("iLUN=%u, fFlags=0x%x\n", iLUN, fFlags));
4358
4359 DEVAC97_LOCK(pDevIns, pThis);
4360
4361 PAC97DRIVER pDrv;
4362 RTListForEach(&pThisCC->lstDrv, pDrv, AC97DRIVER, Node)
4363 {
4364 if (pDrv->uLUN == iLUN)
4365 {
4366 /* Remove the driver from our list and destory it's associated streams.
4367 This also will un-set the driver as a recording source (if associated). */
4368 ichac97R3MixerRemoveDrv(pDevIns, pThisCC, pDrv);
4369 LogFunc(("Detached LUN#%u\n", pDrv->uLUN));
4370
4371 DEVAC97_UNLOCK(pDevIns, pThis);
4372
4373 RTMemFree(pDrv);
4374 return;
4375 }
4376 }
4377
4378 DEVAC97_UNLOCK(pDevIns, pThis);
4379 LogFunc(("LUN#%u was not found\n", iLUN));
4380}
4381
4382
4383/**
4384 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4385 */
4386static DECLCALLBACK(int) ichac97R3Destruct(PPDMDEVINS pDevIns)
4387{
4388 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); /* this shall come first */
4389 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4390
4391 LogFlowFuncEnter();
4392
4393 PAC97DRIVER pDrv, pDrvNext;
4394 RTListForEachSafe(&pThisCC->lstDrv, pDrv, pDrvNext, AC97DRIVER, Node)
4395 {
4396 RTListNodeRemove(&pDrv->Node);
4397 RTMemFree(pDrv);
4398 }
4399
4400 /* Sanity. */
4401 Assert(RTListIsEmpty(&pThisCC->lstDrv));
4402
4403 /* We don't always go via PowerOff, so make sure the mixer is destroyed. */
4404 if (pThisCC->pMixer)
4405 {
4406 AudioMixerDestroy(pThisCC->pMixer, pDevIns);
4407 pThisCC->pMixer = NULL;
4408 }
4409
4410 return VINF_SUCCESS;
4411}
4412
4413
4414/**
4415 * @interface_method_impl{PDMDEVREG,pfnConstruct}
4416 */
4417static DECLCALLBACK(int) ichac97R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4418{
4419 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); /* this shall come first */
4420 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4421 PAC97STATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PAC97STATER3);
4422 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4423 Assert(iInstance == 0); RT_NOREF(iInstance);
4424
4425 /*
4426 * Initialize data so we can run the destructor without scewing up.
4427 */
4428 pThisCC->pDevIns = pDevIns;
4429 pThisCC->IBase.pfnQueryInterface = ichac97R3QueryInterface;
4430 RTListInit(&pThisCC->lstDrv);
4431
4432 /*
4433 * Validate and read configuration.
4434 */
4435 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "BufSizeInMs|BufSizeOutMs|Codec|TimerHz|DebugEnabled|DebugPathOut", "");
4436
4437 /** @devcfgm{ac97,BufSizeInMs,uint16_t,0,2000,0,ms}
4438 * The size of the DMA buffer for input streams expressed in milliseconds. */
4439 int rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeInMs", &pThis->cMsCircBufIn, 0);
4440 if (RT_FAILURE(rc))
4441 return PDMDEV_SET_ERROR(pDevIns, rc,
4442 N_("AC97 configuration error: failed to read 'BufSizeInMs' as 16-bit unsigned integer"));
4443 if (pThis->cMsCircBufIn > 2000)
4444 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4445 N_("AC97 configuration error: 'BufSizeInMs' is out of bound, max 2000 ms"));
4446
4447 /** @devcfgm{ac97,BufSizeOutMs,uint16_t,0,2000,0,ms}
4448 * The size of the DMA buffer for output streams expressed in milliseconds. */
4449 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BufSizeOutMs", &pThis->cMsCircBufOut, 0);
4450 if (RT_FAILURE(rc))
4451 return PDMDEV_SET_ERROR(pDevIns, rc,
4452 N_("AC97 configuration error: failed to read 'BufSizeOutMs' as 16-bit unsigned integer"));
4453 if (pThis->cMsCircBufOut > 2000)
4454 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4455 N_("AC97 configuration error: 'BufSizeOutMs' is out of bound, max 2000 ms"));
4456
4457 /** @devcfgm{ac97,TimerHz,uint16_t,10,1000,100,ms}
4458 * Currently the approximate rate at which the asynchronous I/O threads move
4459 * data from/to the DMA buffer, thru the mixer and drivers stack, and
4460 * to/from the host device/whatever. (It does NOT govern any DMA timer rate any
4461 * more as might be hinted at by the name.) */
4462 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "TimerHz", &pThis->uTimerHz, AC97_TIMER_HZ_DEFAULT);
4463 if (RT_FAILURE(rc))
4464 return PDMDEV_SET_ERROR(pDevIns, rc,
4465 N_("AC'97 configuration error: failed to read 'TimerHz' as a 16-bit unsigned integer"));
4466 if (pThis->uTimerHz < 10 || pThis->uTimerHz > 1000)
4467 return PDMDEV_SET_ERROR(pDevIns, VERR_OUT_OF_RANGE,
4468 N_("AC'97 configuration error: 'TimerHz' is out of range (10-1000 Hz)"));
4469
4470 if (pThis->uTimerHz != AC97_TIMER_HZ_DEFAULT)
4471 LogRel(("AC97: Using custom device timer rate: %RU16 Hz\n", pThis->uTimerHz));
4472
4473 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "DebugEnabled", &pThisCC->Dbg.fEnabled, false);
4474 if (RT_FAILURE(rc))
4475 return PDMDEV_SET_ERROR(pDevIns, rc,
4476 N_("AC97 configuration error: failed to read debugging enabled flag as boolean"));
4477
4478 rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "DebugPathOut", &pThisCC->Dbg.pszOutPath, NULL);
4479 if (RT_FAILURE(rc))
4480 return PDMDEV_SET_ERROR(pDevIns, rc,
4481 N_("AC97 configuration error: failed to read debugging output path flag as string"));
4482
4483 if (pThisCC->Dbg.fEnabled)
4484 LogRel2(("AC97: Debug output will be saved to '%s'\n", pThisCC->Dbg.pszOutPath));
4485
4486 /*
4487 * The AD1980 codec (with corresponding PCI subsystem vendor ID) is whitelisted
4488 * in the Linux kernel; Linux makes no attempt to measure the data rate and assumes
4489 * 48 kHz rate, which is exactly what we need. Same goes for AD1981B.
4490 */
4491 char szCodec[20];
4492 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "Codec", &szCodec[0], sizeof(szCodec), "STAC9700");
4493 if (RT_FAILURE(rc))
4494 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4495 N_("AC'97 configuration error: Querying \"Codec\" as string failed"));
4496 if (!strcmp(szCodec, "STAC9700"))
4497 pThis->enmCodecModel = AC97CODEC_STAC9700;
4498 else if (!strcmp(szCodec, "AD1980"))
4499 pThis->enmCodecModel = AC97CODEC_AD1980;
4500 else if (!strcmp(szCodec, "AD1981B"))
4501 pThis->enmCodecModel = AC97CODEC_AD1981B;
4502 else
4503 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
4504 N_("AC'97 configuration error: The \"Codec\" value \"%s\" is unsupported"), szCodec);
4505
4506 LogRel(("AC97: Using codec '%s'\n", szCodec));
4507
4508 /*
4509 * Use an own critical section for the device instead of the default
4510 * one provided by PDM. This allows fine-grained locking in combination
4511 * with TM when timer-specific stuff is being called in e.g. the MMIO handlers.
4512 */
4513 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "AC'97");
4514 AssertRCReturn(rc, rc);
4515
4516 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
4517 AssertRCReturn(rc, rc);
4518
4519 /*
4520 * Initialize data (most of it anyway).
4521 */
4522 /* PCI Device */
4523 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
4524 PCIDevSetVendorId(pPciDev, 0x8086); /* 00 ro - intel. */ Assert(pPciDev->abConfig[0x00] == 0x86); Assert(pPciDev->abConfig[0x01] == 0x80);
4525 PCIDevSetDeviceId(pPciDev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */ Assert(pPciDev->abConfig[0x02] == 0x15); Assert(pPciDev->abConfig[0x03] == 0x24);
4526 PCIDevSetCommand(pPciDev, 0x0000); /* 04 rw,ro - pcicmd. */ Assert(pPciDev->abConfig[0x04] == 0x00); Assert(pPciDev->abConfig[0x05] == 0x00);
4527 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);
4528 PCIDevSetRevisionId(pPciDev, 0x01); /* 08 ro - rid. */ Assert(pPciDev->abConfig[0x08] == 0x01);
4529 PCIDevSetClassProg(pPciDev, 0x00); /* 09 ro - pi. */ Assert(pPciDev->abConfig[0x09] == 0x00);
4530 PCIDevSetClassSub(pPciDev, 0x01); /* 0a ro - scc; 01 == Audio. */ Assert(pPciDev->abConfig[0x0a] == 0x01);
4531 PCIDevSetClassBase(pPciDev, 0x04); /* 0b ro - bcc; 04 == multimedia.*/Assert(pPciDev->abConfig[0x0b] == 0x04);
4532 PCIDevSetHeaderType(pPciDev, 0x00); /* 0e ro - headtyp. */ Assert(pPciDev->abConfig[0x0e] == 0x00);
4533 PCIDevSetBaseAddress(pPciDev, 0, /* 10 rw - nambar - native audio mixer base. */
4534 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);
4535 PCIDevSetBaseAddress(pPciDev, 1, /* 14 rw - nabmbar - native audio bus mastering. */
4536 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);
4537 PCIDevSetInterruptLine(pPciDev, 0x00); /* 3c rw. */ Assert(pPciDev->abConfig[0x3c] == 0x00);
4538 PCIDevSetInterruptPin(pPciDev, 0x01); /* 3d ro - INTA#. */ Assert(pPciDev->abConfig[0x3d] == 0x01);
4539
4540 if (pThis->enmCodecModel == AC97CODEC_AD1980)
4541 {
4542 PCIDevSetSubSystemVendorId(pPciDev, 0x1028); /* 2c ro - Dell.) */
4543 PCIDevSetSubSystemId(pPciDev, 0x0177); /* 2e ro. */
4544 }
4545 else if (pThis->enmCodecModel == AC97CODEC_AD1981B)
4546 {
4547 PCIDevSetSubSystemVendorId(pPciDev, 0x1028); /* 2c ro - Dell.) */
4548 PCIDevSetSubSystemId(pPciDev, 0x01ad); /* 2e ro. */
4549 }
4550 else
4551 {
4552 PCIDevSetSubSystemVendorId(pPciDev, 0x8086); /* 2c ro - Intel.) */
4553 PCIDevSetSubSystemId(pPciDev, 0x0000); /* 2e ro. */
4554 }
4555
4556 /*
4557 * Register the PCI device and associated I/O regions.
4558 */
4559 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
4560 if (RT_FAILURE(rc))
4561 return rc;
4562
4563 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 0 /*iPciRegion*/, 256 /*cPorts*/,
4564 ichac97IoPortNamWrite, ichac97IoPortNamRead, NULL /*pvUser*/,
4565 "ICHAC97 NAM", NULL /*paExtDescs*/, &pThis->hIoPortsNam);
4566 AssertRCReturn(rc, rc);
4567
4568 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 1 /*iPciRegion*/, 64 /*cPorts*/,
4569 ichac97IoPortNabmWrite, ichac97IoPortNabmRead, NULL /*pvUser*/,
4570 "ICHAC97 NABM", g_aNabmPorts, &pThis->hIoPortsNabm);
4571 AssertRCReturn(rc, rc);
4572
4573 /*
4574 * Saved state.
4575 */
4576 rc = PDMDevHlpSSMRegister(pDevIns, AC97_SAVED_STATE_VERSION, sizeof(*pThis), ichac97R3SaveExec, ichac97R3LoadExec);
4577 if (RT_FAILURE(rc))
4578 return rc;
4579
4580 /*
4581 * Attach drivers. We ASSUME they are configured consecutively without any
4582 * gaps, so we stop when we hit the first LUN w/o a driver configured.
4583 */
4584 for (unsigned iLun = 0; ; iLun++)
4585 {
4586 AssertBreak(iLun < UINT8_MAX);
4587 LogFunc(("Trying to attach driver for LUN#%u ...\n", iLun));
4588 rc = ichac97R3AttachInternal(pDevIns, pThisCC, iLun, NULL /* ppDrv */);
4589 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4590 {
4591 LogFunc(("cLUNs=%u\n", iLun));
4592 break;
4593 }
4594 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("LUN#%u: rc=%Rrc\n", iLun, rc), rc);
4595 }
4596
4597 uint32_t fMixer = AUDMIXER_FLAGS_NONE;
4598 if (pThisCC->Dbg.fEnabled)
4599 fMixer |= AUDMIXER_FLAGS_DEBUG;
4600
4601 rc = AudioMixerCreate("AC'97 Mixer", 0 /* uFlags */, &pThisCC->pMixer);
4602 AssertRCReturn(rc, rc);
4603
4604 rc = AudioMixerCreateSink(pThisCC->pMixer, "Line In",
4605 PDMAUDIODIR_IN, pDevIns, &pThisCC->pSinkLineIn);
4606 AssertRCReturn(rc, rc);
4607 rc = AudioMixerCreateSink(pThisCC->pMixer, "Microphone In",
4608 PDMAUDIODIR_IN, pDevIns, &pThisCC->pSinkMicIn);
4609 AssertRCReturn(rc, rc);
4610 rc = AudioMixerCreateSink(pThisCC->pMixer, "PCM Output",
4611 PDMAUDIODIR_OUT, pDevIns, &pThisCC->pSinkOut);
4612 AssertRCReturn(rc, rc);
4613
4614 /*
4615 * Create all hardware streams.
4616 */
4617 AssertCompile(RT_ELEMENTS(pThis->aStreams) == AC97_MAX_STREAMS);
4618 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4619 {
4620 rc = ichac97R3StreamConstruct(pThisCC, &pThis->aStreams[i], &pThisCC->aStreams[i], i /* SD# */);
4621 AssertRCReturn(rc, rc);
4622 }
4623
4624 /*
4625 * Create the emulation timers (one per stream).
4626 *
4627 * We must the critical section for the timers as the device has a
4628 * noop section associated with it.
4629 *
4630 * Note: Use TMCLOCK_VIRTUAL_SYNC here, as the guest's AC'97 driver
4631 * relies on exact (virtual) DMA timing and uses DMA Position Buffers
4632 * instead of the LPIB registers.
4633 */
4634 /** @todo r=bird: The need to use virtual sync is perhaps because TM
4635 * doesn't schedule regular TMCLOCK_VIRTUAL timers as accurately as it
4636 * should (VT-x preemption timer, etc). Hope to address that before
4637 * long. @bugref{9943}. */
4638 static const char * const s_apszNames[] = { "AC97 PI", "AC97 PO", "AC97 MC" };
4639 AssertCompile(RT_ELEMENTS(s_apszNames) == AC97_MAX_STREAMS);
4640 for (unsigned i = 0; i < AC97_MAX_STREAMS; i++)
4641 {
4642 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, ichac97R3Timer, &pThis->aStreams[i],
4643 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, s_apszNames[i], &pThis->aStreams[i].hTimer);
4644 AssertRCReturn(rc, rc);
4645
4646 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->aStreams[i].hTimer, &pThis->CritSect);
4647 AssertRCReturn(rc, rc);
4648 }
4649
4650 ichac97R3Reset(pDevIns);
4651
4652 /*
4653 * Info items.
4654 */
4655 //PDMDevHlpDBGFInfoRegister(pDevIns, "ac97", "AC'97 registers. (ac97 [register case-insensitive])", ichac97R3DbgInfo);
4656 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97bdl", "AC'97 buffer descriptor list (BDL). (ac97bdl [stream number])",
4657 ichac97R3DbgInfoBDL);
4658 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97stream", "AC'97 stream info. (ac97stream [stream number])", ichac97R3DbgInfoStream);
4659 PDMDevHlpDBGFInfoRegister(pDevIns, "ac97mixer", "AC'97 mixer state.", ichac97R3DbgInfoMixer);
4660
4661 /*
4662 * Register statistics.
4663 */
4664 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNabmReads, STAMTYPE_COUNTER, "UnimplementedNabmReads", STAMUNIT_OCCURENCES, "Unimplemented NABM register reads.");
4665 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNabmWrites, STAMTYPE_COUNTER, "UnimplementedNabmWrites", STAMUNIT_OCCURENCES, "Unimplemented NABM register writes.");
4666 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNamReads, STAMTYPE_COUNTER, "UnimplementedNamReads", STAMUNIT_OCCURENCES, "Unimplemented NAM register reads.");
4667 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUnimplementedNamWrites, STAMTYPE_COUNTER, "UnimplementedNamWrites", STAMUNIT_OCCURENCES, "Unimplemented NAM register writes.");
4668# ifdef VBOX_WITH_STATISTICS
4669 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ichac97Timer.");
4670# endif
4671 for (unsigned idxStream = 0; idxStream < RT_ELEMENTS(pThis->aStreams); idxStream++)
4672 {
4673 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].cbDmaPeriod, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4674 "Bytes to transfer in the current DMA period.", "Stream%u/cbTransferChunk", idxStream);
4675 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].Regs.cr, STAMTYPE_X8, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
4676 "Control register (CR), bit 0 is the run bit.", "Stream%u/reg-CR", idxStream);
4677 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].Regs.sr, STAMTYPE_X16, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
4678 "Status register (SR).", "Stream%u/reg-SR", idxStream);
4679 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.Cfg.Props.uHz, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_HZ,
4680 "The stream frequency.", "Stream%u/Hz", idxStream);
4681 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.Cfg.Props.cbFrame, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4682 "The frame size.", "Stream%u/FrameSize", idxStream);
4683 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offRead, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4684 "Virtual internal buffer read position.", "Stream%u/offRead", idxStream);
4685 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.offWrite, STAMTYPE_U64, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4686 "Virtual internal buffer write position.", "Stream%u/offWrite", idxStream);
4687 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaBufSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4688 "Size of the internal DMA buffer.", "Stream%u/DMABufSize", idxStream);
4689 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaBufUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4690 "Number of bytes used in the internal DMA buffer.", "Stream%u/DMABufUsed", idxStream);
4691 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowProblems, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4692 "Number of internal DMA buffer problems.", "Stream%u/DMABufferProblems", idxStream);
4693 if (ichac97R3GetDirFromSD(idxStream) == PDMAUDIODIR_OUT)
4694 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrors, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4695 "Number of internal DMA buffer overflows.", "Stream%u/DMABufferOverflows", idxStream);
4696 else
4697 {
4698 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrors, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4699 "Number of internal DMA buffer underuns.", "Stream%u/DMABufferUnderruns", idxStream);
4700 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaFlowErrorBytes, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4701 "Number of bytes of silence added to cope with underruns.", "Stream%u/DMABufferSilence", idxStream);
4702 }
4703 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaSkippedDch, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4704 "DMA transfer period skipped, controller halted (DCH).", "Stream%u/DMASkippedDch", idxStream);
4705 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatDmaSkippedPendingBcis, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4706 "DMA transfer period skipped because of BCIS pending.", "Stream%u/DMASkippedPendingBCIS", idxStream);
4707
4708 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatStart, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4709 "Starting the stream.", "Stream%u/Start", idxStream);
4710 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatStop, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4711 "Stopping the stream.", "Stream%u/Stop", idxStream);
4712 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReset, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4713 "Resetting the stream.", "Stream%u/Reset", idxStream);
4714 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReSetUpChanged, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4715 "ichac97R3StreamReSetUp when recreating the streams.", "Stream%u/ReSetUp-Change", idxStream);
4716 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatReSetUpSame, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
4717 "ichac97R3StreamReSetUp when no change.", "Stream%u/ReSetUp-NoChange", idxStream);
4718 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatWriteCr, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4719 "CR register writes.", "Stream%u/WriteCr", idxStream);
4720 PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.StatWriteLviRecover, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4721 "LVI register writes recovering from underflow.", "Stream%u/WriteLviRecover", idxStream);
4722 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteLvi, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4723 "LVI register writes (non-recoving).", "Stream%u/WriteLvi", idxStream);
4724 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteSr1, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4725 "SR register 1-byte writes.", "Stream%u/WriteSr-1byte", idxStream);
4726 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteSr2, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4727 "SR register 2-byte writes.", "Stream%u/WriteSr-2byte", idxStream);
4728 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].StatWriteBdBar, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4729 "BDBAR register writes.", "Stream%u/WriteBdBar", idxStream);
4730 }
4731
4732 LogFlowFuncLeaveRC(VINF_SUCCESS);
4733 return VINF_SUCCESS;
4734}
4735
4736#else /* !IN_RING3 */
4737
4738/**
4739 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
4740 */
4741static DECLCALLBACK(int) ichac97RZConstruct(PPDMDEVINS pDevIns)
4742{
4743 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4744 PAC97STATE pThis = PDMDEVINS_2_DATA(pDevIns, PAC97STATE);
4745
4746 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
4747 AssertRCReturn(rc, rc);
4748
4749 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNam, ichac97IoPortNamWrite, ichac97IoPortNamRead, NULL /*pvUser*/);
4750 AssertRCReturn(rc, rc);
4751 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNabm, ichac97IoPortNabmWrite, ichac97IoPortNabmRead, NULL /*pvUser*/);
4752 AssertRCReturn(rc, rc);
4753
4754 return VINF_SUCCESS;
4755}
4756
4757#endif /* !IN_RING3 */
4758
4759/**
4760 * The device registration structure.
4761 */
4762const PDMDEVREG g_DeviceICHAC97 =
4763{
4764 /* .u32Version = */ PDM_DEVREG_VERSION,
4765 /* .uReserved0 = */ 0,
4766 /* .szName = */ "ichac97",
4767 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
4768 | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION /* stream clearnup with working drivers */,
4769 /* .fClass = */ PDM_DEVREG_CLASS_AUDIO,
4770 /* .cMaxInstances = */ 1,
4771 /* .uSharedVersion = */ 42,
4772 /* .cbInstanceShared = */ sizeof(AC97STATE),
4773 /* .cbInstanceCC = */ CTX_EXPR(sizeof(AC97STATER3), 0, 0),
4774 /* .cbInstanceRC = */ 0,
4775 /* .cMaxPciDevices = */ 1,
4776 /* .cMaxMsixVectors = */ 0,
4777 /* .pszDescription = */ "ICH AC'97 Audio Controller",
4778#if defined(IN_RING3)
4779 /* .pszRCMod = */ "VBoxDDRC.rc",
4780 /* .pszR0Mod = */ "VBoxDDR0.r0",
4781 /* .pfnConstruct = */ ichac97R3Construct,
4782 /* .pfnDestruct = */ ichac97R3Destruct,
4783 /* .pfnRelocate = */ NULL,
4784 /* .pfnMemSetup = */ NULL,
4785 /* .pfnPowerOn = */ NULL,
4786 /* .pfnReset = */ ichac97R3Reset,
4787 /* .pfnSuspend = */ NULL,
4788 /* .pfnResume = */ NULL,
4789 /* .pfnAttach = */ ichac97R3Attach,
4790 /* .pfnDetach = */ ichac97R3Detach,
4791 /* .pfnQueryInterface = */ NULL,
4792 /* .pfnInitComplete = */ NULL,
4793 /* .pfnPowerOff = */ ichac97R3PowerOff,
4794 /* .pfnSoftReset = */ NULL,
4795 /* .pfnReserved0 = */ NULL,
4796 /* .pfnReserved1 = */ NULL,
4797 /* .pfnReserved2 = */ NULL,
4798 /* .pfnReserved3 = */ NULL,
4799 /* .pfnReserved4 = */ NULL,
4800 /* .pfnReserved5 = */ NULL,
4801 /* .pfnReserved6 = */ NULL,
4802 /* .pfnReserved7 = */ NULL,
4803#elif defined(IN_RING0)
4804 /* .pfnEarlyConstruct = */ NULL,
4805 /* .pfnConstruct = */ ichac97RZConstruct,
4806 /* .pfnDestruct = */ NULL,
4807 /* .pfnFinalDestruct = */ NULL,
4808 /* .pfnRequest = */ NULL,
4809 /* .pfnReserved0 = */ NULL,
4810 /* .pfnReserved1 = */ NULL,
4811 /* .pfnReserved2 = */ NULL,
4812 /* .pfnReserved3 = */ NULL,
4813 /* .pfnReserved4 = */ NULL,
4814 /* .pfnReserved5 = */ NULL,
4815 /* .pfnReserved6 = */ NULL,
4816 /* .pfnReserved7 = */ NULL,
4817#elif defined(IN_RC)
4818 /* .pfnConstruct = */ ichac97RZConstruct,
4819 /* .pfnReserved0 = */ NULL,
4820 /* .pfnReserved1 = */ NULL,
4821 /* .pfnReserved2 = */ NULL,
4822 /* .pfnReserved3 = */ NULL,
4823 /* .pfnReserved4 = */ NULL,
4824 /* .pfnReserved5 = */ NULL,
4825 /* .pfnReserved6 = */ NULL,
4826 /* .pfnReserved7 = */ NULL,
4827#else
4828# error "Not in IN_RING3, IN_RING0 or IN_RC!"
4829#endif
4830 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
4831};
4832
4833#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
4834
Note: See TracBrowser for help on using the repository browser.

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