VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio_old/DevIchAc97.cpp@ 62463

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

Devices: scm

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