DevIchAc97.cpp revision b5d837811bf21f30a31748bbbcb28ee562bb2355
/* $Id$ */
/** @file
* DevIchAc97 - VBox ICH AC97 Audio Controller.
*/
/*
* Copyright (C) 2006-2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#include "../Builtins.h"
extern "C" {
#include "audio.h"
}
#ifndef VBOX
//#define USE_MIXER
#else
#define USE_MIXER
#endif
#define AC97_SSM_VERSION 1
enum {
AC97_Reset = 0x00,
AC97_Master_Volume_Mute = 0x02,
AC97_Headphone_Volume_Mute = 0x04,
AC97_Master_Volume_Mono_Mute = 0x06,
AC97_Master_Tone_RL = 0x08,
AC97_PC_BEEP_Volume_Mute = 0x0A,
AC97_Phone_Volume_Mute = 0x0C,
AC97_Mic_Volume_Mute = 0x0E,
AC97_Line_In_Volume_Mute = 0x10,
AC97_CD_Volume_Mute = 0x12,
AC97_Video_Volume_Mute = 0x14,
AC97_Aux_Volume_Mute = 0x16,
AC97_PCM_Out_Volume_Mute = 0x18,
AC97_Record_Select = 0x1A,
AC97_Record_Gain_Mute = 0x1C,
AC97_Record_Gain_Mic_Mute = 0x1E,
AC97_General_Purpose = 0x20,
AC97_3D_Control = 0x22,
AC97_AC_97_RESERVED = 0x24,
AC97_Powerdown_Ctrl_Stat = 0x26,
AC97_Extended_Audio_ID = 0x28,
AC97_Extended_Audio_Ctrl_Stat = 0x2A,
AC97_PCM_Front_DAC_Rate = 0x2C,
AC97_PCM_Surround_DAC_Rate = 0x2E,
AC97_PCM_LFE_DAC_Rate = 0x30,
AC97_PCM_LR_ADC_Rate = 0x32,
AC97_MIC_ADC_Rate = 0x34,
AC97_6Ch_Vol_C_LFE_Mute = 0x36,
AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
AC97_Vendor_Reserved = 0x58,
AC97_Vendor_ID1 = 0x7c,
AC97_Vendor_ID2 = 0x7e
};
#ifndef VBOX
# define SOFT_VOLUME
#else
#endif
#define GS_RO_MASK (GS_B3S12| \
GS_B2S12| \
GS_B1S12| \
GS_S1CR| \
GS_S0CR| \
GS_MINT| \
GS_POINT| \
GS_PIINT| \
GS_RSRVD| \
GS_MOINT| \
/** Buffer Descriptor */
#define EACS_VRA 1
#define EACS_VRM 8
#define VOL_MASK 0x1f
#define MUTE_SHIFT 15
#define REC_MASK 7
enum
{
REC_MIC = 0,
};
typedef struct BD
{
} BD;
typedef struct AC97BusMasterRegs
{
int bd_valid; /* initialized? */
typedef struct AC97LinkState
{
/** Global Control (Bus Master Control Register) */
/** Global Status (Bus Master Control Register) */
/** Codec Access Semaphore Register (Bus Master Control Register) */
/** Bus Master Control Registers for PCM in, PCM out, and Mic in */
/** PCM in */
/** PCM out */
/** Mic in */
int bup_flag;
/** Pointer to the device instance. */
/** Pointer to the connector of the attached audio driver. */
/** Pointer to the attached audio driver. */
/** The base interface. */
/** Base port of the I/O space region. */
enum
{
};
typedef struct PCIAC97LinkState
{
enum { \
}
enum
{
PI_INDEX = 0, /* PCM in */
PO_INDEX, /* PCM out */
MC_INDEX, /* Mic in */
};
enum
{
GLOB_CNT = 0x2c,
GLOB_STA = 0x30,
CAS = 0x34
};
static void warm_reset (AC97LinkState *s)
{
(void) s;
}
static void cold_reset (AC97LinkState * s)
{
(void) s;
}
/** Fetch Buffer Descriptor at _CIV */
{
uint8_t b[8];
r->bd_valid = 1;
#if !defined(RT_ARCH_X86) && !defined(RT_ARCH_AMD64)
#else
#endif
Log (("ac97: bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
}
/**
* Update the BM status register
*/
{
int event = 0;
int level = 0;
{
/** @todo is IRQ deasserted when only one of status bits is cleared? */
if (!new_mask)
{
event = 1;
level = 0;
}
{
event = 1;
level = 1;
}
{
event = 1;
level = 1;
}
}
Log (("ac97: IOC%d LVB%d sr=%#x event=%d level=%d\n",
if (event)
{
if (level)
else
}
}
{
switch (bm_index)
{
default: AssertFailed ();
break;
}
}
{
Log (("ac97: reset_bm_regs\n"));
r->bdbar = 0;
r->civ = 0;
r->lvi = 0;
/** @todo do we need to do that? */
r->picb = 0;
r->piv = 0;
r->bd_valid = 0;
voice_set_active (s, r - s->bm_regs, 0);
}
{
if (i + 2 > sizeof (s->mixer_data))
{
Log (("ac97: mixer_store: index %d out of bounds %d\n",
i, sizeof (s->mixer_data)));
return;
}
s->mixer_data[i + 0] = v & 0xff;
}
{
if (i + 2 > sizeof (s->mixer_data))
{
Log (("ac97: mixer_store: index %d out of bounds %d\n",
i, sizeof (s->mixer_data)));
val = 0xffff;
}
else
return val;
}
{
if (freq)
{
as.endianness = 0;
switch (index)
{
case PI_INDEX: /* PCM in */
s, pi_callback, &as);
#ifdef LOG_VOICES
#endif
break;
case PO_INDEX: /* PCM out */
s, po_callback, &as);
#ifdef LOG_VOICES
#endif
break;
case MC_INDEX: /* Mic in */
s, mc_callback, &as);
#ifdef LOG_VOICES
#endif
break;
}
}
else
{
switch (index)
{
case PI_INDEX:
#ifdef LOG_VOICES
LogRel (("AC97: Closing PCM IN\n"));
#endif
break;
case PO_INDEX:
#ifdef LOG_VOICES
LogRel (("AC97: Closing PCM OUT\n"));
#endif
break;
case MC_INDEX:
#ifdef LOG_VOICES
LogRel (("AC97: Closing MIC IN\n"));
#endif
break;
}
}
}
{
}
#ifdef USE_MIXER
{
# ifdef SOFT_VOLUME
if (index == AC97_Master_Volume_Mute)
else
# else
# endif
/*
* From AC'97 SoundMax Codec AD1981A: "Because AC '97 defines 6-bit volume registers, to
* maintain compatibility whenever the D5 or D13 bits are set to `1,' their respective
* lower five volume bits are automatically set to `1' by the Codec logic. On readback,
* all lower 5 bits will read ones whenever these bits are set to `1.'"
*
* Linux ALSA depends on this behavior.
*/
}
{
switch (i)
{
case REC_MIC: return AUD_REC_MIC;
case REC_CD: return AUD_REC_CD;
case REC_VIDEO: return AUD_REC_VIDEO;
case REC_AUX: return AUD_REC_AUX;
case REC_LINE_IN: return AUD_REC_LINE_IN;
case REC_PHONE: return AUD_REC_PHONE;
default: Log (("ac97: Unknown record source %d, using MIC\n", i));
return AUD_REC_MIC;
}
}
{
switch (rs)
{
case AUD_REC_MIC: return REC_MIC;
case AUD_REC_CD: return REC_CD;
case AUD_REC_VIDEO: return REC_VIDEO;
case AUD_REC_AUX: return REC_AUX;
case AUD_REC_LINE_IN: return REC_LINE_IN;
case AUD_REC_PHONE: return REC_PHONE;
return REC_MIC;
}
}
{
}
#endif /* USE_MIXER */
static void mixer_reset (AC97LinkState *s)
{
Log (("ac97: mixer_reset\n"));
/*
* Sigmatel 9700 (STAC9700)
*/
#ifdef USE_MIXER
record_select (s, 0);
#else
mixer_store (s, AC97_Record_Select, 0);
#endif
reset_voices (s, active);
}
{
int to_copy = 0;
if (!temp)
{
*stop = 1;
return 0;
}
while (temp)
{
int copied;
Log (("ac97: write_audio max=%x to_copy=%x copied=%x\n",
if (!copied)
{
*stop = 1;
break;
}
}
if (!temp)
{
if (to_copy < 4)
{
Log (("ac97: whoops\n"));
s->last_samp = 0;
}
else
}
return written;
}
{
int written = 0;
Log (("ac97: write_bup\n"));
{
{
unsigned int i;
for (i = 0; i < sizeof (s->silence) / 4; i++)
*p++ = s->last_samp;
}
else
}
while (elapsed)
{
while (temp)
{
if (!copied)
return;
}
}
}
{
int to_copy = 0;
if (!temp)
{
*stop = 1;
return 0;
}
while (temp)
{
int acquired;
if (!acquired)
{
*stop = 1;
break;
}
}
return nread;
}
{
{
{
switch (index)
{
case PO_INDEX:
break;
}
}
return;
}
{
int temp;
if (!r->bd_valid)
{
Log (("ac97: invalid bd\n"));
fetch_bd (s, r);
}
if (!r->picb)
{
Log (("ac97: fresh bd %d is empty %#x %#x\n",
{
s->bup_flag = 0;
break;
}
fetch_bd (s, r);
return;
}
switch (index)
{
case PO_INDEX:
break;
case PI_INDEX:
case MC_INDEX:
break;
}
if (!r->picb)
{
{
stop = 1;
}
else
{
fetch_bd (s, r);
}
}
}
}
{
}
{
}
{
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
{
AC97LinkState *s = &d->ac97;
switch (cb)
{
case 1:
{
AC97BusMasterRegs *r = NULL;
*pu32 = ~0U;
switch (index)
{
case CAS:
/* Codec Access Semaphore Register */
s->cas = 1;
break;
case PI_CIV:
case PO_CIV:
case MC_CIV:
/* Current Index Value Register */
break;
case PI_LVI:
case PO_LVI:
case MC_LVI:
/* Last Valid Index Register */
break;
case PI_PIV:
case PO_PIV:
case MC_PIV:
/* Prefetched Index Value Register */
break;
case PI_CR:
case PO_CR:
case MC_CR:
/* Control Register */
break;
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register (lower part) */
break;
default:
break;
}
break;
}
case 2:
{
AC97BusMasterRegs *r = NULL;
*pu32 = ~0U;
switch (index)
{
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register */
break;
case PI_PICB:
case PO_PICB:
case MC_PICB:
/* Position in Current Buffer Register */
break;
default:
break;
}
break;
}
case 4:
{
AC97BusMasterRegs *r = NULL;
*pu32 = ~0U;
switch (index)
{
case PI_BDBAR:
case PO_BDBAR:
case MC_BDBAR:
/* Buffer Descriptor Base Address Register */
break;
case PI_CIV:
case PO_CIV:
case MC_CIV:
/* 32-bit access: Current Index Value Register +
* Last Valid Index Register +
* Status Register */
break;
case PI_PICB:
case PO_PICB:
case MC_PICB:
/* 32-bit access: Position in Current Buffer Register +
* Prefetched Index Value Register +
* Control Register */
break;
case GLOB_CNT:
/* Global Control */
break;
case GLOB_STA:
/* Global Status */
break;
default:
break;
}
break;
}
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
AC97LinkState *s = &d->ac97;
switch (cb)
{
case 1:
{
AC97BusMasterRegs *r = NULL;
switch (index)
{
case PI_LVI:
case PO_LVI:
case MC_LVI:
/* Last Valid Index */
fetch_bd (s, r);
}
break;
case PI_CR:
case PO_CR:
case MC_CR:
/* Control Register */
reset_bm_regs (s, r);
else
{
{
voice_set_active (s, r - s->bm_regs, 0);
}
else
{
fetch_bd (s, r);
}
}
break;
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register */
break;
default:
break;
}
break;
}
case 2:
{
AC97BusMasterRegs *r = NULL;
switch (index)
{
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register */
break;
default:
break;
}
break;
}
case 4:
{
AC97BusMasterRegs *r = NULL;
switch (index)
{
case PI_BDBAR:
case PO_BDBAR:
case MC_BDBAR:
/* Buffer Descriptor list Base Address Register */
Log (("ac97: BDBAR[%d] <- %#x (bdbar %#x)\n",
break;
case GLOB_CNT:
/* Global Control */
warm_reset (s);
cold_reset (s);
break;
case GLOB_STA:
/* Global Status */
break;
default:
break;
}
break;
}
default:
break;
}
return VINF_SUCCESS;
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
{
AC97LinkState *s = &d->ac97;
switch (cb)
{
case 1:
{
s->cas = 0;
*pu32 = ~0U;
break;
}
case 2:
{
*pu32 = ~0U;
s->cas = 0;
switch (index)
{
default:
break;
}
break;
}
case 4:
{
s->cas = 0;
*pu32 = ~0U;
break;
}
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param uPort Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
AC97LinkState *s = &d->ac97;
switch (cb)
{
case 1:
{
s->cas = 0;
break;
}
case 2:
{
s->cas = 0;
switch (index)
{
case AC97_Reset:
mixer_reset (s);
break;
case AC97_Powerdown_Ctrl_Stat:
u32 &= ~0xf;
break;
#ifdef USE_MIXER
case AC97_Master_Volume_Mute:
break;
case AC97_PCM_Out_Volume_Mute:
break;
case AC97_Line_In_Volume_Mute:
break;
case AC97_Record_Select:
record_select (s, u32);
break;
#else /* !USE_MIXER */
case AC97_Master_Volume_Mute:
case AC97_PCM_Out_Volume_Mute:
case AC97_Line_In_Volume_Mute:
case AC97_Record_Select:
break;
#endif /* !USE_MIXER */
case AC97_Vendor_ID1:
case AC97_Vendor_ID2:
break;
case AC97_Extended_Audio_ID:
break;
{
}
{
}
break;
case AC97_PCM_Front_DAC_Rate:
{
}
else
{
Log (("ac97: Attempt to set front DAC rate to %d, "
"but VRA is not set\n",
u32));
}
break;
case AC97_MIC_ADC_Rate:
{
}
else
{
Log (("ac97: Attempt to set MIC ADC rate to %d, "
"but VRM is not set\n",
u32));
}
break;
case AC97_PCM_LR_ADC_Rate:
{
}
else
{
Log (("ac97: Attempt to set LR ADC rate to %d, but VRA is not set\n",
u32));
}
break;
default:
break;
}
break;
}
case 4:
{
s->cas = 0;
break;
}
default:
break;
}
return VINF_SUCCESS;
}
/**
* Callback function for mapping a PCI I/O region.
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device.
* Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region.
* If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative
* to pci_mem_base like earlier!
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
*/
{
int rc;
if (iRegion == 0)
else
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Saves a state of the AC'97 device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
{
size_t i;
{
AC97BusMasterRegs *r = &s->bm_regs[i];
}
return VINF_SUCCESS;
}
/**
* Loads a saved AC'97 device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param uVersion The data unit version number.
* @param uPhase The data phase.
*/
{
size_t i;
AssertMsgReturn (uVersion == AC97_SSM_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
{
AC97BusMasterRegs *r = &s->bm_regs[i];
}
#ifdef USE_MIXER
#endif /* USE_MIXER */
reset_voices (s, active);
s->bup_flag = 0;
s->last_samp = 0;
return VINF_SUCCESS;
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*
* @remark The original sources didn't install a reset handler, but it seems to
* make sense to me so we'll do it.
*/
{
/*
* Reset the device state (will need pDrv later).
*/
/*
* Reset the mixer too. The Windows XP driver seems to rely on
* this. At least it wants to read the vendor id before it resets
* the codec manually.
*/
}
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the driver.
* @param pInterface Pointer to this interface structure.
* @param enmInterface The requested interface identification.
* @thread Any thread.
*/
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
default:
return NULL;
}
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed,
* pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which
* registers and such to use.
* The device number is also found in pDevIns->iInstance,
* but since it's likely to be freqently used PDM passes
* it as parameter.
* @param pCfgHandle Configuration node handle for the device.
* Use this to obtain the configuration
* of the device instance. It's also found in
* pDevIns->pCfgHandle, but like iInstance it's expected
* to be used a bit in this function.
*/
{
int rc;
/*
* Initialize data (most of it anyway).
*/
/* IBase */
/* PCI Device (the assertions will be removed later) */
PCIDevSetVendorId (&pThis->dev, 0x8086); /* 00 ro - intel. */ Assert (pThis->dev.config[0x00] == 0x86); Assert (pThis->dev.config[0x01] == 0x80);
PCIDevSetDeviceId (&pThis->dev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */Assert (pThis->dev.config[0x02] == 0x15); Assert (pThis->dev.config[0x03] == 0x24);
PCIDevSetCommand (&pThis->dev, 0x0000); /* 04 rw,ro - pcicmd. */ Assert (pThis->dev.config[0x04] == 0x00); Assert (pThis->dev.config[0x05] == 0x00);
PCIDevSetStatus (&pThis->dev, 0x0280); /* 06 rwc?,ro? - pcists. */ Assert (pThis->dev.config[0x06] == 0x80); Assert (pThis->dev.config[0x07] == 0x02);
PCIDevSetRevisionId (&pThis->dev, 0x01); /* 08 ro - rid. */ Assert (pThis->dev.config[0x08] == 0x01);
PCIDevSetClassSub (&pThis->dev, 0x01); /* 0a ro - scc; 01 == Audio. */ Assert (pThis->dev.config[0x0a] == 0x01);
PCIDevSetClassBase (&pThis->dev, 0x04); /* 0b ro - bcc; 04 == multimedia. */ Assert (pThis->dev.config[0x0b] == 0x04);
PCIDevSetHeaderType (&pThis->dev, 0x00); /* 0e ro - headtyp. */ Assert (pThis->dev.config[0x0e] == 0x00);
true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert (pThis->dev.config[0x10] == 0x01); Assert (pThis->dev.config[0x11] == 0x00); Assert (pThis->dev.config[0x12] == 0x00); Assert (pThis->dev.config[0x13] == 0x00);
true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert (pThis->dev.config[0x14] == 0x01); Assert (pThis->dev.config[0x15] == 0x00); Assert (pThis->dev.config[0x16] == 0x00); Assert (pThis->dev.config[0x17] == 0x00);
PCIDevSetSubSystemVendorId (&pThis->dev, 0x8086); /* 2c ro - intel.) */ Assert (pThis->dev.config[0x2c] == 0x86); Assert (pThis->dev.config[0x2d] == 0x80);
PCIDevSetSubSystemId (&pThis->dev, 0x0000); /* 2e ro. */ Assert (pThis->dev.config[0x2e] == 0x00); Assert (pThis->dev.config[0x2f] == 0x00);
PCIDevSetInterruptPin (&pThis->dev, 0x01); /* 3d ro - INTA#. */ Assert (pThis->dev.config[0x3d] == 0x01);
/*
* Register the PCI device, it's I/O regions, the timer and the
* saved state item.
*/
if (RT_FAILURE (rc))
return rc;
if (RT_FAILURE (rc))
return rc;
if (RT_FAILURE (rc))
return rc;
rc = PDMDevHlpSSMRegister (pDevIns, AC97_SSM_VERSION, sizeof(*pThis), ichac97SaveExec, ichac97LoadExec);
if (RT_FAILURE (rc))
return rc;
/*
* Attach driver.
*/
&s->pDrvBase, "Audio Driver Port");
if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
Log (("ac97: No attached driver!\n"));
else if (RT_FAILURE (rc))
{
return rc;
}
#ifndef RT_OS_DARWIN /* coreaudio doesn't supply these. */
if (!s->voice_pi)
LogRel (("AC97: WARNING: Unable to open PCM IN!\n"));
if (!s->voice_mc)
LogRel (("AC97: WARNING: Unable to open PCM MC!\n"));
#endif
if (!s->voice_po)
LogRel (("AC97: WARNING: Unable to open PCM OUT!\n"));
{
/* Was not able initialize *any* voice. Select the NULL audio driver instead */
AUD_init_null ();
N_ ("No audio devices could be opened. Selecting the NULL audio backend "
"with the consequence that no sound is audible"));
}
#ifndef RT_OS_DARWIN
{
char szMissingVoices[128];
if (!s->voice_pi)
if (!s->voice_po)
len += RTStrPrintf (szMissingVoices + len, sizeof(szMissingVoices) - len, len ? ", PCM_out" : "PCM_out");
if (!s->voice_mc)
len += RTStrPrintf (szMissingVoices + len, sizeof(szMissingVoices) - len, len ? ", PCM_mic" : "PCM_mic");
N_ ("Some audio devices (%s) could not be opened. Guest applications generating audio "
"output or depending on audio input may hang. Make sure your host audio device "
"is working properly. Check the logfile for error messages of the audio "
"subsystem"), szMissingVoices);
}
#endif
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceICHAC97 =
{
/* u32Version */
/* szDeviceName */
"ichac97",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"ICH AC'97 Audio Controller",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(PCIAC97LinkState),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};