VirtualBox

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

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

Audio: Baked AudioHlpFileNameGet into AudioHlpFileCreate and added name formatting. bugref:9890

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