VirtualBox

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

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

DevIchAc97: Don't try setup streams with BDBAR=0 as that upsets the IOMMU. bugref:9890

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