VirtualBox

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

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

DevIchAc97: Eliminated the pRegs variables, just use pStream->Regs. instead. bugref:9890

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