DevIchAc97.cpp revision f9de02a9df6b89c81f9cfa3fecf268f188085e1b
/* $Id$ */
/** @file
* DevIchAc97 - VBox ICH AC97 Audio Controller.
*/
/*
* Copyright (C) 2006-2014 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
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifndef VBOX
//#define USE_MIXER
#else
# define USE_MIXER
#endif
#ifdef DEBUG
//#define DEBUG_LUN
# ifdef DEBUG_LUN
# define DEBUG_LUN_NUM 1
# endif
#endif /* DEBUG */
#define AC97_SSM_VERSION 1
#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 | \
/** @name 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,
};
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
};
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Buffer descriptor.
*/
typedef struct BD
{
} BD;
typedef struct AC97BusMasterRegs
{
int bd_valid; /**< initialized? */
/** Pointer to a AC97 bus master register. */
typedef AC97BusMasterRegs *PAC97BMREG;
#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. */
/**
* Struct for maintaining a host backend driver.
*/
typedef struct AC97STATE *PAC97STATE;
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. */
/** Number of samples to play (output), needed
* for the timer routine. */
} AC97DRIVER, *PAC97DRIVER;
#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 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. */
typedef AC97STATE *PAC97STATE;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
enum
{
};
enum { \
}
enum
{
PI_INDEX = 0, /* PCM in */
PO_INDEX, /* PCM out */
MC_INDEX, /* Mic in */
};
enum
{
GLOB_CNT = 0x2c,
GLOB_STA = 0x30,
CAS = 0x34
};
#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;
}
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 VBOX_WITH_PDM_AUDIO_DRIVER
#endif
#ifdef SOFT_VOLUME
if (index == AC97_Master_Volume_Mute)
{
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
pDrv->pConnector->pfnIsSetOutVolume(pDrv->pConnector, pDrv->Out.pStrmOut, RT_BOOL(mute), lvol, rvol);
# else
# endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
else
{
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
/** @todo In SetVolume no passing audmixerctl_in as its not used in DrvAudio.cpp. */
}
# else
# endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
#else /* !SOFT_VOLUME */
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# else
# endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
#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
{
}
{
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
/*
* Sigmatel 9700 (STAC9700)
*/
#ifdef USE_MIXER
ichac97RecordSelect(pThis, 0);
# ifdef VBOX_WITH_PDM_AUDIO_DRIVER
# else
# endif
#else
#endif
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
if (RT_SUCCESS(rc2))
{
/* Add all required audio sinks. */
&pThis->pSinkLineIn);
&pThis->pSinkMicIn);
}
#endif
/* Reset all streams. */
}
static int ichac97WriteAudio(PAC97STATE pThis, PAC97BMREG pReg, uint32_t cbMax, uint32_t *pcbWritten)
{
uint32_t cbWrittenTotal = 0;
if (!cbToWrite)
return VERR_NO_DATA;
int rc = VINF_SUCCESS;
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)
{
rc = VERR_NO_DATA;
break;
}
addr += cbWrittenMin;
}
if (RT_SUCCESS(rc))
{
if (!cbToWrite) /* All data written? */
{
if (cbToRead < 4)
{
}
else
}
}
if (RT_SUCCESS(rc))
{
if (pcbWritten)
}
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;
}
}
}
static int ichac97ReadAudio(PAC97STATE pThis, PAC97BMREG pReg, uint32_t cbMax, uint32_t *pcbWritten)
{
int rc = VINF_SUCCESS;
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
/* Select audio sink to process. */
if (!cbToRead)
return VERR_NO_DATA;
if (pvMixBuf)
{
if ( RT_SUCCESS(rc)
&& cbRead)
{
}
}
else
rc = VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
if (pcbWritten)
*pcbWritten = cbRead;
}
return rc;
#else
int to_copy = 0;
if (!temp)
return VERR_NO_DATA;
while (temp)
{
int acquired;
if (!acquired)
{
break;
}
}
if (RT_SUCCESS(rc))
{
if (pcbWritten)
*pcbWritten = nread;
}
return rc;
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
}
#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
{
int rc = VINF_SUCCESS;
{
continue;
if (RT_SUCCESS(rc))
{
}
else
pDrv->cSamplesLive = 0;
}
/*
* Playback.
*/
if (cbOutMin)
{
}
else
{
{
if (pDrv->cSamplesLive)
}
}
/*
* Recording.
*/
if (cbInMax)
}
#endif /* VBOX_WITH_PDM_AUDIO_DRIVER */
{
{
{
switch (index)
{
case PO_INDEX:
break;
default:
break;
}
}
return VINF_SUCCESS;
}
int rc;
uint32_t cbWrittenTotal = 0;
while ((cbElapsed >> 1))
{
{
LogFlowFunc(("Invalid buffer descriptor, fetching next one ...\n"));
}
{
LogFlowFunc(("Fresh buffer descriptor %d is empty, addr=%#x, len=%#x, skipping\n",
{
break;
}
continue;
}
switch (index)
{
case PO_INDEX:
{
if (RT_SUCCESS(rc))
{
}
break;
}
case PI_INDEX:
case MC_INDEX:
{
if (RT_SUCCESS(rc))
{
}
break;
}
default:
break;
}
{
{
}
{
rc = VERR_NO_DATA;
}
else
{
}
}
if (RT_FAILURE(rc))
{
if (rc == VERR_NO_DATA)
rc = VINF_SUCCESS;
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}
*/
{
/*
* Validations.
*/
N_("Invalid configuration for the AC97 device"));
/*
* 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);
PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x8086); /* 2c ro - intel.) */ Assert(pThis->PciDev.config[0x2c] == 0x86); Assert(pThis->PciDev.config[0x2d] == 0x80);
PCIDevSetSubSystemId (&pThis->PciDev, 0x0000); /* 2e ro. */ Assert(pThis->PciDev.config[0x2e] == 0x00); Assert(pThis->PciDev.config[0x2f] == 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);
/*
* 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
do
{
if (RT_FAILURE(rc))
{
if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
rc = VINF_SUCCESS;
break;
}
} while (0);
#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"));
}
{
char szMissingStreams[255];
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"));
}
{
char szMissingVoices[128];
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 hdaTimer.");
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.
*/
const PDMDEVREG g_DeviceICHAC97 =
{
/* 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 */