VirtualBox

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

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

AudioMixer: More cleanups. bugref:9890

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