VirtualBox

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

Last change on this file since 84343 was 82988, checked in by vboxsync, 5 years ago

DevIchAc97/ichac97R3MixerAddDrvStream: Destroy streams on failure to prevent annoying assertions in STAM about duplicate statistics when the stream is reopened. Not sure if this is the right fix, but clearly someone didn't test ICH97 with VRDP enabled in a debug/strict build.

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