VirtualBox

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

Last change on this file since 88158 was 88057, checked in by vboxsync, 4 years ago

Audio: enums must have an 'END' entry for validation purposes. bugref:9890

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