VirtualBox

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

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

VMM,Devices: Eliminate direct calls to DBGFR3* and use the device helper callbacks, bugref:10074

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