VirtualBox

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

Last change on this file since 59376 was 59376, checked in by vboxsync, 9 years ago

Audio: if the backend couldn't be initialized (ie pfnInit() failed), use the NULL audio driver

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