VirtualBox

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

Last change on this file since 60404 was 60361, checked in by vboxsync, 8 years ago

Spaces.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.8 KB
Line 
1/* $Id: DevIchAc97.cpp 60361 2016-04-06 14:05:31Z vboxsync $ */
2/** @file
3 * DevIchAc97 - VBox ICH AC97 Audio Controller.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_AC97
23#include <VBox/log.h>
24#include <VBox/vmm/pdmdev.h>
25#include <VBox/vmm/pdmaudioifs.h>
26
27#include <iprt/assert.h>
28#ifdef IN_RING3
29# include <iprt/mem.h>
30# include <iprt/string.h>
31# include <iprt/uuid.h>
32#endif
33
34#include "VBoxDD.h"
35
36#include "AudioMixBuffer.h"
37#include "AudioMixer.h"
38#include "DrvAudio.h"
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44
45#ifdef DEBUG
46//#define DEBUG_LUN
47# ifdef DEBUG_LUN
48# define DEBUG_LUN_NUM 1
49# endif
50#endif /* DEBUG */
51
52#define AC97_SSM_VERSION 1
53
54#ifdef VBOX
55# define SOFT_VOLUME /** @todo Get rid of this crap. */
56#else
57# define SOFT_VOLUME
58#endif
59
60#define SR_FIFOE RT_BIT(4) /* rwc, FIFO error. */
61#define SR_BCIS RT_BIT(3) /* rwc, Buffer completion interrupt status. */
62#define SR_LVBCI RT_BIT(2) /* rwc, Last valid buffer completion interrupt. */
63#define SR_CELV RT_BIT(1) /* ro, Current equals last valid. */
64#define SR_DCH RT_BIT(0) /* ro, Controller halted. */
65#define SR_VALID_MASK (RT_BIT(5) - 1)
66#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
67#define SR_RO_MASK (SR_DCH | SR_CELV)
68#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
69
70#define CR_IOCE RT_BIT(4) /* rw, Interrupt On Completion Enable. */
71#define CR_FEIE RT_BIT(3) /* rw FIFO Error Interrupt Enable. */
72#define CR_LVBIE RT_BIT(2) /* rw */
73#define CR_RR RT_BIT(1) /* rw */
74#define CR_RPBM RT_BIT(0) /* rw */
75#define CR_VALID_MASK (RT_BIT(5) - 1)
76#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
77
78#define GC_WR 4 /* rw */
79#define GC_CR 2 /* rw */
80#define GC_VALID_MASK (RT_BIT(6) - 1)
81
82#define GS_MD3 RT_BIT(17) /* rw */
83#define GS_AD3 RT_BIT(16) /* rw */
84#define GS_RCS RT_BIT(15) /* rwc */
85#define GS_B3S12 RT_BIT(14) /* ro */
86#define GS_B2S12 RT_BIT(13) /* ro */
87#define GS_B1S12 RT_BIT(12) /* ro */
88#define GS_S1R1 RT_BIT(11) /* rwc */
89#define GS_S0R1 RT_BIT(10) /* rwc */
90#define GS_S1CR RT_BIT(9) /* ro */
91#define GS_S0CR RT_BIT(8) /* ro */
92#define GS_MINT RT_BIT(7) /* ro */
93#define GS_POINT RT_BIT(6) /* ro */
94#define GS_PIINT RT_BIT(5) /* ro */
95#define GS_RSRVD (RT_BIT(4)|RT_BIT(3))
96#define GS_MOINT RT_BIT(2) /* ro */
97#define GS_MIINT RT_BIT(1) /* ro */
98#define GS_GSCI RT_BIT(0) /* rwc */
99#define GS_RO_MASK (GS_B3S12 | \
100 GS_B2S12 | \
101 GS_B1S12 | \
102 GS_S1CR | \
103 GS_S0CR | \
104 GS_MINT | \
105 GS_POINT | \
106 GS_PIINT | \
107 GS_RSRVD | \
108 GS_MOINT | \
109 GS_MIINT)
110#define GS_VALID_MASK (RT_BIT(18) - 1)
111#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
112
113/** @name Buffer Descriptor
114 * @{ */
115#define BD_IOC RT_BIT(31) /**< Interrupt on Completion */
116#define BD_BUP RT_BIT(30) /**< Buffer Underrun Policy */
117/** @} */
118
119#define EACS_VRA 1
120#define EACS_VRM 8
121
122#define VOL_MASK 0x1f
123#define MUTE_SHIFT 15
124
125#define REC_MASK 7
126enum
127{
128 REC_MIC = 0,
129 REC_CD,
130 REC_VIDEO,
131 REC_AUX,
132 REC_LINE_IN,
133 REC_STEREO_MIX,
134 REC_MONO_MIX,
135 REC_PHONE
136};
137
138enum
139{
140 AC97_Reset = 0x00,
141 AC97_Master_Volume_Mute = 0x02,
142 AC97_Headphone_Volume_Mute = 0x04, /** Also known as AUX, see table 16, section 5.7. */
143 AC97_Master_Volume_Mono_Mute = 0x06,
144 AC97_Master_Tone_RL = 0x08,
145 AC97_PC_BEEP_Volume_Mute = 0x0A,
146 AC97_Phone_Volume_Mute = 0x0C,
147 AC97_Mic_Volume_Mute = 0x0E,
148 AC97_Line_In_Volume_Mute = 0x10,
149 AC97_CD_Volume_Mute = 0x12,
150 AC97_Video_Volume_Mute = 0x14,
151 AC97_Aux_Volume_Mute = 0x16,
152 AC97_PCM_Out_Volume_Mute = 0x18,
153 AC97_Record_Select = 0x1A,
154 AC97_Record_Gain_Mute = 0x1C,
155 AC97_Record_Gain_Mic_Mute = 0x1E,
156 AC97_General_Purpose = 0x20,
157 AC97_3D_Control = 0x22,
158 AC97_AC_97_RESERVED = 0x24,
159 AC97_Powerdown_Ctrl_Stat = 0x26,
160 AC97_Extended_Audio_ID = 0x28,
161 AC97_Extended_Audio_Ctrl_Stat = 0x2A,
162 AC97_PCM_Front_DAC_Rate = 0x2C,
163 AC97_PCM_Surround_DAC_Rate = 0x2E,
164 AC97_PCM_LFE_DAC_Rate = 0x30,
165 AC97_PCM_LR_ADC_Rate = 0x32,
166 AC97_MIC_ADC_Rate = 0x34,
167 AC97_6Ch_Vol_C_LFE_Mute = 0x36,
168 AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
169 AC97_Vendor_Reserved = 0x58,
170 AC97_AD_Misc = 0x76,
171 AC97_Vendor_ID1 = 0x7c,
172 AC97_Vendor_ID2 = 0x7e
173};
174
175/* Codec models. */
176enum {
177 Codec_STAC9700 = 0, /* SigmaTel STAC9700 */
178 Codec_AD1980, /* Analog Devices AD1980 */
179 Codec_AD1981B /* Analog Devices AD1981B */
180};
181
182/* Analog Devices miscellaneous regiter bits used in AD1980. */
183#define AD_MISC_LOSEL RT_BIT(5) /* Surround (rear) goes to line out outputs. */
184#define AD_MISC_HPSEL RT_BIT(10) /* PCM (front) goes to headphone outputs. */
185
186#define ICHAC97STATE_2_DEVINS(a_pAC97) ((a_pAC97)->pDevInsR3)
187
188enum
189{
190 BUP_SET = RT_BIT(0),
191 BUP_LAST = RT_BIT(1)
192};
193
194/** Emits registers for a specific (Native Audio Bus Master BAR) NABMBAR. */
195#define AC97_NABMBAR_REGS(prefix, off) \
196 enum { \
197 prefix ## _BDBAR = off, \
198 prefix ## _CIV = off + 4, \
199 prefix ## _LVI = off + 5, \
200 prefix ## _SR = off + 6, \
201 prefix ## _PICB = off + 8, \
202 prefix ## _PIV = off + 10, \
203 prefix ## _CR = off + 11 \
204 }
205
206#ifndef VBOX_DEVICE_STRUCT_TESTCASE
207typedef enum
208{
209 PI_INDEX = 0, /** PCM in */
210 PO_INDEX, /** PCM out */
211 MC_INDEX, /** Mic in */
212 LAST_INDEX
213} AC97SOUNDSOURCE;
214
215AC97_NABMBAR_REGS(PI, PI_INDEX * 16);
216AC97_NABMBAR_REGS(PO, PO_INDEX * 16);
217AC97_NABMBAR_REGS(MC, MC_INDEX * 16);
218#endif
219
220enum
221{
222 /** NABMBAR: Global Control Register. */
223 GLOB_CNT = 0x2c,
224 /** NABMBAR Global Status. */
225 GLOB_STA = 0x30,
226 /** Codec Access Semaphore Register. */
227 CAS = 0x34
228};
229
230#define AC97_PORT2IDX(a_idx) ( ((a_idx) >> 4) & 3 )
231
232/*********************************************************************************************************************************
233* Structures and Typedefs *
234*********************************************************************************************************************************/
235
236/**
237 * Buffer Descriptor List Entry (BDLE).
238 */
239typedef struct AC97BDLE
240{
241 uint32_t addr;
242 uint32_t ctl_len;
243} AC97BDLE, *PAC97BDLE;
244
245/**
246 * Bus master register set for an audio stream.
247 */
248typedef struct AC97BMREGS
249{
250 uint32_t bdbar; /** rw 0, Buffer Descriptor List: BAR (Base Address Register). */
251 uint8_t civ; /** ro 0, Current index value. */
252 uint8_t lvi; /** rw 0, Last valid index. */
253 uint16_t sr; /** rw 1, Status register. */
254 uint16_t picb; /** ro 0, Position in current buffer. */
255 uint8_t piv; /** ro 0, Prefetched index value. */
256 uint8_t cr; /** rw 0, Control register. */
257 int bd_valid; /** Whether current BDLE is initialized or not. */
258 AC97BDLE bd; /** Current Buffer Descriptor List Entry (BDLE). */
259} AC97BMREGS, *PAC97BMREGS;
260
261/**
262 * Internal state of an AC97 stream.
263 */
264typedef struct AC97STREAMSTATE
265{
266 /* Nothing yet. */
267} AC97STREAMSTATE, *PAC97STREAMSTATE;
268
269/**
270 * Structure for keeping an AC97 stream state.
271 *
272 * Contains only register values which do *not* change until a
273 * stream reset occurs.
274 */
275typedef struct AC97STREAM
276{
277 /** Stream number (SDn). */
278 uint8_t u8Strm;
279 /** Bus master registers of this stream. */
280 AC97BMREGS Regs;
281 /** Internal state of this stream. */
282 AC97STREAMSTATE State;
283} AC97STREAM, *PAC97STREAM;
284
285typedef struct AC97INPUTSTREAM
286{
287 /** PCM line input stream. */
288 R3PTRTYPE(PPDMAUDIOGSTSTRMIN) pStrmIn;
289 /** Mixer handle for line input stream. */
290 R3PTRTYPE(PAUDMIXSTREAM) phStrmIn;
291} AC97INPUTSTREAM, *PAC97INPUTSTREAM;
292
293typedef struct AC97OUTPUTSTREAM
294{
295 /** PCM output stream. */
296 R3PTRTYPE(PPDMAUDIOGSTSTRMOUT) pStrmOut;
297 /** Mixer handle for output stream. */
298 R3PTRTYPE(PAUDMIXSTREAM) phStrmOut;
299} AC97OUTPUTSTREAM, *PAC97OUTPUTSTREAM;
300
301/**
302 * Struct for maintaining a host backend driver.
303 */
304typedef struct AC97STATE *PAC97STATE;
305typedef struct AC97DRIVER
306{
307 /** Node for storing this driver in our device driver list of AC97STATE. */
308 RTLISTNODER3 Node;
309 /** Pointer to AC97 controller (state). */
310 R3PTRTYPE(PAC97STATE) pAC97State;
311 /** Driver flags. */
312 PDMAUDIODRVFLAGS Flags;
313 uint32_t PaddingFlags;
314 /** LUN # to which this driver has been assigned. */
315 uint8_t uLUN;
316 /** Whether this driver is in an attached state or not. */
317 bool fAttached;
318 uint8_t Padding[4];
319 /** Pointer to attached driver base interface. */
320 R3PTRTYPE(PPDMIBASE) pDrvBase;
321 /** Audio connector interface to the underlying host backend. */
322 R3PTRTYPE(PPDMIAUDIOCONNECTOR) pConnector;
323 /** Stream for line input. */
324 AC97INPUTSTREAM LineIn;
325 /** Stream for mic input. */
326 AC97INPUTSTREAM MicIn;
327 /** Stream for output. */
328 AC97OUTPUTSTREAM Out;
329} AC97DRIVER, *PAC97DRIVER;
330
331typedef struct AC97STATE
332{
333 /** The PCI device state. */
334 PCIDevice PciDev;
335 /** R3 Pointer to the device instance. */
336 PPDMDEVINSR3 pDevInsR3;
337 /** Global Control (Bus Master Control Register) */
338 uint32_t glob_cnt;
339 /** Global Status (Bus Master Control Register) */
340 uint32_t glob_sta;
341 /** Codec Access Semaphore Register (Bus Master Control Register) */
342 uint32_t cas;
343 uint32_t last_samp;
344 uint8_t mixer_data[256];
345 /** Stream state for line-in. */
346 AC97STREAM StrmStLineIn;
347 /** Stream state for microphone-in. */
348 AC97STREAM StrmStMicIn;
349 /** Stream state for output. */
350 AC97STREAM StrmStOut;
351#ifndef VBOX_WITH_AUDIO_CALLBACKS
352 /** The timer for pumping data thru the attached LUN drivers. */
353 PTMTIMERR3 pTimer;
354 /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
355 uint64_t cTimerTicks;
356 /** Timestamp of the last timer callback (ac97Timer).
357 * Used to calculate the time actually elapsed between two timer callbacks. */
358 uint64_t uTimerTS;
359#endif
360#ifdef VBOX_WITH_STATISTICS
361 STAMPROFILE StatTimer;
362 STAMCOUNTER StatBytesRead;
363 STAMCOUNTER StatBytesWritten;
364#endif
365 /** List of associated LUN drivers (AC97DRIVER). */
366 RTLISTANCHOR lstDrv;
367 /** The device' software mixer. */
368 R3PTRTYPE(PAUDIOMIXER) pMixer;
369 /** Audio sink for PCM output. */
370 R3PTRTYPE(PAUDMIXSINK) pSinkOutput;
371 /** Audio sink for line input. */
372 R3PTRTYPE(PAUDMIXSINK) pSinkLineIn;
373 /** Audio sink for microphone input. */
374 R3PTRTYPE(PAUDMIXSINK) pSinkMicIn;
375 uint8_t silence[128];
376 int bup_flag;
377 /** The base interface for LUN\#0. */
378 PDMIBASE IBase;
379 /** Base port of the I/O space region. */
380 RTIOPORT IOPortBase[2];
381 /** Pointer to temporary scratch read/write buffer. */
382 R3PTRTYPE(uint8_t *) pvReadWriteBuf;
383 /** Size of the temporary scratch read/write buffer. */
384 uint32_t cbReadWriteBuf;
385 /** Codec model. */
386 uint32_t uCodecModel;
387} AC97STATE, *PAC97STATE;
388
389#ifdef VBOX_WITH_STATISTICS
390AssertCompileMemberAlignment(AC97STATE, StatTimer, 8);
391#endif
392
393#ifndef VBOX_DEVICE_STRUCT_TESTCASE
394
395DECLINLINE(PAC97STREAM) ichac97GetStreamFromID(PAC97STATE pThis, uint32_t uID);
396static DECLCALLBACK(void) ichac97Reset(PPDMDEVINS pDevIns);
397#ifndef VBOX_WITH_AUDIO_CALLBACKS
398static DECLCALLBACK(void) ichac97Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
399#endif
400static int ichac97TransferAudio(PAC97STATE pThis, AC97SOUNDSOURCE enmSrc, uint32_t cbElapsed);
401
402static void ichac97WarmReset(PAC97STATE pThis)
403{
404 NOREF(pThis);
405}
406
407static void ichac97ColdReset(PAC97STATE pThis)
408{
409 NOREF(pThis);
410}
411
412/** Fetches the buffer descriptor at _CIV. */
413static void ichac97StreamFetchBDLE(PAC97STATE pThis, PAC97STREAM pStrmSt)
414{
415 PPDMDEVINS pDevIns = ICHAC97STATE_2_DEVINS(pThis);
416 PAC97BMREGS pRegs = &pStrmSt->Regs;
417
418 uint32_t u32[2];
419
420 PDMDevHlpPhysRead(pDevIns, pRegs->bdbar + pRegs->civ * 8, &u32[0], sizeof(u32));
421 pRegs->bd_valid = 1;
422#if !defined(RT_ARCH_X86) && !defined(RT_ARCH_AMD64)
423# error Please adapt the code (audio buffers are little endian)!
424#else
425 pRegs->bd.addr = RT_H2LE_U32(u32[0] & ~3);
426 pRegs->bd.ctl_len = RT_H2LE_U32(u32[1]);
427#endif
428 pRegs->picb = pRegs->bd.ctl_len & 0xffff;
429 LogFlowFunc(("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
430 pRegs->civ, pRegs->bd.addr, pRegs->bd.ctl_len >> 16,
431 pRegs->bd.ctl_len & 0xffff, (pRegs->bd.ctl_len & 0xffff) << 1));
432}
433
434/**
435 * Update the BM status register
436 */
437static void ichac97StreamUpdateStatus(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t new_sr)
438{
439 PPDMDEVINS pDevIns = ICHAC97STATE_2_DEVINS(pThis);
440 PAC97BMREGS pRegs = &pStrmSt->Regs;
441
442 bool fSignal = false;
443 bool iIrqLevel;
444
445 uint32_t new_mask = new_sr & SR_INT_MASK;
446 uint32_t old_mask = pRegs->sr & SR_INT_MASK;
447
448 static uint32_t const masks[] = { GS_PIINT, GS_POINT, GS_MINT };
449
450 if (new_mask ^ old_mask)
451 {
452 /** @todo Is IRQ deasserted when only one of status bits is cleared? */
453 if (!new_mask)
454 {
455 fSignal = true;
456 iIrqLevel = 0;
457 }
458 else if ((new_mask & SR_LVBCI) && (pRegs->cr & CR_LVBIE))
459 {
460 fSignal = true;
461 iIrqLevel = 1;
462 }
463 else if ((new_mask & SR_BCIS) && (pRegs->cr & CR_IOCE))
464 {
465 fSignal = true;
466 iIrqLevel = 1;
467 }
468 }
469
470 pRegs->sr = new_sr;
471
472 LogFlowFunc(("IOC%d, LVB%d, sr=%#x, fSignal=%RTbool, iIrqLevel=%d\n",
473 pRegs->sr & SR_BCIS, pRegs->sr & SR_LVBCI, pRegs->sr, fSignal, iIrqLevel));
474
475 if (fSignal)
476 {
477 if (iIrqLevel)
478 pThis->glob_sta |= masks[pStrmSt->u8Strm];
479 else
480 pThis->glob_sta &= ~masks[pStrmSt->u8Strm];
481
482 LogFlowFunc(("set irq level=%d\n", !!iIrqLevel));
483 PDMDevHlpPCISetIrq(pDevIns, 0, !!iIrqLevel);
484 }
485}
486
487static int ichac97StreamSetActive(PAC97STATE pThis, PAC97STREAM pStrmSt, bool fActive)
488{
489 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
490 AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
491
492 LogFlowFunc(("u8Strm=%RU8, fActive=%RTbool\n", pStrmSt->u8Strm, fActive));
493
494 int rc = VINF_SUCCESS;
495
496 PAC97DRIVER pDrv;
497 switch (pStrmSt->u8Strm)
498 {
499 case PI_INDEX:
500 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
501 {
502 int rc2 = pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
503 pDrv->LineIn.pStrmIn, fActive);
504 if (RT_SUCCESS(rc))
505 rc = rc2;
506 }
507 break;
508
509 case PO_INDEX:
510 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
511 {
512 int rc2 = pDrv->pConnector->pfnEnableOut(pDrv->pConnector,
513 pDrv->Out.pStrmOut, fActive);
514 if (RT_SUCCESS(rc))
515 rc = rc2;
516 }
517 break;
518
519 case MC_INDEX:
520 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
521 {
522 int rc2 = pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
523 pDrv->MicIn.pStrmIn, fActive);
524 if (RT_SUCCESS(rc))
525 rc = rc2;
526 }
527 break;
528
529 default:
530 AssertMsgFailed(("Wrong index %RU32\n", pStrmSt->u8Strm));
531 rc = VERR_NOT_SUPPORTED;
532 break;
533 }
534
535 return rc;
536}
537
538static void ichac97StreamResetBMRegs(PAC97STATE pThis, PAC97STREAM pStrmSt)
539{
540 AssertPtrReturnVoid(pThis);
541 AssertPtrReturnVoid(pStrmSt);
542
543 LogFlowFuncEnter();
544
545 PAC97BMREGS pRegs = &pStrmSt->Regs;
546
547 pRegs->bdbar = 0;
548 pRegs->civ = 0;
549 pRegs->lvi = 0;
550
551 ichac97StreamUpdateStatus(pThis, pStrmSt, SR_DCH); /** @todo Do we need to do that? */
552
553 pRegs->picb = 0;
554 pRegs->piv = 0;
555 pRegs->cr = pRegs->cr & CR_DONT_CLEAR_MASK;
556 pRegs->bd_valid = 0;
557
558 int rc = ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
559 AssertRC(rc);
560
561 RT_ZERO(pThis->silence);
562}
563
564static void ichac97MixerSet(PAC97STATE pThis, uint32_t u8Idx, uint16_t v)
565{
566 if (u8Idx + 2 > sizeof(pThis->mixer_data))
567 {
568 AssertMsgFailed(("Index %RU8 out of bounds(%zu)\n", u8Idx, sizeof(pThis->mixer_data)));
569 return;
570 }
571
572 pThis->mixer_data[u8Idx + 0] = RT_LO_U8(v);
573 pThis->mixer_data[u8Idx + 1] = RT_HI_U8(v);
574}
575
576static uint16_t ichac97MixerGet(PAC97STATE pThis, uint32_t u8Idx)
577{
578 uint16_t uVal;
579
580 if (u8Idx + 2 > sizeof(pThis->mixer_data))
581 {
582 AssertMsgFailed(("Index %RU8 out of bounds (%zu)\n", u8Idx, sizeof(pThis->mixer_data)));
583 uVal = UINT16_MAX;
584 }
585 else
586 uVal = RT_MAKE_U16(pThis->mixer_data[u8Idx + 0], pThis->mixer_data[u8Idx + 1]);
587
588 return uVal;
589}
590
591static DECLCALLBACK(void) ichac97CloseIn(PAC97STATE pThis, PDMAUDIORECSOURCE enmRecSource)
592{
593 NOREF(pThis);
594 NOREF(enmRecSource);
595 LogFlowFuncEnter();
596}
597
598static DECLCALLBACK(void) ichac97CloseOut(PAC97STATE pThis)
599{
600 NOREF(pThis);
601 LogFlowFuncEnter();
602}
603
604static int ichac97OpenIn(PAC97STATE pThis,
605 const char *pszName, PDMAUDIORECSOURCE enmRecSource,
606 PPDMAUDIOSTREAMCFG pCfg)
607{
608 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
609 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
610 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
611
612 PAUDMIXSINK pSink;
613 switch (enmRecSource)
614 {
615 case PDMAUDIORECSOURCE_MIC:
616 pSink = pThis->pSinkMicIn;
617 break;
618 case PDMAUDIORECSOURCE_LINE:
619 pSink = pThis->pSinkLineIn;
620 break;
621 default:
622 AssertMsgFailed(("Audio source %ld not supported\n", enmRecSource));
623 return VERR_NOT_SUPPORTED;
624 }
625
626 int rc = VINF_SUCCESS;
627
628 PAC97DRIVER pDrv;
629 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
630 {
631 char *pszDesc;
632 if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s", pDrv->uLUN, pszName) <= 0)
633 {
634 rc = VERR_NO_MEMORY;
635 break;
636 }
637
638 PAC97INPUTSTREAM pStrmIn;
639 if (enmRecSource == PDMAUDIORECSOURCE_MIC) /** @todo Refine this once we have more streams. */
640 pStrmIn = &pDrv->MicIn;
641 else
642 pStrmIn = &pDrv->LineIn;
643
644 int rc2 = pDrv->pConnector->pfnCreateIn(pDrv->pConnector, pszDesc, pCfg, &pStrmIn->pStrmIn);
645 LogFlowFunc(("LUN#%RU8: Created input \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc2));
646 if (RT_SUCCESS(rc2))
647 {
648 AudioMixerRemoveStream(pSink, pStrmIn->phStrmIn);
649 rc2 = AudioMixerAddStreamIn(pSink,
650 pDrv->pConnector, pStrmIn->pStrmIn,
651 0 /* uFlags */, &pStrmIn->phStrmIn);
652 }
653
654 if (RT_SUCCESS(rc))
655 rc = rc2;
656
657 RTStrFree(pszDesc);
658 }
659
660 LogFlowFuncLeaveRC(rc);
661 return rc;
662}
663
664static int ichac97OpenOut(PAC97STATE pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
665{
666 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
667 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
668 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
669
670 int rc = VINF_SUCCESS;
671 char *pszDesc;
672
673 PAC97DRIVER pDrv;
674 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
675 {
676 if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s (%RU32Hz, %RU8 %s)",
677 pDrv->uLUN, pszName, pCfg->uHz, pCfg->cChannels, pCfg->cChannels > 1 ? "Channels" : "Channel") <= 0)
678 {
679 rc = VERR_NO_MEMORY;
680 break;
681 }
682
683 int rc2 = pDrv->pConnector->pfnCreateOut(pDrv->pConnector, pszDesc, pCfg, &pDrv->Out.pStrmOut);
684 LogFlowFunc(("LUN#%RU8: Created output \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc2));
685 if (RT_SUCCESS(rc2))
686 {
687 AudioMixerRemoveStream(pThis->pSinkOutput, pDrv->Out.phStrmOut);
688 rc2 = AudioMixerAddStreamOut(pThis->pSinkOutput,
689 pDrv->pConnector, pDrv->Out.pStrmOut,
690 0 /* uFlags */, &pDrv->Out.phStrmOut);
691 }
692
693 if (RT_SUCCESS(rc))
694 rc = rc2;
695
696 RTStrFree(pszDesc);
697 }
698
699 LogFlowFuncLeaveRC(rc);
700 return rc;
701}
702
703static int ichac97StreamInitEx(PAC97STATE pThis, PAC97STREAM pStrmSt, uint8_t u8Strm, PPDMAUDIOSTREAMCFG pCfg)
704{
705 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
706 AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
707 AssertReturn(u8Strm <= LAST_INDEX, VERR_INVALID_PARAMETER);
708 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
709
710 pStrmSt->u8Strm = u8Strm;
711
712 LogFlowFunc(("u8Strm=%RU8, %RU32Hz, %RU8 %s\n",
713 pStrmSt->u8Strm, pCfg->uHz, pCfg->cChannels, pCfg->cChannels > 1 ? "Channels" : "Channel"));
714
715 int rc;
716 switch (pStrmSt->u8Strm)
717 {
718 case PI_INDEX:
719 rc = ichac97OpenIn(pThis, "ac97.pi", PDMAUDIORECSOURCE_LINE, pCfg);
720 break;
721
722 case MC_INDEX:
723 rc = ichac97OpenIn(pThis, "ac97.mc", PDMAUDIORECSOURCE_MIC, pCfg);
724 break;
725
726 case PO_INDEX:
727 rc = ichac97OpenOut(pThis, "ac97.po", pCfg);
728 break;
729
730 default:
731 rc = VERR_NOT_SUPPORTED;
732 break;
733 }
734
735 LogFlowFuncLeaveRC(rc);
736 return rc;
737}
738
739static int ichac97StreamInit(PAC97STATE pThis, PAC97STREAM pStrmSt, uint8_t u8Strm)
740{
741 int rc = VINF_SUCCESS;
742
743 PDMAUDIOSTREAMCFG streamCfg;
744 RT_ZERO(streamCfg);
745
746 switch (u8Strm)
747 {
748 case PI_INDEX:
749 streamCfg.uHz = ichac97MixerGet(pThis, AC97_PCM_LR_ADC_Rate);
750 streamCfg.enmDir = PDMAUDIODIR_IN;
751 streamCfg.DestSource.Source = PDMAUDIORECSOURCE_LINE;
752 break;
753
754 case MC_INDEX:
755 streamCfg.uHz = ichac97MixerGet(pThis, AC97_MIC_ADC_Rate);
756 streamCfg.enmDir = PDMAUDIODIR_IN;
757 streamCfg.DestSource.Source = PDMAUDIORECSOURCE_MIC;
758 break;
759
760 case PO_INDEX:
761 streamCfg.uHz = ichac97MixerGet(pThis, AC97_PCM_Front_DAC_Rate);
762 streamCfg.enmDir = PDMAUDIODIR_OUT;
763 streamCfg.DestSource.Dest = PDMAUDIOPLAYBACKDEST_FRONT;
764 break;
765
766 default:
767 rc = VERR_NOT_SUPPORTED;
768 break;
769 }
770
771 if (RT_FAILURE(rc))
772 return rc;
773
774 if (streamCfg.uHz)
775 {
776 streamCfg.cChannels = 2;
777 streamCfg.enmFormat = AUD_FMT_S16;
778 streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
779
780 return ichac97StreamInitEx(pThis, pStrmSt, u8Strm, &streamCfg);
781 }
782
783 /* If no frequency is given, disable the stream. */
784 return ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
785}
786
787static int ichac97StreamReInit(PAC97STATE pThis, PAC97STREAM pStrmSt)
788{
789 return ichac97StreamInit(pThis, pStrmSt, pStrmSt->u8Strm);
790}
791
792static void ichac97StreamReset(PAC97STATE pThis, PAC97STREAM pStrmSt)
793{
794 AssertPtrReturnVoid(pThis);
795 AssertPtrReturnVoid(pStrmSt);
796
797 LogFlowFunc(("uStrm=%RU8\n", pStrmSt->u8Strm));
798}
799
800static int ichac97MixerSetVolume(PAC97STATE pThis, int index, PDMAUDIOMIXERCTL mt, uint32_t val)
801{
802 int mute = (val >> MUTE_SHIFT) & 1;
803 uint8_t rvol = val & VOL_MASK;
804 uint8_t lvol = (val >> 8) & VOL_MASK;
805
806 /* For the master volume, 0 corresponds to 0dB gain. But for the other
807 * volume controls, 0 corresponds to +12dB and 8 to 0dB. */
808 if (mt != PDMAUDIOMIXERCTL_VOLUME)
809 {
810 /* NB: Currently there is no gain support, only attenuation. */
811 lvol = lvol < 8 ? 0 : lvol - 8;
812 rvol = rvol < 8 ? 0 : rvol - 8;
813 }
814
815 /* AC'97 has 1.5dB steps; we use 0.375dB steps. */
816 rvol = 255 - rvol * 4;
817 lvol = 255 - lvol * 4;
818
819 LogFunc(("mt=%ld, val=%RX32, mute=%RTbool\n", mt, val, RT_BOOL(mute)));
820
821 int rc;
822
823#ifdef SOFT_VOLUME
824 if (pThis->pMixer) /* Device can be in reset state, so no mixer available. */
825 {
826 PDMAUDIOVOLUME vol = { RT_BOOL(mute), lvol, rvol };
827 switch (mt)
828 {
829 case PDMAUDIOMIXERCTL_VOLUME:
830 rc = AudioMixerSetMasterVolume(pThis->pMixer, &vol);
831 break;
832
833 case PDMAUDIOMIXERCTL_FRONT:
834 rc = AudioMixerSetSinkVolume(pThis->pSinkOutput, &vol);
835 break;
836
837 case PDMAUDIOMIXERCTL_MIC_IN:
838 rc = AudioMixerSetSinkVolume(pThis->pSinkMicIn, &vol);
839 break;
840
841 case PDMAUDIOMIXERCTL_LINE_IN:
842 rc = AudioMixerSetSinkVolume(pThis->pSinkLineIn, &vol);
843 break;
844
845 default:
846 rc = VERR_NOT_SUPPORTED;
847 break;
848 }
849 }
850 else
851 rc = VERR_NOT_SUPPORTED;
852
853 if (RT_FAILURE(rc))
854 return rc;
855#else
856 rc = VINF_SUCCESS;
857#endif /* SOFT_VOLUME */
858
859 rvol = VOL_MASK - ((VOL_MASK * rvol) / 255);
860 lvol = VOL_MASK - ((VOL_MASK * lvol) / 255);
861
862 /*
863 * From AC'97 SoundMax Codec AD1981A: "Because AC '97 defines 6-bit volume registers, to
864 * maintain compatibility whenever the D5 or D13 bits are set to `1,' their respective
865 * lower five volume bits are automatically set to `1' by the Codec logic. On readback,
866 * all lower 5 bits will read ones whenever these bits are set to `1.'"
867 *
868 * Linux ALSA depends on this behavior.
869 */
870 if (val & RT_BIT(5))
871 val |= RT_BIT(4) | RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0);
872 if (val & RT_BIT(13))
873 val |= RT_BIT(12) | RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8);
874
875 ichac97MixerSet(pThis, index, val);
876
877 return rc;
878}
879
880static PDMAUDIORECSOURCE ichac97IndextoRecSource(uint8_t i)
881{
882 switch (i)
883 {
884 case REC_MIC: return PDMAUDIORECSOURCE_MIC;
885 case REC_CD: return PDMAUDIORECSOURCE_CD;
886 case REC_VIDEO: return PDMAUDIORECSOURCE_VIDEO;
887 case REC_AUX: return PDMAUDIORECSOURCE_AUX;
888 case REC_LINE_IN: return PDMAUDIORECSOURCE_LINE;
889 case REC_PHONE: return PDMAUDIORECSOURCE_PHONE;
890 default:
891 break;
892 }
893
894 LogFlowFunc(("Unknown record source %d, using MIC\n", i));
895 return PDMAUDIORECSOURCE_MIC;
896}
897
898static uint8_t ichac97RecSourceToIndex(PDMAUDIORECSOURCE rs)
899{
900 switch (rs)
901 {
902 case PDMAUDIORECSOURCE_MIC: return REC_MIC;
903 case PDMAUDIORECSOURCE_CD: return REC_CD;
904 case PDMAUDIORECSOURCE_VIDEO: return REC_VIDEO;
905 case PDMAUDIORECSOURCE_AUX: return REC_AUX;
906 case PDMAUDIORECSOURCE_LINE: return REC_LINE_IN;
907 case PDMAUDIORECSOURCE_PHONE: return REC_PHONE;
908 default:
909 break;
910 }
911
912 LogFlowFunc(("Unknown audio recording source %d using MIC\n", rs));
913 return REC_MIC;
914}
915
916static void ichac97RecordSelect(PAC97STATE pThis, uint32_t val)
917{
918 uint8_t rs = val & REC_MASK;
919 uint8_t ls = (val >> 8) & REC_MASK;
920 PDMAUDIORECSOURCE ars = ichac97IndextoRecSource(rs);
921 PDMAUDIORECSOURCE als = ichac97IndextoRecSource(ls);
922 //AUD_set_record_source(&als, &ars);
923 rs = ichac97RecSourceToIndex(ars);
924 ls = ichac97RecSourceToIndex(als);
925 ichac97MixerSet(pThis, AC97_Record_Select, rs | (ls << 8));
926}
927
928static int ichac97MixerReset(PAC97STATE pThis)
929{
930 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
931
932 LogFlowFuncEnter();
933
934 RT_ZERO(pThis->mixer_data);
935
936 /* Note: Make sure to reset all registers first before bailing out on error. */
937
938 ichac97MixerSet(pThis, AC97_Reset , 0x0000); /* 6940 */
939 ichac97MixerSet(pThis, AC97_Master_Volume_Mono_Mute , 0x8000);
940 ichac97MixerSet(pThis, AC97_PC_BEEP_Volume_Mute , 0x0000);
941
942 ichac97MixerSet(pThis, AC97_Phone_Volume_Mute , 0x8008);
943 ichac97MixerSet(pThis, AC97_Mic_Volume_Mute , 0x8008);
944 ichac97MixerSet(pThis, AC97_CD_Volume_Mute , 0x8808);
945 ichac97MixerSet(pThis, AC97_Aux_Volume_Mute , 0x8808);
946 ichac97MixerSet(pThis, AC97_Record_Gain_Mic_Mute , 0x8000);
947 ichac97MixerSet(pThis, AC97_General_Purpose , 0x0000);
948 ichac97MixerSet(pThis, AC97_3D_Control , 0x0000);
949 ichac97MixerSet(pThis, AC97_Powerdown_Ctrl_Stat , 0x000f);
950
951 ichac97MixerSet(pThis, AC97_Extended_Audio_ID , 0x0809);
952 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
953 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate , 0xbb80);
954 ichac97MixerSet(pThis, AC97_PCM_Surround_DAC_Rate , 0xbb80);
955 ichac97MixerSet(pThis, AC97_PCM_LFE_DAC_Rate , 0xbb80);
956 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate , 0xbb80);
957 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate , 0xbb80);
958
959 if (pThis->uCodecModel == Codec_AD1980)
960 {
961 /* Analog Devices 1980 (AD1980) */
962 ichac97MixerSet(pThis, AC97_Reset , 0x0010); /* Headphones. */
963 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
964 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5370);
965 ichac97MixerSet(pThis, AC97_Headphone_Volume_Mute , 0x8000);
966 }
967 else if (pThis->uCodecModel == Codec_AD1981B)
968 {
969 /* Analog Devices 1981B (AD1981B) */
970 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x4144);
971 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x5374);
972 }
973 else
974 {
975 /* Sigmatel 9700 (STAC9700) */
976 ichac97MixerSet(pThis, AC97_Vendor_ID1 , 0x8384);
977 ichac97MixerSet(pThis, AC97_Vendor_ID2 , 0x7600); /* 7608 */
978 }
979 ichac97RecordSelect(pThis, 0);
980
981 ichac97MixerSetVolume(pThis, AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME, 0x8000);
982 ichac97MixerSetVolume(pThis, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT, 0x8808);
983 ichac97MixerSetVolume(pThis, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8808);
984
985 return VINF_SUCCESS;
986}
987
988/**
989 * Writes data from the device to the host backends.
990 *
991 * @return IPRT status code.
992 * @param pThis
993 * @param pStrmSt
994 * @param cbMax
995 * @param pcbWritten
996 */
997static int ichac97WriteAudio(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t cbMax, uint32_t *pcbWritten)
998{
999 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1000 AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
1001 AssertReturn(cbMax, VERR_INVALID_PARAMETER);
1002 /* pcbWritten is optional. */
1003
1004 PPDMDEVINS pDevIns = ICHAC97STATE_2_DEVINS(pThis);
1005 PAC97BMREGS pRegs = &pStrmSt->Regs;
1006
1007 uint32_t addr = pRegs->bd.addr;
1008 uint32_t cbWrittenTotal = 0;
1009 uint32_t cbToRead;
1010
1011 uint32_t cbToWrite = RT_MIN((uint32_t)(pRegs->picb << 1), cbMax);
1012 if (!cbToWrite)
1013 {
1014 if (pcbWritten)
1015 *pcbWritten = 0;
1016 return VINF_EOF;
1017 }
1018
1019 int rc = VINF_SUCCESS;
1020
1021 LogFlowFunc(("pReg=%p, cbMax=%RU32, cbToWrite=%RU32\n", pRegs, cbMax, cbToWrite));
1022
1023 while (cbToWrite)
1024 {
1025 cbToRead = RT_MIN(cbToWrite, pThis->cbReadWriteBuf);
1026 PDMDevHlpPhysRead(pDevIns, addr, pThis->pvReadWriteBuf, cbToRead); /** @todo Check rc? */
1027
1028 uint32_t cbWritten;
1029
1030 /* Just multiplex the output to the connected backends.
1031 * No need to utilize the virtual mixer here (yet). */
1032 PAC97DRIVER pDrv;
1033 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
1034 {
1035 int rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
1036 pThis->pvReadWriteBuf, cbToRead, &cbWritten);
1037 LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWritten=%RU32\n", pDrv->uLUN, rc2, cbWritten));
1038 }
1039
1040 LogFlowFunc(("\tcbToRead=%RU32, cbToWrite=%RU32, cbLeft=%RU32\n",
1041 cbToRead, cbToWrite, cbToWrite - cbWrittenTotal));
1042
1043 Assert(cbToWrite >= cbToRead);
1044 cbToWrite -= cbToRead;
1045 addr += cbToRead;
1046 cbWrittenTotal += cbToRead;
1047 }
1048
1049 pRegs->bd.addr = addr;
1050
1051 if (RT_SUCCESS(rc))
1052 {
1053 if (!cbToWrite) /* All data written? */
1054 {
1055 if (cbToRead < 4)
1056 {
1057 AssertMsgFailed(("Unable to save last written sample, cbToRead < 4 (is %RU32)\n", cbToRead));
1058 pThis->last_samp = 0;
1059 }
1060 else
1061 pThis->last_samp = *(uint32_t *)&pThis->pvReadWriteBuf[cbToRead - 4];
1062 }
1063
1064 if (pcbWritten)
1065 *pcbWritten = cbWrittenTotal;
1066 }
1067
1068 LogFlowFunc(("cbWrittenTotal=%RU32, rc=%Rrc\n", cbWrittenTotal, rc));
1069 return rc;
1070}
1071
1072static void ichac97WriteBUP(PAC97STATE pThis, uint32_t cbElapsed)
1073{
1074 if (!(pThis->bup_flag & BUP_SET))
1075 {
1076 if (pThis->bup_flag & BUP_LAST)
1077 {
1078 unsigned int i;
1079 uint32_t *p = (uint32_t*)pThis->silence;
1080 for (i = 0; i < sizeof(pThis->silence) / 4; i++)
1081 *p++ = pThis->last_samp;
1082 }
1083 else
1084 RT_ZERO(pThis->silence);
1085
1086 pThis->bup_flag |= BUP_SET;
1087 }
1088
1089 while (cbElapsed)
1090 {
1091 uint32_t cbToWrite = RT_MIN(cbElapsed, (uint32_t)sizeof(pThis->silence));
1092 uint32_t cbWrittenToStream;
1093 int rc2;
1094
1095 PAC97DRIVER pDrv;
1096 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
1097 {
1098 if (pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut))
1099 {
1100 rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
1101 pThis->silence, cbToWrite, &cbWrittenToStream);
1102 if (RT_SUCCESS(rc2))
1103 {
1104 if (cbWrittenToStream < cbToWrite) /* Lagging behind? */
1105 LogFlowFunc(("\tLUN#%RU8: Warning: Only written %RU32 / %RU32 bytes, expect lags\n",
1106 pDrv->uLUN, cbWrittenToStream, cbToWrite));
1107 }
1108 }
1109 else /* Stream disabled, not fatal. */
1110 {
1111 cbWrittenToStream = 0;
1112 rc2 = VERR_NOT_AVAILABLE;
1113 /* Keep going. */
1114 }
1115 }
1116
1117 /* Always report all data as being written;
1118 * backends who were not able to catch up have to deal with it themselves. */
1119 Assert(cbElapsed >= cbToWrite);
1120 cbElapsed -= cbToWrite;
1121 }
1122}
1123
1124static int ichac97ReadAudio(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t cbMax, uint32_t *pcbRead)
1125{
1126 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1127 AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
1128 AssertReturn(cbMax, VERR_INVALID_PARAMETER);
1129 /* pcbRead is optional. */
1130
1131 PPDMDEVINS pDevIns = ICHAC97STATE_2_DEVINS(pThis);
1132 PAC97BMREGS pRegs = &pStrmSt->Regs;
1133
1134 /* Select audio sink to process. */
1135 AssertMsg(pStrmSt->u8Strm != PO_INDEX, ("Can't read from output\n"));
1136 PAUDMIXSINK pSink = pStrmSt->u8Strm == MC_INDEX ? pThis->pSinkMicIn : pThis->pSinkLineIn;
1137 AssertPtr(pSink);
1138
1139 uint32_t cbRead = 0;
1140
1141 uint32_t cbMixBuf = cbMax;
1142 uint32_t cbToRead = RT_MIN((uint32_t)(pRegs->picb << 1), cbMixBuf);
1143
1144 if (!cbToRead)
1145 {
1146 if (pcbRead)
1147 *pcbRead = 0;
1148 return VINF_EOF;
1149 }
1150
1151 int rc;
1152
1153 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbMixBuf);
1154 if (pvMixBuf)
1155 {
1156 rc = AudioMixerProcessSinkIn(pSink, AUDMIXOP_BLEND, pvMixBuf, cbToRead, &cbRead);
1157 if ( RT_SUCCESS(rc)
1158 && cbRead)
1159 {
1160 PDMDevHlpPCIPhysWrite(pDevIns, pRegs->bd.addr, pvMixBuf, cbRead);
1161 pRegs->bd.addr += cbRead;
1162 }
1163
1164 RTMemFree(pvMixBuf);
1165 }
1166 else
1167 rc = VERR_NO_MEMORY;
1168
1169 if (RT_SUCCESS(rc))
1170 {
1171 if (pcbRead)
1172 *pcbRead = cbRead;
1173 }
1174
1175 return rc;
1176}
1177
1178#ifndef VBOX_WITH_AUDIO_CALLBACKS
1179
1180static DECLCALLBACK(void) ichac97Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1181{
1182 PAC97STATE pThis = (PAC97STATE)pvUser;
1183 Assert(pThis == PDMINS_2_DATA(pDevIns, PAC97STATE));
1184 AssertPtr(pThis);
1185
1186 STAM_PROFILE_START(&pThis->StatTimer, a);
1187
1188 uint32_t cbInMax = 0;
1189 uint32_t cbOutMin = UINT32_MAX;
1190
1191 PAC97DRIVER pDrv;
1192
1193 uint64_t cTicksNow = TMTimerGet(pTimer);
1194 uint64_t cTicksElapsed = cTicksNow - pThis->uTimerTS;
1195 uint64_t cTicksPerSec = TMTimerGetFreq(pTimer);
1196
1197 pThis->uTimerTS = cTicksNow;
1198
1199 /*
1200 * Calculate the mixer's (fixed) sampling rate.
1201 */
1202 AssertPtr(pThis->pMixer);
1203
1204 PDMAUDIOSTREAMCFG mixerStrmCfg;
1205 int rc = AudioMixerGetDeviceFormat(pThis->pMixer, &mixerStrmCfg);
1206 AssertRC(rc);
1207
1208 PDMPCMPROPS mixerStrmProps;
1209 rc = DrvAudioStreamCfgToProps(&mixerStrmCfg, &mixerStrmProps);
1210 AssertRC(rc);
1211
1212 uint32_t cMixerSamplesMin = (int)((2 * cTicksElapsed * mixerStrmCfg.uHz + cTicksPerSec) / cTicksPerSec / 2);
1213 uint32_t cbMixerSamplesMin = cMixerSamplesMin << mixerStrmProps.cShift;
1214
1215 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
1216 {
1217 uint32_t cbIn = 0;
1218 uint32_t cbOut = 0;
1219
1220 rc = pDrv->pConnector->pfnQueryStatus(pDrv->pConnector,
1221 &cbIn, &cbOut, NULL /* cSamplesLive */);
1222 if (RT_SUCCESS(rc))
1223 rc = pDrv->pConnector->pfnPlayOut(pDrv->pConnector, NULL /* cSamplesPlayed */);
1224
1225#ifdef DEBUG_TIMER
1226 LogFlowFunc(("LUN#%RU8: rc=%Rrc, cbIn=%RU32, cbOut=%RU32\n", pDrv->uLUN, rc, cbIn, cbOut));
1227#endif
1228 /* If we there was an error handling (available) output or there simply is no output available,
1229 * then calculate the minimum data rate which must be processed by the device emulation in order
1230 * to function correctly.
1231 *
1232 * This is not the optimal solution, but as we have to deal with this on a timer-based approach
1233 * (until we have the audio callbacks) we need to have device' DMA engines running. */
1234 if (!pDrv->pConnector->pfnIsValidOut(pDrv->pConnector, pDrv->Out.pStrmOut))
1235 {
1236 /* Use the mixer's (fixed) sampling rate. */
1237 cbOut = RT_MAX(cbOut, cbMixerSamplesMin);
1238 continue;
1239 }
1240
1241 const bool fIsActiveOut = pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut);
1242 if ( RT_FAILURE(rc)
1243 || !fIsActiveOut)
1244 {
1245 uint32_t cSamplesMin = (int)((2 * cTicksElapsed * pDrv->Out.pStrmOut->Props.uHz + cTicksPerSec) / cTicksPerSec / 2);
1246 uint32_t cbSamplesMin = AUDIOMIXBUF_S2B(&pDrv->Out.pStrmOut->MixBuf, cSamplesMin);
1247
1248#ifdef DEBUG_TIMER
1249 LogFlowFunc(("\trc=%Rrc, cSamplesMin=%RU32, cbSamplesMin=%RU32\n", rc, cSamplesMin, cbSamplesMin));
1250#endif
1251 cbOut = RT_MAX(cbOut, cbSamplesMin);
1252 }
1253
1254 cbOutMin = RT_MIN(cbOutMin, cbOut);
1255 cbInMax = RT_MAX(cbInMax, cbIn);
1256 }
1257
1258#ifdef DEBUG_TIMER
1259 LogFlowFunc(("cbInMax=%RU32, cbOutMin=%RU32\n", cbInMax, cbOutMin));
1260#endif
1261
1262 if (cbOutMin == UINT32_MAX)
1263 cbOutMin = 0;
1264
1265 /*
1266 * Playback.
1267 */
1268 if (cbOutMin)
1269 {
1270 Assert(cbOutMin != UINT32_MAX);
1271 ichac97TransferAudio(pThis, PO_INDEX, cbOutMin); /** @todo Add rc! */
1272 }
1273
1274 /*
1275 * Recording.
1276 */
1277 if (cbInMax)
1278 ichac97TransferAudio(pThis, PI_INDEX, cbInMax); /** @todo Add rc! */
1279
1280 /* Kick the timer again. */
1281 uint64_t cTicks = pThis->cTimerTicks;
1282 /** @todo adjust cTicks down by now much cbOutMin represents. */
1283 TMTimerSet(pThis->pTimer, cTicksNow + cTicks);
1284
1285 STAM_PROFILE_STOP(&pThis->StatTimer, a);
1286}
1287
1288#endif
1289
1290static int ichac97TransferAudio(PAC97STATE pThis, AC97SOUNDSOURCE enmSrc, uint32_t cbElapsed)
1291{
1292 LogFlowFunc(("pThis=%p, enmSrc=%RU32, cbElapsed=%RU32\n", pThis, enmSrc, cbElapsed));
1293
1294 PAC97STREAM pStrmSt;
1295 switch (enmSrc)
1296 {
1297 case PI_INDEX: pStrmSt = &pThis->StrmStLineIn; break;
1298 case MC_INDEX: pStrmSt = &pThis->StrmStMicIn; break;
1299 case PO_INDEX: pStrmSt = &pThis->StrmStOut; break;
1300 default:
1301 {
1302 AssertMsgFailed(("Unknown source index %ld\n", enmSrc));
1303 return VERR_NOT_SUPPORTED;
1304 }
1305 }
1306
1307 PAC97BMREGS pRegs = &pStrmSt->Regs;
1308
1309 if (pRegs->sr & SR_DCH) /* Controller halted? */
1310 {
1311 if (pRegs->cr & CR_RPBM)
1312 {
1313 switch (enmSrc)
1314 {
1315 case PO_INDEX:
1316 ichac97WriteBUP(pThis, cbElapsed);
1317 break;
1318
1319 default:
1320 break;
1321 }
1322 }
1323
1324 return VINF_SUCCESS;
1325 }
1326
1327 int rc = VINF_SUCCESS;
1328 uint32_t cbWrittenTotal = 0;
1329
1330 while (cbElapsed >> 1)
1331 {
1332 if (!pRegs->bd_valid)
1333 {
1334 LogFlowFunc(("Invalid buffer descriptor, fetching next one ...\n"));
1335 ichac97StreamFetchBDLE(pThis, pStrmSt);
1336 }
1337
1338 if (!pRegs->picb) /* Got a new buffer descriptor, that is, the position is 0? */
1339 {
1340 LogFlowFunc(("Fresh buffer descriptor %RU8 is empty, addr=%#x, len=%#x, skipping\n",
1341 pRegs->civ, pRegs->bd.addr, pRegs->bd.ctl_len));
1342 if (pRegs->civ == pRegs->lvi)
1343 {
1344 pRegs->sr |= SR_DCH; /* CELV? */
1345 pThis->bup_flag = 0;
1346
1347 rc = VINF_EOF;
1348 break;
1349 }
1350
1351 pRegs->sr &= ~SR_CELV;
1352 pRegs->civ = pRegs->piv;
1353 pRegs->piv = (pRegs->piv + 1) % 32;
1354
1355 ichac97StreamFetchBDLE(pThis, pStrmSt);
1356 continue;
1357 }
1358
1359 uint32_t cbTransferred;
1360 switch (enmSrc)
1361 {
1362 case PO_INDEX:
1363 {
1364 rc = ichac97WriteAudio(pThis, pStrmSt, cbElapsed, &cbTransferred);
1365 if ( RT_SUCCESS(rc)
1366 && cbTransferred)
1367 {
1368 cbWrittenTotal += cbTransferred;
1369 Assert(cbElapsed >= cbTransferred);
1370 cbElapsed -= cbTransferred;
1371 Assert((cbTransferred & 1) == 0); /* Else the following shift won't work */
1372 pRegs->picb -= (cbTransferred >> 1);
1373 }
1374 break;
1375 }
1376
1377 case PI_INDEX:
1378 case MC_INDEX:
1379 {
1380 rc = ichac97ReadAudio(pThis, pStrmSt, cbElapsed, &cbTransferred);
1381 if ( RT_SUCCESS(rc)
1382 && cbTransferred)
1383 {
1384 Assert(cbElapsed >= cbTransferred);
1385 cbElapsed -= cbTransferred;
1386 Assert((cbTransferred & 1) == 0); /* Else the following shift won't work */
1387 pRegs->picb -= (cbTransferred >> 1);
1388 }
1389 break;
1390 }
1391
1392 default:
1393 AssertMsgFailed(("Source %RU32 not supported\n", enmSrc));
1394 rc = VERR_NOT_SUPPORTED;
1395 break;
1396 }
1397
1398 LogFlowFunc(("pReg->picb=%#x, cbWrittenTotal=%RU32\n", pRegs->picb, cbWrittenTotal));
1399
1400 if (!pRegs->picb)
1401 {
1402 uint32_t new_sr = pRegs->sr & ~SR_CELV;
1403
1404 if (pRegs->bd.ctl_len & BD_IOC)
1405 {
1406 new_sr |= SR_BCIS;
1407 }
1408
1409 if (pRegs->civ == pRegs->lvi)
1410 {
1411 LogFlowFunc(("Underrun civ (%RU8) == lvi (%RU8)\n", pRegs->civ, pRegs->lvi));
1412 new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
1413 pThis->bup_flag = (pRegs->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
1414
1415 rc = VINF_EOF;
1416 }
1417 else
1418 {
1419 pRegs->civ = pRegs->piv;
1420 pRegs->piv = (pRegs->piv + 1) % 32;
1421 ichac97StreamFetchBDLE(pThis, pStrmSt);
1422 }
1423
1424 ichac97StreamUpdateStatus(pThis, pStrmSt, new_sr);
1425 }
1426
1427 if ( RT_FAILURE(rc)
1428 || rc == VINF_EOF) /* All data processed? */
1429 {
1430 break;
1431 }
1432 }
1433
1434 LogFlowFuncLeaveRC(rc);
1435 return rc;
1436}
1437
1438/**
1439 * @callback_method_impl{FNIOMIOPORTIN}
1440 */
1441static DECLCALLBACK(int) ichac97IOPortNABMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
1442 uint32_t *pu32Val, unsigned cbVal)
1443{
1444 PAC97STATE pThis = (PAC97STATE)pvUser;
1445
1446 /* Get the index of the NABMBAR port. */
1447 const uint32_t uPortIdx = Port - pThis->IOPortBase[1];
1448
1449 PAC97STREAM pStrmSt = ichac97GetStreamFromID(pThis, AC97_PORT2IDX(uPortIdx));
1450 PAC97BMREGS pRegs = pStrmSt ? &pStrmSt->Regs : NULL;
1451
1452 switch (cbVal)
1453 {
1454 case 1:
1455 {
1456 switch (uPortIdx)
1457 {
1458 case CAS:
1459 /* Codec Access Semaphore Register */
1460 LogFlowFunc(("CAS %d\n", pThis->cas));
1461 *pu32Val = pThis->cas;
1462 pThis->cas = 1;
1463 break;
1464 case PI_CIV:
1465 case PO_CIV:
1466 case MC_CIV:
1467 /* Current Index Value Register */
1468 *pu32Val = pRegs->civ;
1469 LogFlowFunc(("CIV[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1470 break;
1471 case PI_LVI:
1472 case PO_LVI:
1473 case MC_LVI:
1474 /* Last Valid Index Register */
1475 *pu32Val = pRegs->lvi;
1476 LogFlowFunc(("LVI[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1477 break;
1478 case PI_PIV:
1479 case PO_PIV:
1480 case MC_PIV:
1481 /* Prefetched Index Value Register */
1482 *pu32Val = pRegs->piv;
1483 LogFlowFunc(("PIV[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1484 break;
1485 case PI_CR:
1486 case PO_CR:
1487 case MC_CR:
1488 /* Control Register */
1489 *pu32Val = pRegs->cr;
1490 LogFlowFunc(("CR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1491 break;
1492 case PI_SR:
1493 case PO_SR:
1494 case MC_SR:
1495 /* Status Register (lower part) */
1496 *pu32Val = pRegs->sr & 0xff; /** @todo r=andy Use RT_LO_U8. */
1497 LogFlowFunc(("SRb[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1498 break;
1499 default:
1500 *pu32Val = UINT32_MAX;
1501 LogFlowFunc(("U nabm readb %#x -> %#x\n", Port, *pu32Val));
1502 break;
1503 }
1504 break;
1505 }
1506
1507 case 2:
1508 {
1509 switch (uPortIdx)
1510 {
1511 case PI_SR:
1512 case PO_SR:
1513 case MC_SR:
1514 /* Status Register */
1515 *pu32Val = pRegs->sr;
1516 LogFlowFunc(("SR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1517 break;
1518 case PI_PICB:
1519 case PO_PICB:
1520 case MC_PICB:
1521 /* Position in Current Buffer Register */
1522 *pu32Val = pRegs->picb;
1523 LogFlowFunc(("PICB[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1524 break;
1525 default:
1526 *pu32Val = UINT32_MAX;
1527 LogFlowFunc(("U nabm readw %#x -> %#x\n", Port, *pu32Val));
1528 break;
1529 }
1530 break;
1531 }
1532
1533 case 4:
1534 {
1535 switch (uPortIdx)
1536 {
1537 case PI_BDBAR:
1538 case PO_BDBAR:
1539 case MC_BDBAR:
1540 /* Buffer Descriptor Base Address Register */
1541 *pu32Val = pRegs->bdbar;
1542 LogFlowFunc(("BMADDR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
1543 break;
1544 case PI_CIV:
1545 case PO_CIV:
1546 case MC_CIV:
1547 /* 32-bit access: Current Index Value Register +
1548 * Last Valid Index Register +
1549 * Status Register */
1550 *pu32Val = pRegs->civ | (pRegs->lvi << 8) | (pRegs->sr << 16); /** @todo r=andy Use RT_MAKE_U32_FROM_U8. */
1551 LogFlowFunc(("CIV LVI SR[%d] -> %#x, %#x, %#x\n",
1552 AC97_PORT2IDX(uPortIdx), pRegs->civ, pRegs->lvi, pRegs->sr));
1553 break;
1554 case PI_PICB:
1555 case PO_PICB:
1556 case MC_PICB:
1557 /* 32-bit access: Position in Current Buffer Register +
1558 * Prefetched Index Value Register +
1559 * Control Register */
1560 *pu32Val = pRegs->picb | (pRegs->piv << 16) | (pRegs->cr << 24); /** @todo r=andy Use RT_MAKE_U32_FROM_U8. */
1561 LogFlowFunc(("PICB PIV CR[%d] -> %#x %#x %#x %#x\n",
1562 AC97_PORT2IDX(uPortIdx), *pu32Val, pRegs->picb, pRegs->piv, pRegs->cr));
1563 break;
1564 case GLOB_CNT:
1565 /* Global Control */
1566 *pu32Val = pThis->glob_cnt;
1567 LogFlowFunc(("glob_cnt -> %#x\n", *pu32Val));
1568 break;
1569 case GLOB_STA:
1570 /* Global Status */
1571 *pu32Val = pThis->glob_sta | GS_S0CR;
1572 LogFlowFunc(("glob_sta -> %#x\n", *pu32Val));
1573 break;
1574 default:
1575 *pu32Val = UINT32_MAX;
1576 LogFlowFunc(("U nabm readl %#x -> %#x\n", Port, *pu32Val));
1577 break;
1578 }
1579 break;
1580 }
1581
1582 default:
1583 return VERR_IOM_IOPORT_UNUSED;
1584 }
1585 return VINF_SUCCESS;
1586}
1587
1588/**
1589 * @callback_method_impl{FNIOMIOPORTOUT}
1590 */
1591static DECLCALLBACK(int) ichac97IOPortNABMWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
1592 uint32_t u32Val, unsigned cbVal)
1593{
1594 PAC97STATE pThis = (PAC97STATE)pvUser;
1595
1596 /* Get the index of the NABMBAR register. */
1597 const uint32_t uPortIdx = Port - pThis->IOPortBase[1];
1598
1599 PAC97STREAM pStrmSt = ichac97GetStreamFromID(pThis, AC97_PORT2IDX(uPortIdx));
1600 PAC97BMREGS pRegs = pStrmSt ? &pStrmSt->Regs : NULL;
1601
1602 switch (cbVal)
1603 {
1604 case 1:
1605 {
1606 switch (uPortIdx)
1607 {
1608 case PI_LVI:
1609 case PO_LVI:
1610 case MC_LVI:
1611 /* Last Valid Index */
1612 if ((pRegs->cr & CR_RPBM) && (pRegs->sr & SR_DCH))
1613 {
1614 pRegs->sr &= ~(SR_DCH | SR_CELV);
1615 pRegs->civ = pRegs->piv;
1616 pRegs->piv = (pRegs->piv + 1) % 32;
1617
1618 ichac97StreamFetchBDLE(pThis, pStrmSt);
1619 }
1620 pRegs->lvi = u32Val % 32;
1621 LogFlowFunc(("LVI[%d] <- %#x\n", AC97_PORT2IDX(uPortIdx), u32Val));
1622 break;
1623 case PI_CR:
1624 case PO_CR:
1625 case MC_CR:
1626 {
1627 /* Control Register */
1628 if (u32Val & CR_RR) /* Busmaster reset */
1629 {
1630 ichac97StreamResetBMRegs(pThis, pStrmSt);
1631 }
1632 else
1633 {
1634 pRegs->cr = u32Val & CR_VALID_MASK;
1635 if (!(pRegs->cr & CR_RPBM))
1636 {
1637 ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
1638 pRegs->sr |= SR_DCH;
1639 }
1640 else
1641 {
1642 pRegs->civ = pRegs->piv;
1643 pRegs->piv = (pRegs->piv + 1) % 32;
1644
1645 ichac97StreamFetchBDLE(pThis, pStrmSt);
1646
1647 pRegs->sr &= ~SR_DCH;
1648 ichac97StreamSetActive(pThis, pStrmSt, true /* fActive */);
1649 }
1650 }
1651 LogFlowFunc(("CR[%d] <- %#x (cr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->cr));
1652 break;
1653 }
1654 case PI_SR:
1655 case PO_SR:
1656 case MC_SR:
1657 /* Status Register */
1658 pRegs->sr |= u32Val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
1659 ichac97StreamUpdateStatus(pThis, pStrmSt, pRegs->sr & ~(u32Val & SR_WCLEAR_MASK));
1660 LogFlowFunc(("SR[%d] <- %#x (sr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->sr));
1661 break;
1662 default:
1663 LogFlowFunc(("U nabm writeb %#x <- %#x\n", Port, u32Val));
1664 break;
1665 }
1666 break;
1667 }
1668
1669 case 2:
1670 {
1671 switch (uPortIdx)
1672 {
1673 case PI_SR:
1674 case PO_SR:
1675 case MC_SR:
1676 /* Status Register */
1677 pRegs->sr |= u32Val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
1678 ichac97StreamUpdateStatus(pThis, pStrmSt, pRegs->sr & ~(u32Val & SR_WCLEAR_MASK));
1679 LogFlowFunc(("SR[%d] <- %#x (sr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->sr));
1680 break;
1681 default:
1682 LogFlowFunc(("U nabm writew %#x <- %#x\n", Port, u32Val));
1683 break;
1684 }
1685 break;
1686 }
1687
1688 case 4:
1689 {
1690 switch (uPortIdx)
1691 {
1692 case PI_BDBAR:
1693 case PO_BDBAR:
1694 case MC_BDBAR:
1695 /* Buffer Descriptor list Base Address Register */
1696 pRegs->bdbar = u32Val & ~3;
1697 LogFlowFunc(("BDBAR[%d] <- %#x (bdbar %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->bdbar));
1698 break;
1699 case GLOB_CNT:
1700 /* Global Control */
1701 if (u32Val & GC_WR)
1702 ichac97WarmReset(pThis);
1703 if (u32Val & GC_CR)
1704 ichac97ColdReset(pThis);
1705 if (!(u32Val & (GC_WR | GC_CR)))
1706 pThis->glob_cnt = u32Val & GC_VALID_MASK;
1707 LogFlowFunc(("glob_cnt <- %#x (glob_cnt %#x)\n", u32Val, pThis->glob_cnt));
1708 break;
1709 case GLOB_STA:
1710 /* Global Status */
1711 pThis->glob_sta &= ~(u32Val & GS_WCLEAR_MASK);
1712 pThis->glob_sta |= (u32Val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
1713 LogFlowFunc(("glob_sta <- %#x (glob_sta %#x)\n", u32Val, pThis->glob_sta));
1714 break;
1715 default:
1716 LogFlowFunc(("U nabm writel %#x <- %#x\n", Port, u32Val));
1717 break;
1718 }
1719 break;
1720 }
1721
1722 default:
1723 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cbVal, u32Val));
1724 break;
1725 }
1726 return VINF_SUCCESS;
1727}
1728
1729/**
1730 * @callback_method_impl{FNIOMIOPORTIN}
1731 */
1732static DECLCALLBACK(int) ichac97IOPortNAMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32Val, unsigned cbVal)
1733{
1734 PAC97STATE pThis = (PAC97STATE)pvUser;
1735
1736 switch (cbVal)
1737 {
1738 case 1:
1739 {
1740 LogFlowFunc(("U nam readb %#x\n", Port));
1741 pThis->cas = 0;
1742 *pu32Val = UINT32_MAX;
1743 break;
1744 }
1745
1746 case 2:
1747 {
1748 uint32_t index = Port - pThis->IOPortBase[0];
1749 *pu32Val = UINT32_MAX;
1750 pThis->cas = 0;
1751 switch (index)
1752 {
1753 default:
1754 *pu32Val = ichac97MixerGet(pThis, index);
1755 LogFlowFunc(("nam readw %#x -> %#x\n", Port, *pu32Val));
1756 break;
1757 }
1758 break;
1759 }
1760
1761 case 4:
1762 {
1763 LogFlowFunc(("U nam readl %#x\n", Port));
1764 pThis->cas = 0;
1765 *pu32Val = UINT32_MAX;
1766 break;
1767 }
1768
1769 default:
1770 return VERR_IOM_IOPORT_UNUSED;
1771 }
1772 return VINF_SUCCESS;
1773}
1774
1775/**
1776 * @callback_method_impl{FNIOMIOPORTOUT}
1777 */
1778static DECLCALLBACK(int) ichac97IOPortNAMWrite(PPDMDEVINS pDevIns,
1779 void *pvUser, RTIOPORT Port, uint32_t u32Val, unsigned cbVal)
1780{
1781 PAC97STATE pThis = (PAC97STATE)pvUser;
1782
1783 switch (cbVal)
1784 {
1785 case 1:
1786 {
1787 LogFlowFunc(("U nam writeb %#x <- %#x\n", Port, u32Val));
1788 pThis->cas = 0;
1789 break;
1790 }
1791
1792 case 2:
1793 {
1794 uint32_t index = Port - pThis->IOPortBase[0];
1795 pThis->cas = 0;
1796 switch (index)
1797 {
1798 case AC97_Reset:
1799 ichac97Reset(pThis->CTX_SUFF(pDevIns));
1800 break;
1801 case AC97_Powerdown_Ctrl_Stat:
1802 u32Val &= ~0xf;
1803 u32Val |= ichac97MixerGet(pThis, index) & 0xf;
1804 ichac97MixerSet(pThis, index, u32Val);
1805 break;
1806 case AC97_Master_Volume_Mute:
1807 if (pThis->uCodecModel == Codec_AD1980)
1808 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_LOSEL)
1809 break; /* Register controls surround (rear), do nothing. */
1810 ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_VOLUME, u32Val);
1811 break;
1812 case AC97_Headphone_Volume_Mute:
1813 if (pThis->uCodecModel == Codec_AD1980)
1814 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_HPSEL)
1815 /* Register controls PCM (front) outputs. */
1816 ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_VOLUME, u32Val);
1817 break;
1818 case AC97_PCM_Out_Volume_Mute:
1819 ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_FRONT, u32Val);
1820 break;
1821 case AC97_Line_In_Volume_Mute:
1822 ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_LINE_IN, u32Val);
1823 break;
1824 case AC97_Record_Select:
1825 ichac97RecordSelect(pThis, u32Val);
1826 break;
1827 case AC97_Vendor_ID1:
1828 case AC97_Vendor_ID2:
1829 LogFlowFunc(("Attempt to write vendor ID to %#x\n", u32Val));
1830 break;
1831 case AC97_Extended_Audio_ID:
1832 LogFlowFunc(("Attempt to write extended audio ID to %#x\n", u32Val));
1833 break;
1834 case AC97_Extended_Audio_Ctrl_Stat:
1835 if (!(u32Val & EACS_VRA))
1836 {
1837 ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate, 48000);
1838 ichac97StreamReInit(pThis, &pThis->StrmStOut);
1839
1840 ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate, 48000);
1841 ichac97StreamReInit(pThis, &pThis->StrmStLineIn);
1842 }
1843 if (!(u32Val & EACS_VRM))
1844 {
1845 ichac97MixerSet(pThis, AC97_MIC_ADC_Rate, 48000);
1846 ichac97StreamReInit(pThis, &pThis->StrmStMicIn);
1847 }
1848 LogFlowFunc(("Setting extended audio control to %#x\n", u32Val));
1849 ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, u32Val);
1850 break;
1851 case AC97_PCM_Front_DAC_Rate:
1852 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA)
1853 {
1854 ichac97MixerSet(pThis, index, u32Val);
1855 LogFlowFunc(("Set front DAC rate to %RU32\n", u32Val));
1856 ichac97StreamReInit(pThis, &pThis->StrmStOut);
1857 }
1858 else
1859 LogFlowFunc(("Attempt to set front DAC rate to %RU32, but VRA is not set\n", u32Val));
1860 break;
1861 case AC97_MIC_ADC_Rate:
1862 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM)
1863 {
1864 ichac97MixerSet(pThis, index, u32Val);
1865 LogFlowFunc(("Set MIC ADC rate to %RU32\n", u32Val));
1866 ichac97StreamReInit(pThis, &pThis->StrmStMicIn);
1867 }
1868 else
1869 LogFlowFunc(("Attempt to set MIC ADC rate to %RU32, but VRM is not set\n", u32Val));
1870 break;
1871 case AC97_PCM_LR_ADC_Rate:
1872 if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA)
1873 {
1874 ichac97MixerSet(pThis, index, u32Val);
1875 LogFlowFunc(("Set front LR ADC rate to %RU32\n", u32Val));
1876 ichac97StreamReInit(pThis, &pThis->StrmStLineIn);
1877 }
1878 else
1879 LogFlowFunc(("Attempt to set LR ADC rate to %RU32, but VRA is not set\n", u32Val));
1880 break;
1881 default:
1882 LogFlowFunc(("U nam writew %#x <- %#x\n", Port, u32Val));
1883 ichac97MixerSet(pThis, index, u32Val);
1884 break;
1885 }
1886 break;
1887 }
1888
1889 case 4:
1890 {
1891 LogFlowFunc(("U nam writel %#x <- %#x\n", Port, u32Val));
1892 pThis->cas = 0;
1893 break;
1894 }
1895
1896 default:
1897 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cbVal, u32Val));
1898 break;
1899 }
1900
1901 return VINF_SUCCESS;
1902}
1903
1904
1905/**
1906 * @callback_method_impl{FNPCIIOREGIONMAP}
1907 */
1908static DECLCALLBACK(int) ichac97IOPortMap(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb,
1909 PCIADDRESSSPACE enmType)
1910{
1911 PPDMDEVINS pDevIns = pPciDev->pDevIns;
1912 PAC97STATE pThis = RT_FROM_MEMBER(pPciDev, AC97STATE, PciDev);
1913 RTIOPORT Port = (RTIOPORT)GCPhysAddress;
1914
1915 Assert(enmType == PCI_ADDRESS_SPACE_IO);
1916 Assert(cb >= 0x20);
1917
1918 if (iRegion < 0 || iRegion > 1) /* We support 2 regions max. at the moment. */
1919 return VERR_INVALID_PARAMETER;
1920
1921 int rc;
1922 if (iRegion == 0)
1923 rc = PDMDevHlpIOPortRegister(pDevIns, Port, 256, pThis,
1924 ichac97IOPortNAMWrite, ichac97IOPortNAMRead,
1925 NULL, NULL, "ICHAC97 NAM");
1926 else
1927 rc = PDMDevHlpIOPortRegister(pDevIns, Port, 64, pThis,
1928 ichac97IOPortNABMWrite, ichac97IOPortNABMRead,
1929 NULL, NULL, "ICHAC97 NABM");
1930 if (RT_FAILURE(rc))
1931 return rc;
1932
1933 pThis->IOPortBase[iRegion] = Port;
1934 return VINF_SUCCESS;
1935}
1936
1937DECLINLINE(PAC97STREAM) ichac97GetStreamFromID(PAC97STATE pThis, uint32_t uID)
1938{
1939 switch (uID)
1940 {
1941 case PI_INDEX: return &pThis->StrmStLineIn;
1942 case MC_INDEX: return &pThis->StrmStMicIn;
1943 case PO_INDEX: return &pThis->StrmStOut;
1944 default: break;
1945 }
1946
1947 return NULL;
1948}
1949
1950#ifdef IN_RING3
1951static int ichac97SaveStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStrmSt)
1952{
1953 PAC97BMREGS pRegs = &pStrmSt->Regs;
1954
1955 SSMR3PutU32(pSSM, pRegs->bdbar);
1956 SSMR3PutU8( pSSM, pRegs->civ);
1957 SSMR3PutU8( pSSM, pRegs->lvi);
1958 SSMR3PutU16(pSSM, pRegs->sr);
1959 SSMR3PutU16(pSSM, pRegs->picb);
1960 SSMR3PutU8( pSSM, pRegs->piv);
1961 SSMR3PutU8( pSSM, pRegs->cr);
1962 SSMR3PutS32(pSSM, pRegs->bd_valid);
1963 SSMR3PutU32(pSSM, pRegs->bd.addr);
1964 SSMR3PutU32(pSSM, pRegs->bd.ctl_len);
1965
1966 return VINF_SUCCESS;
1967}
1968
1969/**
1970 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1971 */
1972static DECLCALLBACK(int) ichac97SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1973{
1974 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
1975
1976 SSMR3PutU32(pSSM, pThis->glob_cnt);
1977 SSMR3PutU32(pSSM, pThis->glob_sta);
1978 SSMR3PutU32(pSSM, pThis->cas);
1979
1980 /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
1981 /* Note: The order the streams are saved here is critical, so don't touch. */
1982 int rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStLineIn);
1983 AssertRC(rc2);
1984 rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStOut);
1985 AssertRC(rc2);
1986 rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStMicIn);
1987 AssertRC(rc2);
1988
1989 SSMR3PutMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
1990
1991 uint8_t active[LAST_INDEX];
1992
1993 PAC97DRIVER pDrv;
1994 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
1995 {
1996 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
1997 AssertPtr(pCon);
1998 active[PI_INDEX] = pCon->pfnIsActiveIn (pCon, pDrv->LineIn.pStrmIn) ? 1 : 0;
1999 active[PO_INDEX] = pCon->pfnIsActiveOut(pCon, pDrv->Out.pStrmOut) ? 1 : 0;
2000 active[MC_INDEX] = pCon->pfnIsActiveIn (pCon, pDrv->MicIn.pStrmIn) ? 1 : 0;
2001 }
2002
2003 SSMR3PutMem(pSSM, active, sizeof(active));
2004
2005 return VINF_SUCCESS;
2006}
2007
2008static int ichac97LoadStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStrmSt)
2009{
2010 PAC97BMREGS pRegs = &pStrmSt->Regs;
2011
2012 SSMR3GetU32(pSSM, &pRegs->bdbar);
2013 SSMR3GetU8( pSSM, &pRegs->civ);
2014 SSMR3GetU8( pSSM, &pRegs->lvi);
2015 SSMR3GetU16(pSSM, &pRegs->sr);
2016 SSMR3GetU16(pSSM, &pRegs->picb);
2017 SSMR3GetU8( pSSM, &pRegs->piv);
2018 SSMR3GetU8( pSSM, &pRegs->cr);
2019 SSMR3GetS32(pSSM, &pRegs->bd_valid);
2020 SSMR3GetU32(pSSM, &pRegs->bd.addr);
2021 SSMR3GetU32(pSSM, &pRegs->bd.ctl_len);
2022
2023 return VINF_SUCCESS;
2024}
2025
2026/**
2027 * @callback_method_impl{FNSSMDEVLOADEXEC}
2028 */
2029static DECLCALLBACK(int) ichac97LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2030{
2031 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
2032
2033 AssertMsgReturn (uVersion == AC97_SSM_VERSION, ("%RU32\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2034 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
2035
2036 SSMR3GetU32(pSSM, &pThis->glob_cnt);
2037 SSMR3GetU32(pSSM, &pThis->glob_sta);
2038 SSMR3GetU32(pSSM, &pThis->cas);
2039
2040 /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
2041 /* Note: The order the streams are loaded here is critical, so don't touch. */
2042 int rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStLineIn);
2043 AssertRC(rc2);
2044 rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStOut);
2045 AssertRC(rc2);
2046 rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStMicIn);
2047 AssertRC(rc2);
2048
2049 SSMR3GetMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
2050
2051 /** @todo r=andy Stream IDs are hardcoded to certain streams. */
2052 uint8_t uaStrmsActive[LAST_INDEX];
2053 SSMR3GetMem(pSSM, uaStrmsActive, sizeof(uaStrmsActive));
2054
2055 ichac97RecordSelect(pThis, ichac97MixerGet(pThis, AC97_Record_Select));
2056# define V_(a, b) ichac97MixerSetVolume(pThis, a, b, ichac97MixerGet(pThis, a))
2057 V_(AC97_Master_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME);
2058 V_(AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_FRONT);
2059 V_(AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN);
2060# undef V_
2061 if (pThis->uCodecModel == Codec_AD1980)
2062 if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_HPSEL)
2063 ichac97MixerSetVolume(pThis, AC97_Headphone_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME,
2064 ichac97MixerGet(pThis, AC97_Headphone_Volume_Mute));
2065
2066 int rc;
2067 rc = ichac97StreamInit(pThis, &pThis->StrmStLineIn, PI_INDEX);
2068 AssertRC(rc);
2069 rc = ichac97StreamInit(pThis, &pThis->StrmStMicIn, MC_INDEX);
2070 AssertRC(rc);
2071 rc = ichac97StreamInit(pThis, &pThis->StrmStOut, PO_INDEX);
2072 AssertRC(rc);
2073
2074 /** @todo r=andy Stream IDs are hardcoded to certain streams. */
2075 rc = ichac97StreamSetActive(pThis, &pThis->StrmStLineIn, RT_BOOL(uaStrmsActive[PI_INDEX]));
2076 AssertRC(rc);
2077 rc = ichac97StreamSetActive(pThis, &pThis->StrmStMicIn, RT_BOOL(uaStrmsActive[MC_INDEX]));
2078 AssertRC(rc);
2079 rc = ichac97StreamSetActive(pThis, &pThis->StrmStOut, RT_BOOL(uaStrmsActive[PO_INDEX]));
2080 AssertRC(rc);
2081
2082 pThis->bup_flag = 0;
2083 pThis->last_samp = 0;
2084
2085 return VINF_SUCCESS;
2086}
2087
2088
2089/**
2090 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2091 */
2092static DECLCALLBACK(void *) ichac97QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2093{
2094 PAC97STATE pThis = RT_FROM_MEMBER(pInterface, AC97STATE, IBase);
2095 Assert(&pThis->IBase == pInterface);
2096
2097 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2098 return NULL;
2099}
2100
2101
2102/**
2103 * @interface_method_impl{PDMDEVREG,pfnReset}
2104 *
2105 * @remarks The original sources didn't install a reset handler, but it seems to
2106 * make sense to me so we'll do it.
2107 */
2108static DECLCALLBACK(void) ichac97Reset(PPDMDEVINS pDevIns)
2109{
2110 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
2111
2112 LogFlowFuncEnter();
2113
2114 /*
2115 * Reset the device state (will need pDrv later).
2116 */
2117 ichac97StreamResetBMRegs(pThis, &pThis->StrmStLineIn);
2118 ichac97StreamResetBMRegs(pThis, &pThis->StrmStMicIn);
2119 ichac97StreamResetBMRegs(pThis, &pThis->StrmStOut);
2120
2121 /*
2122 * Reset the mixer too. The Windows XP driver seems to rely on
2123 * this. At least it wants to read the vendor id before it resets
2124 * the codec manually.
2125 */
2126 ichac97MixerReset(pThis);
2127
2128 /*
2129 * Stop any audio currently playing.
2130 */
2131 PAC97DRIVER pDrv;
2132 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
2133 {
2134 pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->LineIn.pStrmIn, false /* Disable */);
2135 /* Ignore rc. */
2136 pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->MicIn.pStrmIn, false /* Disable */);
2137 /* Ditto. */
2138 pDrv->pConnector->pfnEnableOut(pDrv->pConnector, pDrv->Out.pStrmOut, false /* Disable */);
2139 /* Ditto. */
2140 }
2141
2142 /*
2143 * Reset all streams.
2144 */
2145 ichac97StreamReset(pThis, &pThis->StrmStLineIn);
2146 ichac97StreamReset(pThis, &pThis->StrmStMicIn);
2147 ichac97StreamReset(pThis, &pThis->StrmStOut);
2148
2149 LogRel(("AC97: Reset\n"));
2150}
2151
2152
2153/**
2154 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2155 */
2156static DECLCALLBACK(int) ichac97Destruct(PPDMDEVINS pDevIns)
2157{
2158 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
2159
2160 LogFlowFuncEnter();
2161
2162 PAC97DRIVER pDrv;
2163 while (!RTListIsEmpty(&pThis->lstDrv))
2164 {
2165 pDrv = RTListGetFirst(&pThis->lstDrv, AC97DRIVER, Node);
2166
2167 RTListNodeRemove(&pDrv->Node);
2168 RTMemFree(pDrv);
2169 }
2170
2171 if (pThis->pMixer)
2172 {
2173 AudioMixerDestroy(pThis->pMixer);
2174 pThis->pMixer = NULL;
2175 }
2176
2177 if (pThis->pvReadWriteBuf)
2178 {
2179 RTMemFree(pThis->pvReadWriteBuf);
2180 pThis->pvReadWriteBuf = NULL;
2181 pThis->cbReadWriteBuf = 0;
2182 }
2183
2184 LogFlowFuncLeave();
2185 return VINF_SUCCESS;
2186}
2187
2188
2189/**
2190 * Attach command, internal version.
2191 *
2192 * This is called to let the device attach to a driver for a specified LUN
2193 * during runtime. This is not called during VM construction, the device
2194 * constructor has to attach to all the available drivers.
2195 *
2196 * @returns VBox status code.
2197 * @param pDevIns The device instance.
2198 * @param pDrv Driver to (re-)use for (re-)attaching to.
2199 * If NULL is specified, a new driver will be created and appended
2200 * to the driver list.
2201 * @param uLUN The logical unit which is being detached.
2202 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2203 */
2204static DECLCALLBACK(int) ichac97AttachInternal(PPDMDEVINS pDevIns, PAC97DRIVER pDrv, unsigned uLUN, uint32_t fFlags)
2205{
2206 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
2207
2208 /*
2209 * Attach driver.
2210 */
2211 char *pszDesc = NULL;
2212 if (RTStrAPrintf(&pszDesc, "Audio driver port (AC'97) for LUN #%u", uLUN) <= 0)
2213 AssertReleaseMsgReturn(pszDesc,
2214 ("Not enough memory for AC'97 driver port description of LUN #%u\n", uLUN),
2215 VERR_NO_MEMORY);
2216
2217 PPDMIBASE pDrvBase;
2218 int rc = PDMDevHlpDriverAttach(pDevIns, uLUN,
2219 &pThis->IBase, &pDrvBase, pszDesc);
2220 if (RT_SUCCESS(rc))
2221 {
2222 if (pDrv == NULL)
2223 pDrv = (PAC97DRIVER)RTMemAllocZ(sizeof(AC97DRIVER));
2224 if (pDrv)
2225 {
2226 pDrv->pDrvBase = pDrvBase;
2227 pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
2228 AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
2229 pDrv->pAC97State = pThis;
2230 pDrv->uLUN = uLUN;
2231
2232 /*
2233 * For now we always set the driver at LUN 0 as our primary
2234 * host backend. This might change in the future.
2235 */
2236 if (pDrv->uLUN == 0)
2237 pDrv->Flags |= PDMAUDIODRVFLAG_PRIMARY;
2238
2239 LogFunc(("LUN#%RU8: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->Flags));
2240
2241 /* Attach to driver list if not attached yet. */
2242 if (!pDrv->fAttached)
2243 {
2244 RTListAppend(&pThis->lstDrv, &pDrv->Node);
2245 pDrv->fAttached = true;
2246 }
2247 }
2248 else
2249 rc = VERR_NO_MEMORY;
2250 }
2251 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2252 LogFunc(("No attached driver for LUN #%u\n", uLUN));
2253
2254 if (RT_FAILURE(rc))
2255 {
2256 /* Only free this string on failure;
2257 * must remain valid for the live of the driver instance. */
2258 RTStrFree(pszDesc);
2259 }
2260
2261 LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
2262 return rc;
2263}
2264
2265
2266/**
2267 * Attach command.
2268 *
2269 * This is called to let the device attach to a driver for a specified LUN
2270 * during runtime. This is not called during VM construction, the device
2271 * constructor has to attach to all the available drivers.
2272 *
2273 * @returns VBox status code.
2274 * @param pDevIns The device instance.
2275 * @param uLUN The logical unit which is being detached.
2276 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2277 */
2278static DECLCALLBACK(int) ichac97Attach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
2279{
2280 return ichac97AttachInternal(pDevIns, NULL /* pDrv */, uLUN, fFlags);
2281}
2282
2283static DECLCALLBACK(void) ichac97Detach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
2284{
2285 LogFunc(("iLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
2286}
2287
2288/**
2289 * Re-attach.
2290 *
2291 * @returns VBox status code.
2292 * @param pThis Device instance.
2293 * @param pDrv Driver instance used for attaching to.
2294 * If NULL is specified, a new driver will be created and appended
2295 * to the driver list.
2296 * @param uLUN The logical unit which is being re-detached.
2297 * @param pszDriver Driver name.
2298 */
2299static int ichac97Reattach(PAC97STATE pThis, PAC97DRIVER pDrv, uint8_t uLUN, const char *pszDriver)
2300{
2301 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2302 AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
2303
2304 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
2305 PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
2306 PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/ichac97/0/");
2307
2308 /* Remove LUN branch. */
2309 CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
2310
2311 if (pDrv)
2312 {
2313 /* Re-use a driver instance => detach the driver before. */
2314 int rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
2315 if (RT_FAILURE(rc))
2316 return rc;
2317 }
2318
2319#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
2320
2321 int rc = VINF_SUCCESS;
2322 do
2323 {
2324 PCFGMNODE pLunL0;
2325 rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN); RC_CHECK();
2326 rc = CFGMR3InsertString(pLunL0, "Driver", "AUDIO"); RC_CHECK();
2327 rc = CFGMR3InsertNode(pLunL0, "Config/", NULL); RC_CHECK();
2328
2329 PCFGMNODE pLunL1, pLunL2;
2330 rc = CFGMR3InsertNode (pLunL0, "AttachedDriver/", &pLunL1); RC_CHECK();
2331 rc = CFGMR3InsertNode (pLunL1, "Config/", &pLunL2); RC_CHECK();
2332 rc = CFGMR3InsertString(pLunL1, "Driver", pszDriver); RC_CHECK();
2333
2334 rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver); RC_CHECK();
2335
2336 } while (0);
2337
2338 if (RT_SUCCESS(rc))
2339 rc = ichac97AttachInternal(pThis->pDevInsR3, pDrv, uLUN, 0 /* fFlags */);
2340
2341 LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
2342
2343#undef RC_CHECK
2344
2345 return rc;
2346}
2347
2348/**
2349 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2350 */
2351static DECLCALLBACK(int) ichac97Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2352{
2353 PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
2354
2355 /* NB: This must be done *before* any possible failure (and running the destructor). */
2356 RTListInit(&pThis->lstDrv);
2357
2358 Assert(iInstance == 0);
2359 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2360
2361 /*
2362 * Validations.
2363 */
2364 if (!CFGMR3AreValuesValid(pCfg,
2365 "Codec\0"
2366 "TimerHz\0"))
2367 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2368 N_("Invalid configuration for the AC'97 device"));
2369
2370 /*
2371 * Read config data.
2372 */
2373 char szCodec[20];
2374 int rc = CFGMR3QueryStringDef(pCfg, "Codec", &szCodec[0], sizeof(szCodec), "STAC9700");
2375 if (RT_FAILURE(rc))
2376 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
2377 N_("AC'97 configuration error: Querying \"Codec\" as string failed"));
2378
2379#ifndef VBOX_WITH_AUDIO_CALLBACKS
2380 uint16_t uTimerHz;
2381 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 200 /* Hz */);
2382 if (RT_FAILURE(rc))
2383 return PDMDEV_SET_ERROR(pDevIns, rc,
2384 N_("AC'97 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
2385#endif
2386
2387 /*
2388 * The AD1980 codec (with corresponding PCI subsystem vendor ID) is whitelisted
2389 * in the Linux kernel; Linux makes no attempt to measure the data rate and assumes
2390 * 48 kHz rate, which is exactly what we need. Same goes for AD1981B.
2391 */
2392 bool fChipAD1980 = false;
2393 if (!strcmp(szCodec, "STAC9700"))
2394 pThis->uCodecModel = Codec_STAC9700;
2395 else if (!strcmp(szCodec, "AD1980"))
2396 pThis->uCodecModel = Codec_AD1980;
2397 else if (!strcmp(szCodec, "AD1981B"))
2398 pThis->uCodecModel = Codec_AD1981B;
2399 else
2400 {
2401 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
2402 N_("AC'97 configuration error: The \"Codec\" value \"%s\" is unsupported"),
2403 szCodec);
2404 }
2405
2406 /*
2407 * Initialize data (most of it anyway).
2408 */
2409 pThis->pDevInsR3 = pDevIns;
2410 /* IBase */
2411 pThis->IBase.pfnQueryInterface = ichac97QueryInterface;
2412
2413 /* PCI Device (the assertions will be removed later) */
2414 PCIDevSetVendorId (&pThis->PciDev, 0x8086); /* 00 ro - intel. */ Assert(pThis->PciDev.config[0x00] == 0x86); Assert(pThis->PciDev.config[0x01] == 0x80);
2415 PCIDevSetDeviceId (&pThis->PciDev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */ Assert(pThis->PciDev.config[0x02] == 0x15); Assert(pThis->PciDev.config[0x03] == 0x24);
2416 PCIDevSetCommand (&pThis->PciDev, 0x0000); /* 04 rw,ro - pcicmd. */ Assert(pThis->PciDev.config[0x04] == 0x00); Assert(pThis->PciDev.config[0x05] == 0x00);
2417 PCIDevSetStatus (&pThis->PciDev, VBOX_PCI_STATUS_DEVSEL_MEDIUM | VBOX_PCI_STATUS_FAST_BACK); /* 06 rwc?,ro? - pcists. */ Assert(pThis->PciDev.config[0x06] == 0x80); Assert(pThis->PciDev.config[0x07] == 0x02);
2418 PCIDevSetRevisionId (&pThis->PciDev, 0x01); /* 08 ro - rid. */ Assert(pThis->PciDev.config[0x08] == 0x01);
2419 PCIDevSetClassProg (&pThis->PciDev, 0x00); /* 09 ro - pi. */ Assert(pThis->PciDev.config[0x09] == 0x00);
2420 PCIDevSetClassSub (&pThis->PciDev, 0x01); /* 0a ro - scc; 01 == Audio. */ Assert(pThis->PciDev.config[0x0a] == 0x01);
2421 PCIDevSetClassBase (&pThis->PciDev, 0x04); /* 0b ro - bcc; 04 == multimedia. */ Assert(pThis->PciDev.config[0x0b] == 0x04);
2422 PCIDevSetHeaderType (&pThis->PciDev, 0x00); /* 0e ro - headtyp. */ Assert(pThis->PciDev.config[0x0e] == 0x00);
2423 PCIDevSetBaseAddress (&pThis->PciDev, 0, /* 10 rw - nambar - native audio mixer base. */
2424 true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pThis->PciDev.config[0x10] == 0x01); Assert(pThis->PciDev.config[0x11] == 0x00); Assert(pThis->PciDev.config[0x12] == 0x00); Assert(pThis->PciDev.config[0x13] == 0x00);
2425 PCIDevSetBaseAddress (&pThis->PciDev, 1, /* 14 rw - nabmbar - native audio bus mastering. */
2426 true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pThis->PciDev.config[0x14] == 0x01); Assert(pThis->PciDev.config[0x15] == 0x00); Assert(pThis->PciDev.config[0x16] == 0x00); Assert(pThis->PciDev.config[0x17] == 0x00);
2427 PCIDevSetInterruptLine (&pThis->PciDev, 0x00); /* 3c rw. */ Assert(pThis->PciDev.config[0x3c] == 0x00);
2428 PCIDevSetInterruptPin (&pThis->PciDev, 0x01); /* 3d ro - INTA#. */ Assert(pThis->PciDev.config[0x3d] == 0x01);
2429
2430 if (pThis->uCodecModel == Codec_AD1980)
2431 {
2432 PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x1028); /* 2c ro - Dell.) */
2433 PCIDevSetSubSystemId (&pThis->PciDev, 0x0177); /* 2e ro. */
2434 }
2435 else if (pThis->uCodecModel == Codec_AD1981B)
2436 {
2437 PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x1028); /* 2c ro - Dell.) */
2438 PCIDevSetSubSystemId (&pThis->PciDev, 0x01ad); /* 2e ro. */
2439 }
2440 else
2441 {
2442 PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x8086); /* 2c ro - Intel.) */
2443 PCIDevSetSubSystemId (&pThis->PciDev, 0x0000); /* 2e ro. */
2444 }
2445
2446 /*
2447 * Register the PCI device, it's I/O regions, the timer and the
2448 * saved state item.
2449 */
2450 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
2451 if (RT_FAILURE(rc))
2452 return rc;
2453
2454 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 256, PCI_ADDRESS_SPACE_IO, ichac97IOPortMap);
2455 if (RT_FAILURE(rc))
2456 return rc;
2457
2458 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 64, PCI_ADDRESS_SPACE_IO, ichac97IOPortMap);
2459 if (RT_FAILURE(rc))
2460 return rc;
2461
2462 rc = PDMDevHlpSSMRegister(pDevIns, AC97_SSM_VERSION, sizeof(*pThis), ichac97SaveExec, ichac97LoadExec);
2463 if (RT_FAILURE(rc))
2464 return rc;
2465
2466 /*
2467 * Attach driver.
2468 */
2469 uint8_t uLUN;
2470 for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
2471 {
2472 LogFunc(("Trying to attach driver for LUN #%RU8 ...\n", uLUN));
2473 rc = ichac97AttachInternal(pDevIns, NULL /* pDrv */, uLUN, 0 /* fFlags */);
2474 if (RT_FAILURE(rc))
2475 {
2476 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
2477 rc = VINF_SUCCESS;
2478 else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
2479 {
2480 ichac97Reattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
2481 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2482 N_("No audio devices could be opened. Selecting the NULL audio backend "
2483 "with the consequence that no sound is audible"));
2484 /* attaching to the NULL audio backend will never fail */
2485 rc = VINF_SUCCESS;
2486 }
2487 break;
2488 }
2489 }
2490
2491 LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
2492
2493 if (RT_SUCCESS(rc))
2494 {
2495 rc = AudioMixerCreate("AC'97 Mixer", 0 /* uFlags */, &pThis->pMixer);
2496 if (RT_SUCCESS(rc))
2497 {
2498 /* Set a default audio format for our mixer. */
2499 PDMAUDIOSTREAMCFG streamCfg;
2500 streamCfg.uHz = 44100;
2501 streamCfg.cChannels = 2;
2502 streamCfg.enmFormat = AUD_FMT_S16;
2503 streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
2504
2505 rc = AudioMixerSetDeviceFormat(pThis->pMixer, &streamCfg);
2506 AssertRC(rc);
2507
2508 /* Add all required audio sinks. */
2509 rc = AudioMixerAddSink(pThis->pMixer, "[Playback] PCM Output", AUDMIXSINKDIR_OUTPUT, &pThis->pSinkOutput);
2510 AssertRC(rc);
2511
2512 rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Line In", AUDMIXSINKDIR_INPUT, &pThis->pSinkLineIn);
2513 AssertRC(rc);
2514
2515 rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Microphone In", AUDMIXSINKDIR_INPUT, &pThis->pSinkMicIn);
2516 AssertRC(rc);
2517 }
2518 }
2519
2520 ichac97Reset(pDevIns);
2521
2522 if (RT_SUCCESS(rc))
2523 {
2524 rc = ichac97StreamInit(pThis, &pThis->StrmStLineIn, PI_INDEX);
2525 if (RT_FAILURE(rc))
2526 return rc;
2527 rc = ichac97StreamInit(pThis, &pThis->StrmStMicIn, MC_INDEX);
2528 if (RT_FAILURE(rc))
2529 return rc;
2530 rc = ichac97StreamInit(pThis, &pThis->StrmStOut, PO_INDEX);
2531 if (RT_FAILURE(rc))
2532 return rc;
2533
2534 PAC97DRIVER pDrv;
2535 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
2536 {
2537 /*
2538 * Only primary drivers are critical for the VM to run. Everything else
2539 * might not worth showing an own error message box in the GUI.
2540 */
2541 if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
2542 continue;
2543
2544 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
2545 AssertPtr(pCon);
2546
2547 bool fValidLineIn = pCon->pfnIsValidIn (pCon, pDrv->LineIn.pStrmIn);
2548 bool fValidMicIn = pCon->pfnIsValidIn (pCon, pDrv->MicIn.pStrmIn);
2549 bool fValidOut = pCon->pfnIsValidOut(pCon, pDrv->Out.pStrmOut);
2550
2551 if ( !fValidLineIn
2552 && !fValidMicIn
2553 && !fValidOut)
2554 {
2555 LogRel(("AC97: Falling back to NULL backend (no sound audible)\n"));
2556
2557 ichac97Reset(pDevIns);
2558 ichac97Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
2559
2560 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2561 N_("No audio devices could be opened. Selecting the NULL audio backend "
2562 "with the consequence that no sound is audible"));
2563 }
2564 else
2565 {
2566 bool fWarn = false;
2567
2568 PDMAUDIOBACKENDCFG backendCfg;
2569 int rc2 = pCon->pfnGetConfiguration(pCon, &backendCfg);
2570 if (RT_SUCCESS(rc2))
2571 {
2572 if (backendCfg.cSources)
2573 {
2574 /* If the audio backend supports two or more input streams at once,
2575 * warn if one of our two inputs (microphone-in and line-in) failed to initialize. */
2576 if (backendCfg.cMaxStreamsIn >= 2)
2577 fWarn = !fValidLineIn || !fValidMicIn;
2578 /* If the audio backend only supports one input stream at once (e.g. pure ALSA, and
2579 * *not* ALSA via PulseAudio plugin!), only warn if both of our inputs failed to initialize.
2580 * One of the two simply is not in use then. */
2581 else if (backendCfg.cMaxStreamsIn == 1)
2582 fWarn = !fValidLineIn && !fValidMicIn;
2583 /* Don't warn if our backend is not able of supporting any input streams at all. */
2584 }
2585
2586 if ( !fWarn
2587 && backendCfg.cSinks)
2588 {
2589 fWarn = !fValidOut;
2590 }
2591 }
2592 else
2593 AssertReleaseMsgFailed(("Unable to retrieve audio backend configuration for LUN #%RU8, rc=%Rrc\n",
2594 pDrv->uLUN, rc2));
2595
2596 if (fWarn)
2597 {
2598 char szMissingStreams[255];
2599 size_t len = 0;
2600 if (!fValidLineIn)
2601 {
2602 LogRel(("AC97: WARNING: Unable to open PCM line input for LUN #%RU8!\n", pDrv->uLUN));
2603 len = RTStrPrintf(szMissingStreams, sizeof(szMissingStreams), "PCM Input");
2604 }
2605 if (!fValidMicIn)
2606 {
2607 LogRel(("AC97: WARNING: Unable to open PCM microphone input for LUN #%RU8!\n", pDrv->uLUN));
2608 len += RTStrPrintf(szMissingStreams + len,
2609 sizeof(szMissingStreams) - len, len ? ", PCM Microphone" : "PCM Microphone");
2610 }
2611 if (!fValidOut)
2612 {
2613 LogRel(("AC97: WARNING: Unable to open PCM output for LUN #%RU8!\n", pDrv->uLUN));
2614 len += RTStrPrintf(szMissingStreams + len,
2615 sizeof(szMissingStreams) - len, len ? ", PCM Output" : "PCM Output");
2616 }
2617
2618 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
2619 N_("Some AC'97 audio streams (%s) could not be opened. Guest applications generating audio "
2620 "output or depending on audio input may hang. Make sure your host audio device "
2621 "is working properly. Check the logfile for error messages of the audio "
2622 "subsystem"), szMissingStreams);
2623 }
2624 }
2625 }
2626 }
2627
2628 if (RT_SUCCESS(rc))
2629 {
2630 pThis->cbReadWriteBuf = _4K; /** @todo Make this configurable. */
2631 pThis->pvReadWriteBuf = (uint8_t *)RTMemAllocZ(pThis->cbReadWriteBuf);
2632 if (!pThis->pvReadWriteBuf)
2633 rc = VERR_NO_MEMORY;
2634 }
2635
2636# ifndef VBOX_WITH_AUDIO_CALLBACKS
2637 if (RT_SUCCESS(rc))
2638 {
2639 /* Start the emulation timer. */
2640 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ichac97Timer, pThis,
2641 TMTIMER_FLAGS_NO_CRIT_SECT, "DevIchAc97", &pThis->pTimer);
2642 AssertRCReturn(rc, rc);
2643
2644 if (RT_SUCCESS(rc))
2645 {
2646 pThis->cTimerTicks = TMTimerGetFreq(pThis->pTimer) / uTimerHz;
2647 pThis->uTimerTS = TMTimerGet(pThis->pTimer);
2648 LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicks, uTimerHz));
2649
2650 /* Fire off timer. */
2651 TMTimerSet(pThis->pTimer, TMTimerGet(pThis->pTimer) + pThis->cTimerTicks);
2652 }
2653 }
2654# else
2655 if (RT_SUCCESS(rc))
2656 {
2657 PAC97DRIVER pDrv;
2658 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
2659 {
2660 /* Only register primary driver.
2661 * The device emulation does the output multiplexing then. */
2662 if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
2663 continue;
2664
2665 PDMAUDIOCALLBACK AudioCallbacks[2];
2666
2667 AC97CALLBACKCTX Ctx = { pThis, pDrv };
2668
2669 AudioCallbacks[0].enmType = PDMAUDIOCALLBACKTYPE_INPUT;
2670 AudioCallbacks[0].pfnCallback = ac97CallbackInput;
2671 AudioCallbacks[0].pvCtx = &Ctx;
2672 AudioCallbacks[0].cbCtx = sizeof(AC97CALLBACKCTX);
2673
2674 AudioCallbacks[1].enmType = PDMAUDIOCALLBACKTYPE_OUTPUT;
2675 AudioCallbacks[1].pfnCallback = ac97CallbackOutput;
2676 AudioCallbacks[1].pvCtx = &Ctx;
2677 AudioCallbacks[1].cbCtx = sizeof(AC97CALLBACKCTX);
2678
2679 rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
2680 if (RT_FAILURE(rc))
2681 break;
2682 }
2683 }
2684# endif
2685
2686# ifdef VBOX_WITH_STATISTICS
2687 if (RT_SUCCESS(rc))
2688 {
2689 /*
2690 * Register statistics.
2691 */
2692 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "/Devices/AC97/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ichac97Timer.");
2693 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, "/Devices/AC97/BytesRead" , STAMUNIT_BYTES, "Bytes read from AC97 emulation.");
2694 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, "/Devices/AC97/BytesWritten", STAMUNIT_BYTES, "Bytes written to AC97 emulation.");
2695 }
2696# endif
2697
2698 LogFlowFuncLeaveRC(rc);
2699 return rc;
2700}
2701
2702/**
2703 * The device registration structure.
2704 */
2705const PDMDEVREG g_DeviceICHAC97 =
2706{
2707 /* u32Version */
2708 PDM_DEVREG_VERSION,
2709 /* szName */
2710 "ichac97",
2711 /* szRCMod */
2712 "",
2713 /* szR0Mod */
2714 "",
2715 /* pszDescription */
2716 "ICH AC'97 Audio Controller",
2717 /* fFlags */
2718 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2719 /* fClass */
2720 PDM_DEVREG_CLASS_AUDIO,
2721 /* cMaxInstances */
2722 1,
2723 /* cbInstance */
2724 sizeof(AC97STATE),
2725 /* pfnConstruct */
2726 ichac97Construct,
2727 /* pfnDestruct */
2728 ichac97Destruct,
2729 /* pfnRelocate */
2730 NULL,
2731 /* pfnMemSetup */
2732 NULL,
2733 /* pfnPowerOn */
2734 NULL,
2735 /* pfnReset */
2736 ichac97Reset,
2737 /* pfnSuspend */
2738 NULL,
2739 /* pfnResume */
2740 NULL,
2741 /* pfnAttach */
2742 ichac97Attach,
2743 /* pfnDetach */
2744 ichac97Detach,
2745 /* pfnQueryInterface. */
2746 NULL,
2747 /* pfnInitComplete */
2748 NULL,
2749 /* pfnPowerOff */
2750 NULL,
2751 /* pfnSoftReset */
2752 NULL,
2753 /* u32VersionEnd */
2754 PDM_DEVREG_VERSION
2755};
2756
2757#endif /* !IN_RING3 */
2758#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use