VirtualBox

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

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

Dev*: Checked up all the PDMDevHlpCritSectEnter calls to make sure the status code is checked. bugref:6695

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

© 2023 Oracle
ContactPrivacy policyTerms of Use