VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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