/* $Id$ */
/** @file
* DevIchAc97 - VBox ICH AC97 Audio Controller.
*/
/*
* Copyright (C) 2006-2015 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifdef IN_RING3
#endif
#include "VBoxDD.h"
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# include "AudioMixer.h"
#else
extern "C" {
#include "audio.h"
}
#endif
#ifdef LOG_GROUP
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifndef VBOX
//#define USE_MIXER
#else
# define USE_MIXER
#endif
#ifdef DEBUG
//#define DEBUG_LUN
# ifdef DEBUG_LUN
# endif
#endif /* DEBUG */
#ifdef VBOX
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# else
# endif
#else
# define SOFT_VOLUME
#endif
GS_B2S12 | \
GS_B1S12 | \
GS_S1CR | \
GS_S0CR | \
GS_MINT | \
GS_POINT | \
GS_PIINT | \
GS_RSRVD | \
GS_MOINT | \
/** @name Buffer Descriptor
* @{ */
/** @} */
enum
{
REC_MIC = 0,
};
enum
{
};
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Buffer descriptor.
*/
typedef struct BD
{
} BD;
typedef struct AC97BusMasterRegs
{
/** Pointer to a AC97 bus master register. */
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
typedef struct AC97INPUTSTREAM
{
/** PCM line input stream. */
/** Mixer handle for line input stream. */
typedef struct AC97OUTPUTSTREAM
{
/** PCM output stream. */
/** Mixer handle for output stream. */
/**
* Struct for maintaining a host backend driver.
*/
typedef struct AC97DRIVER
{
union
{
/** Node for storing this driver in our device driver
* list of AC97STATE. */
struct
{
} dummy;
};
/** Pointer to AC97 controller (state). */
/** Driver flags. */
/** LUN # to which this driver has been assigned. */
/** Audio connector interface to the underlying
* host backend. */
/** Stream for line input. */
/** Stream for mic input. */
/** Stream for output. */
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
typedef struct AC97STATE
{
/** The PCI device state. */
/** 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 */
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/** The emulation timer for handling the attached
* LUN drivers. */
/** Timer ticks for handling the LUN drivers. */
# ifdef VBOX_WITH_STATISTICS
# endif
/** List of associated LUN drivers. */
/** The device' software mixer. */
/** Audio sink for PCM output. */
/** Audio sink for line input. */
/** Audio sink for microphone input. */
#else
/** PCM in */
/** PCM out */
/** Mic in */
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
int bup_flag;
/** Pointer to the device instance. */
/** Pointer to the attached audio driver. */
/** The base interface for LUN\#0. */
/** Base port of the I/O space region. */
} AC97STATE;
/** Pointer to the AC97 device state. */
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
enum
{
};
enum { \
}
enum
{
};
enum
{
};
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
{
}
{
}
/** Fetches the buffer descriptor at _CIV. */
{
#if !defined(RT_ARCH_X86) && !defined(RT_ARCH_AMD64)
#else
#endif
LogFlowFunc(("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;
}
}
LogFlowFunc(("IOC%d LVB%d sr=%#x event=%d level=%d\n",
if (event)
{
if (level)
else
}
}
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
switch (bm_index)
{
case PI_INDEX:
break;
case PO_INDEX:
break;
case MC_INDEX:
break;
default:
break;
}
#else
switch (bm_index)
{
default: AssertFailed (); break;
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
{
LogFlowFunc(("reset_bm_regs\n"));
/** @todo do we need to do that? */
}
{
{
return;
}
}
{
{
val = 0xffff;
}
else
return val;
}
{
int rc;
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
if (freq)
{
char *pszDesc;
switch (index)
{
case PI_INDEX: /* Line input. */
{
{
{
rc = VERR_NO_MEMORY;
break;
}
{
0 /* uFlags */,
}
uLUN++;
}
break;
}
case PO_INDEX: /* Output. */
{
{
{
rc = VERR_NO_MEMORY;
break;
}
{
0 /* uFlags */,
}
uLUN++;
}
break;
}
case MC_INDEX: /* Mic in */
{
{
{
rc = VERR_NO_MEMORY;
break;
}
{
0 /* uFlags */,
}
uLUN++;
}
break;
}
default:
break;
}
}
else
{
switch (index)
{
case PI_INDEX:
{
{
}
LogFlowFunc(("Closed line input\n"));
break;
}
case PO_INDEX:
{
{
}
LogFlowFunc(("Closed output\n"));
break;
}
case MC_INDEX:
{
{
}
LogFlowFunc(("Closed microphone input\n"));
break;
}
default:
break;
}
rc = VINF_SUCCESS;
}
#else
if (freq)
{
as.endianness = 0;
switch (index)
{
case PI_INDEX: /* PCM in */
pThis->voice_pi = AUD_open_in(&pThis->card, pThis->voice_pi, "ac97.pi", pThis, ichac97InputCallback, &as);
#ifdef LOG_VOICES
#endif
break;
case PO_INDEX: /* PCM out */
pThis->voice_po = AUD_open_out(&pThis->card, pThis->voice_po, "ac97.po", pThis, ichac97OutputCallback, &as);
#ifdef LOG_VOICES
#endif
break;
case MC_INDEX: /* Mic in */
pThis->voice_mc = AUD_open_in(&pThis->card, pThis->voice_mc, "ac97.mc", pThis, ichac97MicInCallback, &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;
}
rc = VINF_SUCCESS;
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
/** @todo r=andy D'oh, pretty bad argument handling -- fix this! */
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
#ifdef USE_MIXER
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
{
#ifdef SOFT_VOLUME
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
switch (mt)
{
case PDMAUDIOMIXERCTL_VOLUME:
break;
case PDMAUDIOMIXERCTL_PCM:
break;
case PDMAUDIOMIXERCTL_MIC_IN:
break;
case PDMAUDIOMIXERCTL_LINE_IN:
break;
default:
break;
}
}
# else /* !VBOX_WITH_PDM_AUDIO_DRIVER */
if (index == AC97_Master_Volume_Mute)
else
# endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#else /* !SOFT_VOLUME */
#endif /* SOFT_VOLUME */
/*
* 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 PDMAUDIORECSOURCE_MIC;
case REC_CD: return PDMAUDIORECSOURCE_CD;
case REC_VIDEO: return PDMAUDIORECSOURCE_VIDEO;
case REC_AUX: return PDMAUDIORECSOURCE_AUX;
case REC_LINE_IN: return PDMAUDIORECSOURCE_LINE_IN;
case REC_PHONE: return PDMAUDIORECSOURCE_PHONE;
default:
break;
}
LogFlowFunc(("Unknown record source %d, using MIC\n", i));
return PDMAUDIORECSOURCE_MIC;
}
{
switch (rs)
{
case PDMAUDIORECSOURCE_MIC: return REC_MIC;
case PDMAUDIORECSOURCE_CD: return REC_CD;
case PDMAUDIORECSOURCE_VIDEO: return REC_VIDEO;
case PDMAUDIORECSOURCE_AUX: return REC_AUX;
case PDMAUDIORECSOURCE_LINE_IN: return REC_LINE_IN;
case PDMAUDIORECSOURCE_PHONE: return REC_PHONE;
default:
break;
}
return REC_MIC;
}
{
//AUD_set_record_source(&als, &ars);
}
#endif /* USE_MIXER */
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
}
{
}
if (RT_SUCCESS(rc2))
{
/* Set a default audio format for our mixer. */
/* Add all required audio sinks. */
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
{
/* Analog Devices 1980 (AD1980) */
}
else
{
/* Sigmatel 9700 (STAC9700) */
}
#ifdef USE_MIXER
ichac97RecordSelect(pThis, 0);
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# else
# endif
#else
#endif
/* Reset all streams. */
}
/**
* Writes data from the device to the host backends.
*
* @return IPRT status code.
* @return int
* @param pThis
* @param pReg
* @param cbMax
* @param pcbWritten
*/
static int ichac97WriteAudio(PAC97STATE pThis, PAC97BMREG pReg, uint32_t cbMax, uint32_t *pcbWritten)
{
if (!cbToWrite)
{
*pcbWritten = 0;
return VINF_EOF;
}
while (cbToWrite)
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/* Just multiplex the output to the connected backends.
* No need to utilize the virtual mixer here (yet). */
{
if (RT_FAILURE(rc2))
continue;
LogFlowFunc(("\tLUN#%RU8: cbWritten=%RU32, cWrittenMin=%RU32\n", pDrv->uLUN, cbWritten, cbWrittenMin));
}
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
LogFlowFunc(("\tcbToRead=%RU32, cbWrittenMin=%RU32, cbToWrite=%RU32, cbLeft=%RU32\n",
if (!cbWrittenMin)
{
break;
}
addr += cbWrittenMin;
}
if (RT_SUCCESS(rc))
{
if (!cbToWrite) /* All data written? */
{
if (cbToRead < 4)
{
}
else
}
}
return rc;
}
{
{
{
unsigned int i;
}
else
}
while (cbElapsed)
{
while (cbToWrite)
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
if (RT_FAILURE(rc2))
continue;
}
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
if (!cbWrittenMin)
return;
}
}
}
{
int rc;
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/* Select audio sink to process. */
if (!cbToRead)
{
*pcbRead = 0;
return VINF_EOF;
}
if (pvMixBuf)
{
if ( RT_SUCCESS(rc)
&& cbRead)
{
}
}
else
rc = VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
}
return rc;
#else
rc = VINF_SUCCESS;
int to_copy = 0;
if (!temp)
{
*pcbRead = 0;
return VINF_EOF;
}
while (temp)
{
int acquired;
if (!acquired)
{
break;
}
}
if (RT_SUCCESS(rc))
return rc;
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
{
if (RT_SUCCESS(rc))
{
#ifdef DEBUG_TIMER
#endif
if (cSamplesLive)
{
#ifdef DEBUG_TIMER
if (RT_SUCCESS(rc2))
LogFlowFunc(("LUN#%RU8: cSamplesLive=%RU32, cSamplesPlayed=%RU32\n",
#endif
if (cSamplesPlayed)
{
#ifdef DEBUG_TIMER
if (RT_SUCCESS(rc))
#endif
}
}
}
}
#ifdef DEBUG_TIMER
#endif
if (cbOutMin == UINT32_MAX)
cbOutMin = 0;
/*
* Playback.
*/
if (cbOutMin)
{
}
/*
* Recording.
*/
if (cbInMax)
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
{
{
{
switch (index)
{
case PO_INDEX:
break;
default:
break;
}
}
return VINF_SUCCESS;
}
while (cbElapsed >> 1)
{
{
LogFlowFunc(("Invalid buffer descriptor, fetching next one ...\n"));
}
{
LogFlowFunc(("Fresh buffer descriptor %RU8 is empty, addr=%#x, len=%#x, skipping\n",
{
break;
}
continue;
}
switch (index)
{
case PO_INDEX:
{
if ( RT_SUCCESS(rc)
&& cbTransferred)
{
}
break;
}
case PI_INDEX:
case MC_INDEX:
{
if ( RT_SUCCESS(rc)
&& cbTransferred)
{
}
break;
}
default:
break;
}
{
{
}
{
}
else
{
}
}
if ( RT_FAILURE(rc)
{
break;
}
}
return rc;
}
#ifndef VBOX_WITH_PDM_AUDIO_DRIVER
{
}
{
}
{
}
#endif
/**
* @callback_method_impl{FNIOMIOPORTIN}
*/
static DECLCALLBACK(int) ichac97IOPortNABMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 1:
{
*pu32 = ~0U;
switch (index)
{
case CAS:
/* Codec Access Semaphore Register */
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:
{
*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:
{
*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 */
LogFlowFunc(("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM(index), *pu32, pReg->picb, pReg->piv, pReg->cr));
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;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT}
*/
static DECLCALLBACK(int) ichac97IOPortNABMWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
{
switch (index)
{
case PI_LVI:
case PO_LVI:
case MC_LVI:
/* Last Valid Index */
{
}
break;
case PI_CR:
case PO_CR:
case MC_CR:
/* Control Register */
else
{
{
}
else
{
}
}
break;
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register */
break;
default:
break;
}
break;
}
case 2:
{
switch (index)
{
case PI_SR:
case PO_SR:
case MC_SR:
/* Status Register */
break;
default:
break;
}
break;
}
case 4:
{
switch (index)
{
case PI_BDBAR:
case PO_BDBAR:
case MC_BDBAR:
/* Buffer Descriptor list Base Address Register */
break;
case GLOB_CNT:
/* Global Control */
break;
case GLOB_STA:
/* Global Status */
break;
default:
break;
}
break;
}
default:
break;
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN}
*/
static DECLCALLBACK(int) ichac97IOPortNAMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 1:
{
*pu32 = ~0U;
break;
}
case 2:
{
*pu32 = ~0U;
switch (index)
{
default:
break;
}
break;
}
case 4:
{
*pu32 = ~0U;
break;
}
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT}
*/
{
switch (cb)
{
case 1:
{
break;
}
case 2:
{
switch (index)
{
case AC97_Reset:
break;
case AC97_Powerdown_Ctrl_Stat:
u32 &= ~0xf;
break;
#ifdef USE_MIXER
case AC97_Master_Volume_Mute:
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
break;
case AC97_PCM_Out_Volume_Mute:
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
break;
case AC97_Line_In_Volume_Mute:
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
break;
case AC97_Record_Select:
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
break;
case AC97_MIC_ADC_Rate:
{
}
else
break;
case AC97_PCM_LR_ADC_Rate:
{
}
else
break;
default:
break;
}
break;
}
case 4:
{
break;
}
default:
break;
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNPCIIOREGIONMAP}
*/
static DECLCALLBACK(int) ichac97IOPortMap(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb,
{
int rc;
if (iRegion == 0)
else
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
#ifdef IN_RING3
/**
* @callback_method_impl{FNSSMDEVSAVEEXEC}
*/
{
{
}
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
}
#else
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNSSMDEVLOADEXEC}
*/
static DECLCALLBACK(int) ichac97LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
AssertMsgReturn (uVersion == AC97_SSM_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
{
}
#ifdef USE_MIXER
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# else
# endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#endif /* USE_MIXER */
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*
* @remarks 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.
*/
}
/**
* @interface_method_impl{PDMDEVREG,pfnDestruct}
*/
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
}
{
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
if (pThis->pvReadWriteBuf)
{
pThis->cbReadWriteBuf = 0;
}
return VINF_SUCCESS;
}
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/**
* Attach command.
*
* This is called to let the device attach to a driver for a specified LUN
* during runtime. This is not called during VM construction, the device
* constructor have to attach to all the available drivers.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param uLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
{
("AC'97 device does not support hotplugging\n"),
/*
* Attach driver.
*/
("Not enough memory for AC'97 driver port description of LUN #%u\n", uLUN),
if (RT_SUCCESS(rc))
{
if (pDrv)
{
("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n",
/*
* For now we always set the driver at LUN 0 as our primary
* host backend. This might change in the future.
*/
/* Attach to driver list. */
}
else
rc = VERR_NO_MEMORY;
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
}
else if (RT_FAILURE(rc))
AssertMsgFailed(("Failed to attach AC'97 LUN #%u (\"%s\"), rc=%Rrc\n",
return rc;
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/* NB: This must be done *before* any possible failure (and running the destructor). */
#endif
/*
* Validations.
*/
N_("Invalid configuration for the AC'97 device"));
/*
* Determine the chip type.
*/
if (RT_FAILURE(rc))
N_("AC'97 configuration error: Querying \"Type\" as string failed"));
/*
* The AD1980 codec (with corresponding PCI subsystem vendor ID) is whitelisted
* in the Linux kernel; Linux makes no attempt to measure the data rate and assumes
* 48 kHz rate, which is exactly what we need.
*/
bool fChipAD1980 = false;
fChipAD1980 = false;
fChipAD1980 = true;
else
{
N_("AC'97 configuration error: The \"Type\" value \"%s\" is unsupported"),
szType);
}
/*
* Initialize data (most of it anyway).
*/
/* IBase */
/* PCI Device (the assertions will be removed later) */
PCIDevSetVendorId (&pThis->PciDev, 0x8086); /* 00 ro - intel. */ Assert(pThis->PciDev.config[0x00] == 0x86); Assert(pThis->PciDev.config[0x01] == 0x80);
PCIDevSetDeviceId (&pThis->PciDev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */ Assert(pThis->PciDev.config[0x02] == 0x15); Assert(pThis->PciDev.config[0x03] == 0x24);
PCIDevSetCommand (&pThis->PciDev, 0x0000); /* 04 rw,ro - pcicmd. */ Assert(pThis->PciDev.config[0x04] == 0x00); Assert(pThis->PciDev.config[0x05] == 0x00);
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);
PCIDevSetRevisionId (&pThis->PciDev, 0x01); /* 08 ro - rid. */ Assert(pThis->PciDev.config[0x08] == 0x01);
PCIDevSetClassProg (&pThis->PciDev, 0x00); /* 09 ro - pi. */ Assert(pThis->PciDev.config[0x09] == 0x00);
PCIDevSetClassSub (&pThis->PciDev, 0x01); /* 0a ro - scc; 01 == Audio. */ Assert(pThis->PciDev.config[0x0a] == 0x01);
PCIDevSetClassBase (&pThis->PciDev, 0x04); /* 0b ro - bcc; 04 == multimedia. */ Assert(pThis->PciDev.config[0x0b] == 0x04);
PCIDevSetHeaderType (&pThis->PciDev, 0x00); /* 0e ro - headtyp. */ Assert(pThis->PciDev.config[0x0e] == 0x00);
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);
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);
PCIDevSetInterruptLine (&pThis->PciDev, 0x00); /* 3c rw. */ Assert(pThis->PciDev.config[0x3c] == 0x00);
PCIDevSetInterruptPin (&pThis->PciDev, 0x01); /* 3d ro - INTA#. */ Assert(pThis->PciDev.config[0x3d] == 0x01);
if (fChipAD1980)
{
}
else
{
}
/*
* 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.
*/
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
if (RT_FAILURE(rc))
{
if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
rc = VINF_SUCCESS;
break;
}
uLUN++;
}
#else
if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
LogFunc(("ac97: No attached driver!\n"));
else if (RT_FAILURE(rc))
{
return rc;
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#ifndef VBOX_WITH_PDM_AUDIO_DRIVER
#endif
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
uLUN = 0;
{
uLUN++;
}
{
/*
* Only primary drivers are critical for the VM to run. Everything else
* might not worth showing an own error message box in the GUI.
*/
continue;
{
LogRel(("AC97: Falling back to NULL driver\n"));
/* Was not able initialize *any* stream.
* Select the NULL audio driver instead. */
N_("No audio devices could be opened. Selecting the NULL audio backend "
"with the consequence that no sound is audible"));
}
{
sizeof(szMissingStreams), "PCM Input");
N_("Some AC'97 audio streams (%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"), szMissingStreams);
}
}
#else
LogRel(("AC97: WARNING: Unable to open PCM IN!\n"));
LogRel(("AC97: WARNING: Unable to open PCM MC!\n"));
LogRel(("AC97: WARNING: Unable to open PCM OUT!\n"));
{
N_("No audio devices could be opened. Selecting the NULL audio backend "
"with the consequence that no sound is audible"));
}
{
len += RTStrPrintf(szMissingVoices + len, sizeof(szMissingVoices) - len, len ? ", PCM_out" : "PCM_out");
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 /* VBOX_WITH_PDM_AUDIO_DRIVER */
if (RT_SUCCESS(rc))
{
if (!pThis->pvReadWriteBuf)
rc = VERR_NO_MEMORY;
}
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
if (RT_SUCCESS(rc))
{
/* Start the emulation timer. */
if (RT_SUCCESS(rc))
{
/* Fire off timer. */
}
}
# ifdef VBOX_WITH_STATISTICS
if (RT_SUCCESS(rc))
{
/*
* Register statistics.
*/
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "/Devices/AC97/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ichac97Timer.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, "/Devices/AC97/BytesRead" , STAMUNIT_BYTES, "Bytes read from AC97 emulation.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, "/Devices/AC97/BytesWritten", STAMUNIT_BYTES, "Bytes written to AC97 emulation.");
}
# endif
#endif
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
{
/* u32Version */
/* szName */
"ichac97",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"ICH AC'97 Audio Controller",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(AC97STATE),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* !IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */