VirtualBox

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

Last change on this file since 89110 was 88991, checked in by vboxsync, 4 years ago

Audio: Worked over draining, starting with the internal DMA buffer (instead of just the pre-buffer and backend buffer) and using the async I/O thread to keep calling PDMIAUDIOCONNECTOR::pfnStreamIterate and PDMIHOSTAUDIO::pfnStreamPlay (NULL buffer) every so often till the draining is done. Also put a rough deadline on the draining. The PDMAUDIOSTREAMCMD_DISABLE is now defined to stop playback/capturing immediately, even when already draining (if possible). This gets rid of the timers in DrvAudio and windows backends. DrvAudio no longer issue an DISABLE command at the end of the drain, it assumes the backend does that internally itself. After issuing PDMAUDIOSTREAMCMD_DRAIN the client (be it mixer or drvaudio) will not provide any more data for the buffers via pfnStreamPlay. Only tested windows, needs to re-test all platforms. bugref:9890

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