VirtualBox

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

Last change on this file since 89801 was 89787, checked in by vboxsync, 4 years ago

DevIchAc97: Doxygen grouping fix. bugref:9890

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

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