/*
 * File: HdaCodecDump.c
 *
 * Copyright (c) 2018 John Davis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "HdaCodecDump.h"
#include "StateGenerator.h"
#include "AmlGenerator.h"

CONST CHAR8 *gWidgetNames[HDA_WIDGET_TYPE_VENDOR + 1] = {
	"Audio Output", "Audio Input", "Audio Mixer",
	"Audio Selector", "Pin Complex", "Power Widget",
	"Volume Knob Widget", "Beep Generator Widget",
	"Reserved", "Reserved", "Reserved", "Reserved",
	"Reserved", "Reserved", "Reserved",
	"Vendor Defined Widget" };
CONST CHAR8 *gPortConnectivities[4] = { "Jack", "None", "Fixed", "Int Jack" };
CONST CHAR8 *gDefaultDevices[HDA_CONFIG_DEFAULT_DEVICE_OTHER + 1] = {
	"Line Out", "Speaker", "HP Out", "CD", "SPDIF Out",
	"Digital Out", "Modem Line", "Modem Handset", "Line In", "Aux",
	"Mic", "Telephone", "SPDIF In", "Digital In", "Reserved", "Other" };
CONST CHAR8 *gSurfaces[4] = { "Ext", "Int", "Ext", "Other" };
CONST CHAR8 *gLocations[0xF + 1] = {
	"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", "Special",
	"Special", "Special", "Reserved", "Reserved", "Reserved", "Reserved" };
CONST CHAR8 *gConnTypes[HDA_CONFIG_DEFAULT_CONN_OTHER + 1] = {
	"Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical", "Digital",
	"Analog", "Multi", "XLR", "RJ11", "Combo", "Other", "Other", "Other", "Other" };
CONST CHAR8 *gColors[HDA_CONFIG_DEFAULT_COLOR_OTHER + 1] = {
	"Unknown", "Black", "Grey", "Blue", "Green", "Red", "Orange",
	"Yellow", "Purple", "Pink", "Reserved", "Reserved", "Reserved",
	"Reserved", "White", "Other" };	

#define HDC_ID        { 'H','D','C','O' }
#define HdaLog(n, ...)	MemLog(FALSE, 0, n, ##__VA_ARGS__)

CONST CHAR8  hdcID[4]       = HDC_ID;

extern UINTN					AudioNum;
extern HDA_OUTPUTS				AudioList[20];
extern EFI_AUDIO_IO_PROTOCOL	*AudioIo;
extern CHAR16           		*OEMPath;

VOID
EFIAPI
HdaCodecDumpPrintRatesFormats(
    IN UINT32 Rates,
    IN UINT32 Formats) {
    // Print sample rates.
    HdaLog("    rates [0x%04X]:", (UINT16)Rates);
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8KHZ)
        HdaLog(" 8000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_11KHZ)
        HdaLog(" 11025");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16KHZ)
        HdaLog(" 16000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_22KHZ)
        HdaLog(" 22050");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32KHZ)
        HdaLog(" 32000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_44KHZ)
        HdaLog(" 44100");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_48KHZ)
        HdaLog(" 48000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_88KHZ)
        HdaLog(" 88200");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_96KHZ)
        HdaLog(" 96000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_176KHZ)
        HdaLog(" 176400");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_192KHZ)
        HdaLog(" 192000");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_384KHZ)
        HdaLog(" 384000");
    HdaLog("\n");

    // Print bits.
    HdaLog("    bits [0x%04X]:", (UINT16)(Rates >> 16));
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_8BIT)
        HdaLog(" 8");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_16BIT)
        HdaLog(" 16");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_20BIT)
        HdaLog(" 20");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_24BIT)
        HdaLog(" 24");
    if (Rates & HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES_32BIT)
        HdaLog(" 32");
    HdaLog("\n");

    // Print formats.
    HdaLog("    formats [0x%08X]:", Formats);
    if (Formats & HDA_PARAMETER_SUPPORTED_STREAM_FORMATS_PCM)
        HdaLog(" PCM");
    if (Formats & HDA_PARAMETER_SUPPORTED_STREAM_FORMATS_FLOAT32)
        HdaLog(" FLOAT32");
    if (Formats & HDA_PARAMETER_SUPPORTED_STREAM_FORMATS_AC3)
        HdaLog(" AC3");
    HdaLog("\n");
}

VOID
EFIAPI
HdaCodecDumpPrintAmpCaps(
    IN UINT32 AmpCaps) {
    if (AmpCaps) {
        HdaLog("ofs=0x%02X, nsteps=0x%02X, stepsize=%02X, mute=%u\n",
            HDA_PARAMETER_AMP_CAPS_OFFSET(AmpCaps), HDA_PARAMETER_AMP_CAPS_NUM_STEPS(AmpCaps),
            HDA_PARAMETER_AMP_CAPS_STEP_SIZE(AmpCaps), (AmpCaps & HDA_PARAMETER_AMP_CAPS_MUTE) != 0);
    } else
        HdaLog("N/A\n");
}

VOID
EFIAPI
HdaCodecDumpPrintWidgets(
	IN EFI_HDA_IO_PROTOCOL *HdaIo,
    //IN HDA_WIDGET *Widgets,
    IN HDA_WIDGET_DEV *Widgets,
    IN UINTN WidgetCount) {
    // Print each widget.
    for (UINTN w = 0; w < WidgetCount; w++) {    
        // Print header and capabilities.
        HdaLog("Node 0x%02X [%a] wcaps 0x%08X:", Widgets[w].NodeId,
            gWidgetNames[HDA_PARAMETER_WIDGET_CAPS_TYPE(Widgets[w].Capabilities)], Widgets[w].Capabilities);
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_STEREO)
            HdaLog(" Stereo");
        else
            HdaLog(" Mono");
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_DIGITAL)
            HdaLog(" Digital");
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP)
            HdaLog(" Amp-In");
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_OUT_AMP)
            HdaLog(" Amp-Out");
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_L_R_SWAP)
            HdaLog(" R/L");
        HdaLog("\n");

        // Print input amp info.
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP) {
            // Print caps.
            HdaLog("  Amp-In caps: ");
            HdaCodecDumpPrintAmpCaps(Widgets[w].AmpInCapabilities);

            // Print default values.
            HdaLog("  Amp-In vals:");
            for (UINT8 i = 0; i < HDA_PARAMETER_CONN_LIST_LENGTH_LEN(Widgets[w].ConnectionListLength); i++) {
                if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_STEREO)
                    HdaLog(" [0x%02X 0x%02X]", Widgets[w].AmpInLeftDefaultGainMute[i], Widgets[w].AmpInRightDefaultGainMute[i]);
                else
                    HdaLog(" [0x%02X]", Widgets[w].AmpInLeftDefaultGainMute[i]);
            }
            HdaLog("\n");
        }

        // Print output amp info.
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_OUT_AMP) {
            // Print caps.
            HdaLog("  Amp-Out caps: ");
            HdaCodecDumpPrintAmpCaps(Widgets[w].AmpOutCapabilities);

            // Print default values.
            HdaLog("  Amp-Out vals:");
            if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_STEREO)
                HdaLog(" [0x%02X 0x%02X]\n", Widgets[w].AmpOutLeftDefaultGainMute, Widgets[w].AmpOutRightDefaultGainMute);
            else
                HdaLog(" [0x%02X]\n", Widgets[w].AmpOutLeftDefaultGainMute);
        }

        // Print pin complexe info.
        if (HDA_PARAMETER_WIDGET_CAPS_TYPE(Widgets[w].Capabilities) == HDA_WIDGET_TYPE_PIN_COMPLEX) {
            // Print pin capabilities.
            HdaLog("  Pincap 0x%08X:", Widgets[w].PinCapabilities);
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_INPUT)
                HdaLog(" IN");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_OUTPUT)
                HdaLog(" OUT");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_HEADPHONE)
                HdaLog(" HP");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD)
                HdaLog(" EAPD");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_TRIGGER)
                HdaLog(" Trigger");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_PRESENCE)
                HdaLog(" Detect");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_HBR)
                HdaLog(" HBR");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_HDMI)
                HdaLog(" HDMI");
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_DISPLAYPORT)
                HdaLog(" DP");
            HdaLog("\n");

            // Print EAPD info.
            if (Widgets[w].PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD) {
                HdaLog("  EAPD 0x%X:", Widgets[w].DefaultEapd);
                if (Widgets[w].DefaultEapd & HDA_EAPD_BTL_ENABLE_BTL)
                    HdaLog(" BTL");
                if (Widgets[w].DefaultEapd & HDA_EAPD_BTL_ENABLE_EAPD)
                    HdaLog(" EAPD");
                if (Widgets[w].DefaultEapd & HDA_EAPD_BTL_ENABLE_L_R_SWAP)
                    HdaLog(" R/L");
                HdaLog("\n");
            }

            // Print pin default header.
            HdaLog("  Pin Default 0x%08X: [%a] %a at %a %a\n", Widgets[w].DefaultConfiguration,
                gPortConnectivities[HDA_VERB_GET_CONFIGURATION_DEFAULT_PORT_CONN(Widgets[w].DefaultConfiguration)],
                gDefaultDevices[HDA_VERB_GET_CONFIGURATION_DEFAULT_DEVICE(Widgets[w].DefaultConfiguration)],
                gSurfaces[HDA_VERB_GET_CONFIGURATION_DEFAULT_SURF(Widgets[w].DefaultConfiguration)],
                gLocations[HDA_VERB_GET_CONFIGURATION_DEFAULT_LOC(Widgets[w].DefaultConfiguration)]);

            // Print connection type and color.
            HdaLog("    Conn = %a, Color = %a\n",
                gConnTypes[HDA_VERB_GET_CONFIGURATION_DEFAULT_CONN_TYPE(Widgets[w].DefaultConfiguration)],
                gColors[HDA_VERB_GET_CONFIGURATION_DEFAULT_COLOR(Widgets[w].DefaultConfiguration)]);

            // Print default association and sequence.
            HdaLog("    DefAssociation = 0x%1X, Sequence = 0x%1X\n",
                HDA_VERB_GET_CONFIGURATION_DEFAULT_ASSOCIATION(Widgets[w].DefaultConfiguration),
                HDA_VERB_GET_CONFIGURATION_DEFAULT_SEQUENCE(Widgets[w].DefaultConfiguration));

            // Print default pin control.
            HdaLog("  Pin-ctls: 0x%02X:", Widgets[w].DefaultPinControl);
            if (Widgets[w].DefaultPinControl & HDA_PIN_WIDGET_CONTROL_VREF_EN)
                HdaLog(" VREF");
            if (Widgets[w].DefaultPinControl & HDA_PIN_WIDGET_CONTROL_IN_EN)
                HdaLog(" IN");
            if (Widgets[w].DefaultPinControl & HDA_PIN_WIDGET_CONTROL_OUT_EN)
                HdaLog(" OUT");
            if (Widgets[w].DefaultPinControl & HDA_PIN_WIDGET_CONTROL_HP_EN)
                HdaLog(" HP");
            HdaLog("\n");
        }
        
        // Print connections.
        if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_CONN_LIST) {
            HdaLog("  Connection: %u\n    ", HDA_PARAMETER_CONN_LIST_LENGTH_LEN(Widgets[w].ConnectionListLength));
            for (UINT8 c = 0; c < HDA_PARAMETER_CONN_LIST_LENGTH_LEN(Widgets[w].ConnectionListLength); c++) {
                HdaLog(" 0x%02X", Widgets[w].Connections[c]);
                //if (c == Widgets[w].ConnectionSelect)
                //	HdaLog("*");
            }
            HdaLog("\n");
        }
    }
}

EFI_STATUS SaveHdaDumpTxt() {
    EFI_STATUS Status = EFI_NOT_FOUND;
	AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
   	HDA_CODEC_DEV *HdaCodecDev;
    EFI_HDA_CODEC_INFO_PROTOCOL *HdaCodecInfo;
   	HDA_FUNC_GROUP *AudioFuncGroup;
   	EFI_HDA_IO_PROTOCOL *HdaIo;
	CHAR16 *MiscPath = PoolPrint(L"%s\\misc", OEMPath);
	CHAR8 *MemLogStart;
	UINTN MemLogStartLen;

	// Print each codec found.
	for (UINTN i = 0; i < AudioNum; i++) {
		MemLogStartLen = GetMemLogLen();
		MemLogStart = GetMemLogBuffer() + MemLogStartLen;
		
	    HdaLog("HdaCodecDump Start\n");
	
		Status = gBS->HandleProtocol(AudioList[i].Handle, &gEfiAudioIoProtocolGuid, (VOID**)&AudioIo);
		
		if (EFI_ERROR(Status))
			continue;
	
		AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(AudioIo);

		if (!AudioIoPrivateData || !AudioIoPrivateData->HdaCodecDev || 
			!AudioIoPrivateData->HdaCodecDev->HdaIo || !AudioIoPrivateData->HdaCodecDev->AudioFuncGroup)
			continue;
		
		HdaCodecDev = AudioIoPrivateData->HdaCodecDev;
		HdaCodecInfo = &HdaCodecDev->HdaCodecInfoData->HdaCodecInfo;
		AudioFuncGroup = HdaCodecDev->AudioFuncGroup;
		HdaIo = HdaCodecDev->HdaIo;
	
		// Get name.
		HdaLog("Codec: %s\n", HdaCodecDev->Name);
		
		// Get address
		UINT8 CodecAddress;
		Status = HdaIo->GetAddress(HdaIo, &CodecAddress);
		HdaLog("Address: %u\n", CodecAddress);
		
		// Get AFG ID.
		HdaLog("AFG Function Id: 0x%1X (unsol %u)\n", AudioFuncGroup->NodeId, AudioFuncGroup->UnsolCapable);

		HdaLog("Vendor ID: 0x%08X\n", HdaCodecDev->VendorId);
		HdaLog("Revision ID: 0x%08X\n", HdaCodecDev->RevisionId);

		// Get supported rates/formats.
		if ((AudioFuncGroup->SupportedPcmRates != 0) || (AudioFuncGroup->SupportedFormats != 0)) {
			HdaLog("Default PCM:\n");
			HdaCodecDumpPrintRatesFormats(AudioFuncGroup->SupportedPcmRates, AudioFuncGroup->SupportedFormats);
		} else
			HdaLog("Default PCM: N/A\n");

		// Get default amp caps.
		HdaLog("Default Amp-In caps: ");
		HdaCodecDumpPrintAmpCaps(AudioFuncGroup->AmpInCapabilities);
		HdaLog("Default Amp-Out caps: ");
		HdaCodecDumpPrintAmpCaps(AudioFuncGroup->AmpOutCapabilities);
		
		// Get widgets.
		HDA_WIDGET_DEV *Widgets = AudioFuncGroup->Widgets;
		UINTN WidgetCount = AudioFuncGroup->WidgetsCount;
		HdaCodecDumpPrintWidgets(HdaIo, Widgets, WidgetCount);
		
		CHAR16 *PathHdaDump = PoolPrint(L"%s\\HdaCodec#%d (%s).txt", MiscPath, i, HdaCodecDev->Name);

		if (PathHdaDump != NULL) {
			Status = egSaveFile(SelfRootDir, PathHdaDump, (VOID *)MemLogStart, GetMemLogLen() - MemLogStartLen);

			if (EFI_ERROR(Status))
				Status = egSaveFile(NULL, PathHdaDump, (VOID *)MemLogStart, GetMemLogLen() - MemLogStartLen);

			FreePool(PathHdaDump);
		}
	}

	FreePool(MiscPath);
        
    return Status;
}

EFI_STATUS SaveHdaDumpBin() {
	EFI_STATUS Status = EFI_NOT_FOUND;
	AUDIO_IO_PRIVATE_DATA *AudioIoPrivateData;
   	HDA_CODEC_DEV *HdaCodecDev;
    EFI_HDA_CODEC_INFO_PROTOCOL *HdaCodecInfo;
   	HDA_FUNC_GROUP *AudioFuncGroup;
   	EFI_HDA_IO_PROTOCOL *HdaIo;
    CHAR16 *MiscPath = PoolPrint(L"%s\\misc", OEMPath);
	
	if (MiscPath == NULL)
		return EFI_OUT_OF_RESOURCES;
    
	for (UINTN i = 0; i < AudioNum; i++) {
		HDA_WIDGET_DEV *Widgets;
		UINTN WidgetCount;
		UINT32 HdaCodecDataSize;
		HdaCodec HdaCodec;
		UINT8 CodecAddress;
		UINT8 *HdaCodecData;
		UINT8 *HdaCodecDataPtr;
		CHAR16 *PathHdaDump;
		
		Status = gBS->HandleProtocol(AudioList[i].Handle, &gEfiAudioIoProtocolGuid, (VOID**)&AudioIo);
		
		if (EFI_ERROR(Status))
			continue;
	
		AudioIoPrivateData = AUDIO_IO_PRIVATE_DATA_FROM_THIS(AudioIo);

		if (!AudioIoPrivateData || !AudioIoPrivateData->HdaCodecDev || !AudioIoPrivateData->HdaCodecDev->HdaIo)
			continue;

		HdaCodecDev = AudioIoPrivateData->HdaCodecDev;
		if(!HdaCodecDev || !HdaCodecDev->AudioFuncGroup)
			continue;
		
		HdaCodecInfo = &HdaCodecDev->HdaCodecInfoData->HdaCodecInfo;
		AudioFuncGroup = HdaCodecDev->AudioFuncGroup;
		HdaIo = HdaCodecDev->HdaIo;
		
		Widgets = AudioFuncGroup->Widgets;
		WidgetCount = AudioFuncGroup->WidgetsCount;
		HdaCodecDataSize = (UINT32)(sizeof(HdaCodec) + (WidgetCount * sizeof(HdaWidget)));
		
		// Copy codec data		
		ZeroMem(&HdaCodec, sizeof(HdaCodec));
		
		if(!EFI_ERROR(Status)) {
			for (UINT32 j = 0; j < HDA_MAX_NAMELEN; j++) {
				HdaCodec.Name[j] = (CHAR8)(HdaCodecDev->Name[j] & 0xFF);
				if (HdaCodecDev->Name[j] == 0x0000)
					break;
			}
		}
		
		Status = HdaIo->GetAddress(HdaIo, &CodecAddress);
		
		CopyMem(HdaCodec.Header, hdcID, 4);
		
		HdaCodec.CodecAddress = CodecAddress;
		HdaCodec.AudioFuncId = AudioFuncGroup->NodeId;
		HdaCodec.Unsol = AudioFuncGroup->UnsolCapable;
		HdaCodec.VendorId = HdaCodecDev->VendorId;
		HdaCodec.RevisionId = HdaCodecDev->RevisionId;
		HdaCodec.Rates = AudioFuncGroup->SupportedPcmRates;
		HdaCodec.Formats = AudioFuncGroup->SupportedFormats;
		HdaCodec.AmpInCaps = AudioFuncGroup->AmpInCapabilities;
		HdaCodec.AmpOutCaps = AudioFuncGroup->AmpOutCapabilities;
		HdaCodec.WidgetCount = AudioFuncGroup->WidgetsCount;
		
		// Allocate space for codec data
		HdaCodecData = AllocateZeroPool(HdaCodecDataSize);
		HdaCodecDataPtr = HdaCodecData;
	
		if (!HdaCodecData)
			continue;
			
		CopyMem(HdaCodecDataPtr, &HdaCodec, sizeof(HdaCodec));
		HdaCodecDataPtr += sizeof(HdaCodec);

		for (UINT32 w = 0; w < WidgetCount; w++) {
			UINTN ConnectionListLength;
			HdaWidget HdaWidget;
			
			ConnectionListLength = HDA_PARAMETER_CONN_LIST_LENGTH_LEN(Widgets[w].ConnectionListLength);
			
			ZeroMem(&HdaWidget, sizeof(HdaWidget));
						
			HdaWidget.NodeId = Widgets[w].NodeId;
			HdaWidget.Type = Widgets[w].Type;
			HdaWidget.Capabilities = Widgets[w].Capabilities;
			HdaWidget.DefaultUnSol = Widgets[w].DefaultUnSol;
			HdaWidget.ConnectionListLength = Widgets[w].ConnectionListLength;
			HdaWidget.ConnectionSelect = 0;
			HdaWidget.SupportedPowerStates = Widgets[w].SupportedPowerStates;
			HdaWidget.DefaultPowerState = Widgets[w].DefaultPowerState;
			HdaWidget.AmpOverride = Widgets[w].AmpOverride;
			HdaWidget.AmpInCapabilities = Widgets[w].AmpInCapabilities;
			HdaWidget.AmpOutCapabilities = Widgets[w].AmpOutCapabilities;
			HdaWidget.AmpOutLeftDefaultGainMute = Widgets[w].AmpOutLeftDefaultGainMute;
			HdaWidget.AmpOutRightDefaultGainMute = Widgets[w].AmpOutRightDefaultGainMute;
			HdaWidget.SupportedPcmRates = Widgets[w].SupportedPcmRates;
			HdaWidget.SupportedFormats = Widgets[w].SupportedFormats;
			HdaWidget.DefaultConvFormat = Widgets[w].DefaultConvFormat;
			HdaWidget.DefaultConvStreamChannel = Widgets[w].DefaultConvStreamChannel;
			HdaWidget.DefaultConvChannelCount = Widgets[w].DefaultConvChannelCount;
			HdaWidget.PinCapabilities = Widgets[w].PinCapabilities;
			HdaWidget.DefaultEapd = Widgets[w].DefaultEapd;
			HdaWidget.DefaultPinControl = Widgets[w].DefaultPinControl;
			HdaWidget.DefaultConfiguration = Widgets[w].DefaultConfiguration;
			HdaWidget.VolumeCapabilities = Widgets[w].VolumeCapabilities;
			HdaWidget.DefaultVolume = Widgets[w].DefaultVolume;
	
			for (UINT32 c = 0; c < ConnectionListLength; c++) {
				if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP) {
					HdaWidget.AmpInLeftDefaultGainMute[c] = Widgets[w].AmpInLeftDefaultGainMute[c];
					
					if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_STEREO) {
						HdaWidget.AmpInRightDefaultGainMute[c] = Widgets[w].AmpInRightDefaultGainMute[c];
					}
				}
				
				if (Widgets[w].Capabilities & HDA_PARAMETER_WIDGET_CAPS_CONN_LIST) {
					HdaWidget.Connections[c] = Widgets[w].Connections[c];
				}
			}
			
			CopyMem(HdaCodecDataPtr, &HdaWidget, sizeof(HdaWidget));
			HdaCodecDataPtr += sizeof(HdaWidget);
		}
	
		PathHdaDump = PoolPrint(L"%s\\HdaCodec#%d (%s).bin", MiscPath, i, HdaCodecDev->Name);

		if (PathHdaDump != NULL) {
			Status = egSaveFile(SelfRootDir, PathHdaDump, HdaCodecData, HdaCodecDataSize);

			if (EFI_ERROR(Status))
				Status = egSaveFile(NULL, PathHdaDump, HdaCodecData, HdaCodecDataSize);

			FreePool(PathHdaDump);
		}
	
		FreePool(HdaCodecData);
	}

	FreePool(MiscPath);
	
	return EFI_SUCCESS;
}
