/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/audio.cpp $
 * $Revision: /main/ms-d/34 $
 *     $Date: 2014-05-08 10:31:43 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/audio.cpp$ 
 * 
 * /main/ms-d/34   2014-05-08 10:31:43   pmackent 
 * Move AT^SAIC to RIL initialization (IPIS100096235)
 *
 * /main/ms-d/33   2014-02-19 09:48:28   pmackent
 * Check /Temp directory for update files (IPIS100094915)
 *
 * /main/ms-d/32   2013-03-04 13:19:21   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/31   2013-02-27 15:18:08   pmackent
 * Introduce provider spezific PRL update
 *
 * /main/ms-d/30   2012-10-24 10:55:25   pmackent
 * Allow different audio configuration for CDMA and GSM
 *
 * /main/ms-d/29   2011-10-11 14:20:25   ctna0256
 * Improved version check
 *
 * /main/ms-d/28   2011-09-26 13:34:02   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/27   2011-09-21 10:38:13   ctna0256
 * Error indication for audio update (IPIS100074854)
 *
 * /main/ms-d/26   2011-09-09 14:17:39   ctna0256
 * New notifications for audio/firmware update (IPIS100074854)
 *
 * /main/ms-d/25   2011-07-21 09:38:50   ctna0256
 * Changes for new numbers of audio modes in PH8 Rel2 and timing issue during audio block update (IPIS100073840)
 *
 * /main/ms-d/24   2011-03-09 14:24:26   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/23   2010-12-15 10:43:45   ctna0256
 * Performed lint check
 *
 * /main/ms-d/22   2010-12-02 14:03:50   ctna0256
 * Added some comments
 *
 * /main/ms-d/21   2010-12-02 11:58:10   ctna0256
 * Make search pattern case insensitiv
 *
 * /main/ms-d/20   2010-11-26 10:26:16   ctna0256
 * Introducing firmware update for PH8
 *
 * /main/ms-d/19   2010-11-03 14:23:40   ctna0256
 * Change search order for audio profile file
 *
 * /main/ms-d/18   2010-11-03 11:59:02   ctna0256
 * SNFD is not necessary.
 *
 * /main/ms-d/17   2010-07-02 15:58:28   ctna0256
 * Introduce SAIC-settings and modified SNFO for PH8
 *
 * /main/ms-d/16   2009-09-04 14:01:32   ctna0256
 * Remove unnecessary  in SBNR command
 *
 * /main/ms-d/15   2009-09-04 11:51:08   ctna0256
 * Use mode 6 to check audio block version (IPIS100057286)
 *
 * /main/ms-d/14   2009-05-28 11:36:33   ctna0256
 * Remove "Siemens" from RIL
 *
 * /main/ms-d/13   2008-12-19 15:57:43   ctna0256
 * Make handset default audio mode
 *
 * /main/ms-d/12   2008-08-27 11:17:08   ctna0256
 * Removed some unnecessary debug prints
 *
 * /main/ms-d/11   2008-08-22 10:22:33   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/10   2008-08-21 09:33:52   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/9   2008-04-03 14:41:19   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/8   2007-11-21 13:34:57   mp49210
 * Lint compatibility
 *
 * /main/ms-d/7   2007-08-08 11:16:39   mp49210
 * Introduction of audio profile download
 *
 * /main/ms-d/6   2007-05-15 10:35:23   mp49210
 * Fixed bug reading SNFI audio parameters
 *
 * /main/ms-d/5   2007-02-09 15:15:32   mp49210
 * Lint compatibility
 *
 * /main/ms-d/4   2007-01-23 09:11:27   mp49210
 * Change audio version check
 *
 * /main/ms-d/3   2007-01-19 10:06:50   mp49210
 * New audio configuration registry structure
 *
 * /main/ms-d/2   2006-10-30 10:26:35   wm36264
 * Removed unused stuff
 *
 * /main/ms-d/1   2006-10-25 16:52:43   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"

// This option enable the RIL to search for configuration file not only in lower
// case, but also in upper case. This might be useful, if a system has got a
// case sensitive file system.
#define RIL_SEARCH_FILE_WITH_UPPER_CASE

static BOOL g_fAudioProfDownload = TRUE;

// The audio configuration (defaults)
AUDIO_CONFIG g_acAudioConfig[MAX_AUDIO_MODES] = {
    // RIL_AUDIO_NONE
    {
        3,                                      // Audio mode setting for modem
        0,                                      // dwSAICinterface
        0,                                      // dwSAICmicrophone
        0,                                      // dwSAICearpiece
        0,                                      // dwSAICclock
        0,                                      // dwSAICmsMode
        0,                                      // dwSAICframeMode
        0,                                      // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                       // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                       // dwExtMicAudioCodecVolSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFOcdcRxGainSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFOrxVolSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFOstGainSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                       // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                       // dwSNFImicAmp2Steps
        0, 0, 0, 0, 0, 0,                       // dwSNFImicTxVolSteps
        0, 0, 0, 0, 0, 0                        // dwSNFIinVolSteps
    },
    // RIL_AUDIO_HANDSET
    {
        3,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_SPEAKERPHONE
    {
        2,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_HEADSET
    {
        3,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_CARKIT
    {
        2,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_EXTENSION_BLUETOOTHHEADSET
    {
        3,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_EXTENSION_BLUETOOTHCARKIT
    {
        2,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_EXTENSION_GENERIC1
    {
        4,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        290, 290, 290, 290, 290, 290,                   // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    },
    // RIL_AUDIO_EXTENSION_GENERIC2
    {
        2,                                              // Audio mode setting for modem
        0,                                              // dwSAICinterface
        0,                                              // dwSAICmicrophone
        0,                                              // dwSAICearpiece
        0,                                              // dwSAICclock
        0,                                              // dwSAICmsMode
        0,                                              // dwSAICframeMode
        0,                                              // dwSAICextClkMode
        0, 1, 2, 3, 4, 5,                               // dwExtAudioCodecVolSteps
        0, 1, 2, 3, 4, 5,                               // dwExtMicAudioCodecVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOcdcRxGainSteps
        0, 8, 16, 24, 32, 40,                           // dwSNFOrxVolSteps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFOstGainSteps
        88, 88, 88, 88, 88, 88,                         // dwSNFOoutVolSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFOsideToneSteps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp1Steps
        0, 0, 0, 0, 0, 0,                               // dwSNFImicAmp2Steps
        0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, // dwSNFImicTxVolSteps
        88, 88, 88, 88, 88, 88                          // dwSNFIinVolSteps
    }
};
AUDIO_CONFIG g_acAudioConfigCdma[MAX_AUDIO_MODES];

static int g_iAudioTable[MAX_AUDIO_MODES] = {  RIL_AUDIO_NONE,
                                               RIL_AUDIO_HANDSET,
                                               RIL_AUDIO_SPEAKERPHONE,
                                               RIL_AUDIO_HEADSET,
                                               RIL_AUDIO_CARKIT,
                                               RIL_AUDIO_EXTENSION_BLUETOOTHHEADSET,
                                               RIL_AUDIO_EXTENSION_BLUETOOTHCARKIT,
                                               RIL_AUDIO_EXTENSION_GENERIC1,
                                               RIL_AUDIO_EXTENSION_GENERIC2};


static const TCHAR *g_szAudioRegKeyTable[MAX_AUDIO_MODES] = { TEXT(""),      // No registry key for RIL_AUDIO_NONE
                        RIL_AUDIO_CONFIG_HANDSET,
                        RIL_AUDIO_CONFIG_SPEAKERPHONE,
                        RIL_AUDIO_CONFIG_HEADSET,
                        RIL_AUDIO_CONFIG_CARKIT,
                        RIL_AUDIO_CONFIG_EXTENSION_BLUETOOTHHEADSET,
                        RIL_AUDIO_CONFIG_EXTENSION_BLUETOOTHCARKIT,
                        RIL_AUDIO_CONFIG_EXTENSION_GENERIC1,
                        RIL_AUDIO_CONFIG_EXTENSION_GENERIC2};
static const TCHAR *g_szAudioRegKeyTableCdma[MAX_AUDIO_MODES] = { TEXT(""),      // No registry key for RIL_AUDIO_NONE
                        RIL_AUDIO_CONFIG_HANDSET_CDMA,
                        RIL_AUDIO_CONFIG_SPEAKERPHONE_CDMA,
                        RIL_AUDIO_CONFIG_HEADSET_CDMA,
                        RIL_AUDIO_CONFIG_CARKIT_CDMA,
                        RIL_AUDIO_CONFIG_EXTENSION_BLUETOOTHHEADSET_CDMA,
                        RIL_AUDIO_CONFIG_EXTENSION_BLUETOOTHCARKIT_CDMA,
                        RIL_AUDIO_CONFIG_EXTENSION_GENERIC1_CDMA,
                        RIL_AUDIO_CONFIG_EXTENSION_GENERIC2_CDMA};

// Default audio timings:
// AUDIO_UPDATE_TIMEOUT: Timeout for AT commands used for audio update
// AUDIO_UPDATE_DELAY: Delay for each byte of the audio blocks (needed for USB or other fast systems)
// AUDIO_UPDATE_INIT_DELAY: Delay before download of first block starts (needed, because the module is not immediately ready)
#define AUDIO_UPDATE_TIMEOUT    20000
#define AUDIO_UPDATE_DELAY      1
#define AUDIO_UPDATE_INIT_DELAY   2000

#define UNKNOWN_AUDIO_PROFILE  0xFFFFFFFF

int GetAudioTableIndex(DWORD dwAudioMode)
{
    int iIndex = 0;

    for (int i=0; i < MAX_AUDIO_MODES; i++)
    {
        if (g_iAudioTable[i] == (int)dwAudioMode)
        {
            iIndex = i;
            break;
        }
    }

    return iIndex;
}

AUDIO_CONFIG *GetAudioTableEntry(INT iDevice, CRilHandle *pHandle)
{
    AUDIO_CONFIG *pEntry = &g_acAudioConfig[0];

    if (iDevice >= 0 && iDevice < MAX_AUDIO_MODES) {
        BOOL fCDMA = FALSE;

        if (pHandle != NULL)
            fCDMA = (pHandle->GetCurrentTechnology() == TEC_MODE_CDMA);

        if (fCDMA)
            pEntry = &g_acAudioConfigCdma[iDevice];
        else
            pEntry = &g_acAudioConfig[iDevice];
    }
    return pEntry;
}

//
//
//
BOOL CRilHandle::ExParseSBNRResponse(const char *pszATResponse, UINT uiBufLen, PDWORD pdwVersion) const
{
    DWORD dwStartPos, dwEndPos;
    DWORD dwV1, dwV2, dwV3;

    *pdwVersion = (DWORD)0;

    dwStartPos = 0;
    if(!FindString(dwStartPos, pszATResponse, "^SBNR: ", uiBufLen, &dwEndPos))
    {
        return FALSE;
    }
    uiBufLen -= (dwEndPos-dwStartPos);
    dwStartPos = dwEndPos;
    dwV1 = (DWORD)atoi((char*)&pszATResponse[dwStartPos]);
    if(!FindString(dwStartPos, pszATResponse, ",", uiBufLen, &dwEndPos))
    {
        return FALSE;
    }
    uiBufLen -= (dwEndPos-dwStartPos);
    dwStartPos = dwEndPos;
    dwV2 = (DWORD)atoi((char*)&pszATResponse[dwStartPos]);
    if(!FindString(dwStartPos, pszATResponse, ",", uiBufLen, &dwEndPos))
    {
        return FALSE;
    }
    uiBufLen -= (dwEndPos-dwStartPos);
    dwStartPos = dwEndPos;
    dwV3 = (DWORD)atoi((char*)&pszATResponse[dwStartPos]);
    dwV1 &= 0xFF;
    dwV2 &= 0xFF;
    dwV3 &= 0xFF;
    *pdwVersion = dwV1<<16 | dwV2<<8 | dwV3;

    return TRUE;
}

//
// Searches for an audio parameter file. If found, this function returns file
// name and file version.
// The file name has to have the following format: aud_xxxx.prm. The
// "xxxx" stands for the audio file version (4 digits).
//
// Parameters:
// wszFileName [out]: if file found, name of the audio file (full path name)
// nLen [in]: size of wszFileName
// pdwVersionNr [out]: if file foun, version of the file (-1 if no file found)
//
// Return value: TRUE, if an audio file has been found
//
BOOL CRilHandle::GetAudioProfileFilename(WCHAR *wszFileName, UINT nLen, DWORD *pdwVersionNr) const
{
    BOOL fRet = FALSE;
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszTempFileName[MAX_PATH];

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszTempFileName, 0, sizeof(wszSearchPath));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, RIL_AUDIO_CONFIG_KEY, TEXT("DefaultAudioSearchPath"), wszSearchPath, MAX_PATH)) {
        wcscpy(wszSearchPath, L"\\Storage Card");
    }
    *pdwVersionNr = 0xFFFFFFFF;

    fRet = GetConfigFilename(wszTempFileName, MAX_PATH, L"aud*.prm", wszSearchPath);
    if (wszFileName != NULL)
        (void)wcsncpy(wszFileName, wszTempFileName, nLen);
    if (fRet)
    {
        WCHAR *pwszT;

        pwszT = wcsstr(wszTempFileName, L".prm");
        if (pwszT == NULL)
            pwszT = wcsstr(wszTempFileName, L".PRM");
        if (pwszT != NULL)
        {
            *pwszT = '\0';
            pwszT = wcsrchr(wszTempFileName, '_');
            if (pwszT != NULL && pwszT != wszTempFileName)
                *pdwVersionNr = (DWORD)_ttoi((pwszT+1));
        }

    }
    return fRet;
}

//
// Searches for a file with the given search pattern. First it will search in the given
// default path, then it also tries the \Windows directory. The function will return
// the name auf the first matching file found.
//
// Parameters:
// wszFileName [out]: On success name of the file found. Contains complete path.
// nLen [in]: size of wszFileName
// wszFilePattern [in]: search pattern (eg. "aud*.prm"), should be all lower case
// wszDefaultPath [in]: default search path
//
// Return value: TRUE, if file has been found.
//
BOOL CRilHandle::GetConfigFilename(WCHAR *wszFileName, UINT nLen, WCHAR *wszFilePattern, WCHAR *wszDefaultPath) const
{
    BOOL fRet = FALSE;
    WIN32_FIND_DATA FindFileData;
    HANDLE hFile;
    WCHAR wszSearchPattern[MAX_PATH];

    // 1st try: default path
    (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"%s\\%s", wszDefaultPath, wszFilePattern);
    hFile = FindFirstFile(wszSearchPattern, &FindFileData);
    if(hFile  != INVALID_HANDLE_VALUE)
    {
        if (wszFileName != NULL)
        {
            (void)wcsncpy(wszFileName,wszDefaultPath, nLen);
            (void)wcsncat(wszFileName,L"\\", nLen);
			(void)wcsncpy(wszDefaultPath,wszFileName, nLen);
            (void)wcsncat(wszFileName,FindFileData.cFileName, nLen);
        }
        FindClose(hFile);
        fRet = TRUE;
    }
#ifdef RIL_SEARCH_FILE_WITH_UPPER_CASE
    // 2nd try: default path, but with pattern in upper case.
    // This should not be necessary per default, since usually file names are
    // not case sensitive in Windows. For this reason this additional search
    // is only acivated as a compiler option.
    if (!fRet)
    {
        WCHAR wszFilePatternUpper[MAX_PATH];
        UINT iIdx;

        (void)wcsncpyz(wszFilePatternUpper, wszFilePattern, MAX_PATH);
        for (iIdx = 0; iIdx < wcslen(wszFilePatternUpper); iIdx++)
            wszFilePatternUpper[iIdx] = towupper(wszFilePatternUpper[iIdx]);
        (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"%s\\%s", wszDefaultPath, wszFilePatternUpper);
        hFile = FindFirstFile(wszSearchPattern, &FindFileData);
        if(hFile  != INVALID_HANDLE_VALUE)
        {
            if (wszFileName != NULL)
            {
                (void)wcsncpy(wszFileName,L"\\windows\\", nLen);
				(void)wcsncpy(wszDefaultPath,wszFileName, nLen);
                (void)wcsncat(wszFileName,FindFileData.cFileName, nLen);
            }
            FindClose(hFile);
            fRet = TRUE;
        }
    }
#endif
    // 3rd try: \temp directory.
    if (!fRet)
    {
        (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"\\temp\\%s", wszFilePattern);
        hFile = FindFirstFile(wszSearchPattern, &FindFileData);
        if(hFile  != INVALID_HANDLE_VALUE)
        {
            if (wszFileName != NULL)
            {
                (void)wcsncpy(wszFileName,L"\\temp\\", nLen);
				(void)wcsncpy(wszDefaultPath,wszFileName, nLen);
                (void)wcsncat(wszFileName,FindFileData.cFileName, nLen);
            }
            FindClose(hFile);
            fRet = TRUE;
        }
    }
    // 4th try: \window directory.
    if (!fRet)
    {
        (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"\\windows\\%s", wszFilePattern);
        hFile = FindFirstFile(wszSearchPattern, &FindFileData);
        if(hFile  != INVALID_HANDLE_VALUE)
        {
            if (wszFileName != NULL)
            {
                (void)wcsncpy(wszFileName,L"\\windows\\", nLen);
				(void)wcsncpy(wszDefaultPath,wszFileName, nLen);
                (void)wcsncat(wszFileName,FindFileData.cFileName, nLen);
            }
            FindClose(hFile);
            fRet = TRUE;
        }
    }
    return fRet;
}


DWORD g_dwRadioAudioProfilesVersion = 0;

//
// Check current version of audio blocks (if available).
// The RIL checks only the version of the audio block of the last audio mode (mode 6/8).
// This has the reason, that this mode is the last one to be updated during the audio
// block update procedure. If this update in interrupted for any reason, the version
// number of the last audio mode will still show the old version, so the audio block
// update can be started again.
//
BOOL CRilHandle::CheckAudioProfilesVersion(COM_PORT_TYPE PortType)
{
    DWORD dwProfilesDLLVersion;
    DWORD dwModuleDLLVersion;
    char szCmd[MAX_ATCMD_LEN];
    DWORD dwTemp = 0;
    char szResponse[MAX_DEFAULT_INIT_AT_RESPONSE];

	if(UNSUPPORTS_AUDIO)
	{
		RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Module doesn't support Audio!\r\n")));
		goto Error;
	}

    // Even when the audio parameter download is disabled with this key, the RIL will still
    // read the version number in order to report it with the RIL_Devspecific() API.
    g_fAudioProfDownload = TRUE;
    (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, RIL_AUDIO_CONFIG_KEY, RIL_AUDIO_CONFIG_DISABLE, &dwTemp);
    if(dwTemp == 1)
        g_fAudioProfDownload = FALSE;

    dwTemp = 0;
    (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, RIL_AUDIO_CONFIG_KEY, RIL_AUDIO_VERSION_CHECK_DISABLE, &dwTemp);
    if(dwTemp == 1)
        goto Error;

    // Adjust version to modem format
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SNFS=%d;^SBNR=\"AUD\"\r", NUMBER_OF_AUDIO_MODES);
    dwProfilesDLLVersion = 0;
    if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(PREMUX_TIMEOUTS_SBNR), 0))
    {
        goto Error;
    }
    g_dwRadioAudioProfilesVersion = UNKNOWN_AUDIO_PROFILE;
    if(!ExParseSBNRResponse(szResponse, sizeof(szResponse), &g_dwRadioAudioProfilesVersion))
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to get current audio profile version\r\n")));
    }

    if (!g_fAudioProfDownload)
    {
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Audio profile download is disabled\r\n")));
        goto Error;
    }

    // Per default the last number of the AT^SBNR resonse is not compared.
    dwModuleDLLVersion = ((g_dwRadioAudioProfilesVersion&0xFF00)>>8) + 100*((g_dwRadioAudioProfilesVersion&0xFF0000)>>16);
    // If there is no audio file, there is no need to make further checks.
    if (!GetAudioProfileFilename(NULL, 0, &dwProfilesDLLVersion))  {
        dwProfilesDLLVersion = UNKNOWN_AUDIO_PROFILE;
    }
    else {
        // 6-digit version number? Check all numbers from AT^SBNR.
        if (dwProfilesDLLVersion > 9999)
            dwModuleDLLVersion = (g_dwRadioAudioProfilesVersion&0xFF) + 100*((g_dwRadioAudioProfilesVersion&0xFF00)>>8) + 10000*((g_dwRadioAudioProfilesVersion&0xFF0000)>>16);
    }

    // Compare version number. An update is only performed, if the first two numbers of the SBNR version don't match.
    if(dwProfilesDLLVersion != UNKNOWN_AUDIO_PROFILE && dwModuleDLLVersion != dwProfilesDLLVersion)
    {
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Audio profiles do not match\r\n")));
        return TRUE;
    }

Error:

    return FALSE;
}


//
// Performs an update of the audio blocks using a given audio parameter file (.PRM).
// PRM files contains several blocks of data wich mist be donloaded seperately.
// Each blocks start with a 2 byte block size (in bytes, incuding length). Each block
// (including size) must be send to the module with its own AT^SBNW command.
//
BOOL CRilHandle::DownloadAudioProfiles(COM_PORT_TYPE PortType)
{
    CNotificationData* pnd = NULL;
    BOOL fRet = FALSE;
    BOOL fSendEndNotification = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    BYTE *szBytes = NULL;
    DWORD dwProfilesDLLVersion;
    WORD wBlockLen, wTmpLen;
    DWORD nFileSize;
    DWORD dwByteSize;
    DWORD nBlockCount = 0;
    WCHAR wszFileName[MAX_PATH];
    char  szResponse[MAX_ENHANCED_INIT_AT_RESPONSE];
    DWORD dwAudioTimeout = AUDIO_UPDATE_TIMEOUT;
    DWORD dwAudioDelay = AUDIO_UPDATE_DELAY;
    DWORD dwAudioInitialDelay = AUDIO_UPDATE_INIT_DELAY;
    BOOL fNonCriticalError = FALSE;

    if (!GetAudioProfileFilename(wszFileName, MAX_PATH, &dwProfilesDLLVersion))
    {
        fRet = TRUE;
        goto Error;
    }
    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        fRet = TRUE;
        goto Error;
    }
    nFileSize = GetFileSize(hFile, NULL);

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("AudioUpdateTimeout"), &dwAudioTimeout);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("AudioUpdateDelay"), &dwAudioDelay);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("AudioUpdateInitDelay"), &dwAudioInitialDelay);

    if (g_dwRadioAudioProfilesVersion == 0 || g_dwRadioAudioProfilesVersion == UNKNOWN_AUDIO_PROFILE)
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: AudioUpdate Current AudioProfilesVersion: unknown, FileVersion: %d\r\n"), dwProfilesDLLVersion));
    else
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: AudioUpdate Current AudioProfilesVersion: %d, FileVersion: %d\r\n"), ((g_dwRadioAudioProfilesVersion&0xFF00)>>8) + 100*((g_dwRadioAudioProfilesVersion&0xFF0000)>>16), dwProfilesDLLVersion));

    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_AUDIO_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }

    Sleep(dwAudioInitialDelay);
    while(nFileSize > 0)
    {
        if(!ReadFile(hFile, &wBlockLen, sizeof(WORD), &dwByteSize, NULL))
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Can't read audio profile file\r\n")));
            goto Error;
        }
        wTmpLen = wBlockLen;
        if (dwByteSize > 0)
        {
            if (wBlockLen != 0)
            {
                // the lenght of block will read in little-endian format, so we have to convert it to big-endian
                wBlockLen = (wTmpLen >> 8);
                wBlockLen = wBlockLen & 0x00ff;
                wBlockLen = wBlockLen | (wTmpLen << 8);
                szBytes = (BYTE *)ReallocBlob(szBytes,wBlockLen);
                if(szBytes == NULL) {
                    goto Error;
                }
                if(!ReadFile(hFile,&szBytes[2], (wBlockLen - 2),&dwByteSize, NULL))
                    goto Error;

                nFileSize = nFileSize - wBlockLen;

                szBytes[0] = (BYTE)wTmpLen;
                szBytes[1] = (BYTE)(wTmpLen >> 8);

                if (dwByteSize > 0)
                {
                    // it's not allowed to stay into the audio mode, which is currently updated
                    if(nBlockCount == 0)
                    {
                        if (!ExSendATCmdGetRsp(PortType, "AT^SNFS=3\r", szResponse, sizeof(szResponse), dwAudioTimeout, 0))
                            goto Error;
                    }
                    else if(nBlockCount == 1)
                    {
                        if (!ExSendATCmdGetRsp(PortType, "AT^SNFS=1\r", szResponse, sizeof(szResponse), dwAudioTimeout, 0))
                            goto Error;
                    }
                    nBlockCount++;
                    if (!ExSendATCommand(PortType, "AT^SBNW=AUD,0\r"))
                        goto Error;
                    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwAudioTimeout, FALSE, TRUE))
                        goto Error;
                    else
                    {
                        // send the data to the module
                        if (!ExSendBytes(PortType, szBytes, wBlockLen, dwAudioDelay))
                            goto Error;
                        if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwAudioTimeout, TRUE, TRUE))
                            goto Error;
                        if (strstr(szResponse, "ERROR") != NULL)  { // There are various possible error messages, but all contain the word "ERROR"
                            fNonCriticalError = TRUE;
                            goto Error;
                        }
                        (void)PurgeComm(g_ComManager.GetPortHandle(PortType), PURGE_RXCLEAR | PURGE_TXCLEAR);
                    }
                    // Note: the file contains NUMBER_OF_AUDIO_MODES block plus an additional block for TTY mode.
                    BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_AUDIO_UPDATE_PROGRESS, 100 * nBlockCount / (NUMBER_OF_AUDIO_MODES+1));
                }
                else
                {
                    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Audio profile update FAIL\r\n")));
                    goto Error;
                }
            }
            else
            {
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Audio profile update FAIL\r\n")));
                goto Error;
            }

        }
        else
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Audio profile update FAIL\r\n")));
            goto Error;
        }
    }

    // To verify, that the new audio profile is really loaded, query again the audio profile version.
    (void)CheckAudioProfilesVersion(PortType);
    fRet = TRUE;

Error:

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_AUDIO_UPDATE_END,  fRet ? UPDATE_PROCESS_SUCCESS : UPDATE_PROCESS_ERROR);
    // In case of a non-critical error, allow RIL to go on, so let the function return "true".
    if (fNonCriticalError) fRet = TRUE;

    return fRet;
}

//
//
//
static void FillEmptyAudioSteps(DWORD *pdwSteps, DWORD dwMaxSteps, DWORD dwNumSteps)
{
    // For the TxMcSteps and TxAcSteps the user ist not requiered to fill in all registry values.
    // In this case the rest of the array is filled with the last given value.

    if (dwNumSteps < dwMaxSteps && dwNumSteps > 0)
    {
        UINT i;

        for (i=dwNumSteps; i<dwMaxSteps;i++)
            pdwSteps[i] = pdwSteps[i-1];
    }
}

//
// Reads audio configuration from registry and stores is in g_acAudioConfig
// global variable.
//
void GetAudioConfig(void)
{
    INT iAudioMode;
    HKEY    hKey = NULL;
    DWORD   dwSize;

    // Fetch all audiomode (expect RIL_AUDIO_NONE)
    for (iAudioMode = 1; iAudioMode < MAX_AUDIO_MODES; iAudioMode++)
    {
        // Read AT^SNFS parameter
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_MODE, &(g_acAudioConfig[iAudioMode].dwAudioMode));
        // Read AT^SAIC parameter
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICINTERFACE, &(g_acAudioConfig[iAudioMode].dwSAICinterface));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICMICROPHONE, &(g_acAudioConfig[iAudioMode].dwSAICmicrophone));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICEARPIECE, &(g_acAudioConfig[iAudioMode].dwSAICearpiece));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICCLOCK, &(g_acAudioConfig[iAudioMode].dwSAICclock));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICMSMODE, &(g_acAudioConfig[iAudioMode].dwSAICmsMode));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICFRAMEMODE, &(g_acAudioConfig[iAudioMode].dwSAICframeMode));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SAICEXTCLKMODE, &(g_acAudioConfig[iAudioMode].dwSAICextClkMode));
        // Read external mic setting
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_EXTMICAUDIOCODECVOL, &(g_acAudioConfig[iAudioMode].dwExtMicAudioCodecVolSteps[0]));
        FillEmptyAudioSteps(g_acAudioConfig[iAudioMode].dwExtMicAudioCodecVolSteps, MAX_VOLUME_STEPS, 1);
        // Read AT^SNFI parameters
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICAMP1, &(g_acAudioConfig[iAudioMode].dwSNFImicAmp1Steps[0]));
        FillEmptyAudioSteps(g_acAudioConfig[iAudioMode].dwSNFImicAmp1Steps, MAX_VOLUME_STEPS, 1);
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICAMP2, &(g_acAudioConfig[iAudioMode].dwSNFImicAmp2Steps[0]));
        FillEmptyAudioSteps(g_acAudioConfig[iAudioMode].dwSNFImicAmp2Steps, MAX_VOLUME_STEPS, 1);
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICTXVOL, &(g_acAudioConfig[iAudioMode].dwSNFImicTxVolSteps[0]));
        FillEmptyAudioSteps(g_acAudioConfig[iAudioMode].dwSNFImicTxVolSteps, MAX_VOLUME_STEPS, 1);
        // Now read the "Steps" registry keys
        (void)RegOpenKeyEx(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTable[iAudioMode], 0, KEY_READ, &hKey);
        if(hKey != NULL) {
            // Read exterrnal AC settings
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_EXTAUDIOCODECVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwExtAudioCodecVolSteps[0]), &dwSize);
            // Read AT^SNFO parameters (QCT)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOCDCRXGAINSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFOcdcRxGainSteps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFORXVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFOrxVolSteps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOSTGAINSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFOstGainSteps[0]), &dwSize);
            // Read AT^SNFO parameters (IMC)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOOUTVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFOoutVolSteps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOSIDETONESTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFOsideToneSteps[0]), &dwSize);
            // Read optional exterrnal mic settings (steps)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_EXTMICAUDIOCODECVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwExtMicAudioCodecVolSteps[0]), &dwSize);
            // Read optional AT^SNFI parameters (QCT)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICAMP1STEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFImicAmp1Steps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICAMP2STEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFImicAmp2Steps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICTXVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFImicTxVolSteps[0]), &dwSize);
            // Read optional AT^SNFI parameters (IMC)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIINVOLSTEP, 0, 0, (UCHAR*)&(g_acAudioConfig[iAudioMode].dwSNFIinVolSteps[0]), &dwSize);
            (void)RegCloseKey(hKey);
#ifdef CHECK_SNFO_PARAMETER_RANGE
            if (NEW_SNFO_RXGAIN_SETTINGS) {
              INT iC;

              for (iC = 0; iC < MAX_VOLUME_STEPS; iC++) {
                if (g_acAudioConfig[iAudioMode].dwSNFOcdcRxGainSteps[iC] > 63)
                  g_acAudioConfig[iAudioMode].dwSNFOcdcRxGainSteps[iC] = 57;      // 57 is recomended default value
              }
            }
#endif
        }
        else {
          // If no registry keys are used, the default setting become active. These setting use the HC25-style
          // parameters, so they must be changed to the PH8-style parameters, if a PH8 is used.
          if (NEW_SNFO_RXGAIN_SETTINGS) {
            INT iC;

            for (iC = 0; iC < MAX_VOLUME_STEPS; iC++)
               g_acAudioConfig[iAudioMode].dwSNFOcdcRxGainSteps[iC] = 57;      // 57 is recomended default value for the PH8
          }
        }
    }

    // Now get the CDMA audio parameters. Default: use the same as for GSM
    memcpy(g_acAudioConfigCdma, g_acAudioConfig, MAX_AUDIO_MODES*sizeof(AUDIO_CONFIG));
    for (iAudioMode = 1; iAudioMode < MAX_AUDIO_MODES; iAudioMode++)
    {
        // Read AT^SNFS parameter
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_MODE, &(g_acAudioConfigCdma[iAudioMode].dwAudioMode));
        // Read AT^SAIC parameter
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICINTERFACE, &(g_acAudioConfigCdma[iAudioMode].dwSAICinterface));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICMICROPHONE, &(g_acAudioConfigCdma[iAudioMode].dwSAICmicrophone));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICEARPIECE, &(g_acAudioConfigCdma[iAudioMode].dwSAICearpiece));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICCLOCK, &(g_acAudioConfigCdma[iAudioMode].dwSAICclock));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICMSMODE, &(g_acAudioConfigCdma[iAudioMode].dwSAICmsMode));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICFRAMEMODE, &(g_acAudioConfigCdma[iAudioMode].dwSAICframeMode));
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SAICEXTCLKMODE, &(g_acAudioConfigCdma[iAudioMode].dwSAICextClkMode));
        // Read external mic setting
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_EXTMICAUDIOCODECVOL, &(g_acAudioConfigCdma[iAudioMode].dwExtMicAudioCodecVolSteps[0]));
        FillEmptyAudioSteps(g_acAudioConfigCdma[iAudioMode].dwExtMicAudioCodecVolSteps, MAX_VOLUME_STEPS, 1);
        // Read AT^SNFI parameters
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICAMP1, &(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp1Steps[0]));
        FillEmptyAudioSteps(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp1Steps, MAX_VOLUME_STEPS, 1);
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICAMP2, &(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp2Steps[0]));
        FillEmptyAudioSteps(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp2Steps, MAX_VOLUME_STEPS, 1);
        dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
        (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], RIL_AUDIO_CONFIG_SNFIMICTXVOL, &(g_acAudioConfigCdma[iAudioMode].dwSNFImicTxVolSteps[0]));
        FillEmptyAudioSteps(g_acAudioConfigCdma[iAudioMode].dwSNFImicTxVolSteps, MAX_VOLUME_STEPS, 1);
        // Now read the "Steps" registry keys
        (void)RegOpenKeyEx(RIL_AUDIO_CONFIG_ROOT, g_szAudioRegKeyTableCdma[iAudioMode], 0, KEY_READ, &hKey);
        if(hKey != NULL) {
            // Read exterrnal AC settings
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_EXTAUDIOCODECVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwExtAudioCodecVolSteps[0]), &dwSize);
            // Read AT^SNFO parameters
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOCDCRXGAINSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFOcdcRxGainSteps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFORXVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFOrxVolSteps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFOSTGAINSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFOstGainSteps[0]), &dwSize);
            // Read optional exterrnal mic settings (steps)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_EXTMICAUDIOCODECVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwExtMicAudioCodecVolSteps[0]), &dwSize);
            // Read optional AT^SNFI parameters (steps)
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICAMP1STEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp1Steps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICAMP2STEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFImicAmp2Steps[0]), &dwSize);
            dwSize = MAX_VOLUME_STEPS*sizeof(DWORD);
            (void)RegQueryValueEx(hKey, RIL_AUDIO_CONFIG_SNFIMICTXVOLSTEPS, 0, 0, (UCHAR*)&(g_acAudioConfigCdma[iAudioMode].dwSNFImicTxVolSteps[0]), &dwSize);
            (void)RegCloseKey(hKey);
#ifdef CHECK_SNFO_PARAMETER_RANGE
            if (NEW_SNFO_RXGAIN_SETTINGS) {
              INT iC;

              for (iC = 0; iC < MAX_VOLUME_STEPS; iC++) {
                if (g_acAudioConfigCdma[iAudioMode].dwSNFOcdcRxGainSteps[iC] > 63)
                  g_acAudioConfigCdma[iAudioMode].dwSNFOcdcRxGainSteps[iC] = 57;      // 57 is recomended default value
              }
            }
#endif
        }
        else {
          // If no registry keys are used, the default setting become active. These setting use the HC25-style
          // parameters, so they must be changed to the PH8-style parameters, if a PH8 is used.
          if (NEW_SNFO_RXGAIN_SETTINGS) {
            INT iC;

            for (iC = 0; iC < MAX_VOLUME_STEPS; iC++)
               g_acAudioConfigCdma[iAudioMode].dwSNFOcdcRxGainSteps[iC] = 57;      // 57 is recomended default value for the PH8
          }
        }
    }
}

//
// First initialization of audio settings. This incules:
// - Initial setting for each audio mode
// - Set default audio mode (handset)
// - Reset of all audio modes (optional)
// - Ringtone configuration (optional)
//
BOOL CRilHandle::InitializeAudioSettings()
{
    INT iDevice = 0;
    DWORD dwCurAudioMode = 0;
    DWORD dwRTT = 0;
    DWORD dwRTV = 0;
    char* szWork;
    AUDIO_CONFIG *pAudio = NULL;
    char szCmd[MAX_ATCMD_LEN];
    BOOL fRet = TRUE;
    UINT iMode = 0;
    DWORD dwDefaultAudio = RIL_AUDIO_HANDSET;

    // Walk through settings
    for (iMode = 1; iMode < MAX_AUDIO_MODES; iMode++) {
        BOOL fIsSet = FALSE;
        UINT i;

        dwCurAudioMode = g_acAudioConfig[iMode].dwAudioMode;

        // Check, if audio mode is already set.
        for (i = 1; i < iMode; i++) {
            if (g_acAudioConfig[i].dwAudioMode == dwCurAudioMode) {
                if (NO_SAIC_IN_CALL) {
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Ignoring multiple setting use of audio mode %d in %s (using: %s)\r\n"), dwCurAudioMode, g_szAudioRegKeyTable[iMode], g_szAudioRegKeyTable[i]));
                }
                fIsSet = TRUE;
                break;
            }
        }

        // If not set, initialize audio mode.
        if (!fIsSet && dwCurAudioMode > 0) {
            pAudio = &g_acAudioConfig[iMode];
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SNFS=%d\r", dwCurAudioMode);
            szWork = strchr(szCmd, '\r');
            if (g_dwUseSNFOSNFI == 1 && dwCurAudioMode > 1 && g_dwSeparateAudioATCmds != 1) {
                (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szCmd), ";%s\r", GetResetAudioParams(FALSE));
                szWork = strchr(szCmd, '\r');
                if (SUPPORTS_SAIC_AUDIO_CONFIG && pAudio->dwSAICinterface > 0 && g_dwUseSNFOSNFI == 1) {
                    (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szCmd), ";^SAIC=%u,%u,%u,%u,%u,%u,%u\r", pAudio->dwSAICinterface, pAudio->dwSAICmicrophone, pAudio->dwSAICearpiece, pAudio->dwSAICclock, pAudio->dwSAICmsMode, pAudio->dwSAICframeMode, pAudio->dwSAICextClkMode);
                    szWork = strchr(szCmd, '\r');
                }
            }
            if (!QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0)) {
                fRet = FALSE;
            }
            if (g_dwUseSNFOSNFI == 1 && dwCurAudioMode > 1 && g_dwSeparateAudioATCmds == 1) {
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, GetResetAudioParams(TRUE));
                szWork = strchr(szCmd, '\r');
                if (SUPPORTS_SAIC_AUDIO_CONFIG && pAudio->dwSAICinterface > 0 && g_dwUseSNFOSNFI == 1) {
                    (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szCmd), ";^SAIC=%u,%u,%u,%u,%u,%u,%u\r", pAudio->dwSAICinterface, pAudio->dwSAICmicrophone, pAudio->dwSAICearpiece, pAudio->dwSAICclock, pAudio->dwSAICmsMode, pAudio->dwSAICframeMode, pAudio->dwSAICextClkMode);
                    szWork = strchr(szCmd, '\r');
                }
                if (!QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0)) {
                    fRet = FALSE;
                }
            }
        }
    }

    // Set initial settings (handset mode) and set parameters to zero to avoid a
    // squealing noise at the start of the first voice call.
    (void)GetRegistryDWORD(RIL_AUDIO_CONFIG_ROOT, RIL_AUDIO_CONFIG_KEY, TEXT("DefaultAudioMode"), &dwDefaultAudio);
    iDevice = GetAudioTableIndex(dwDefaultAudio);
    pAudio = GetAudioTableEntry(iDevice, this);
    dwCurAudioMode = (pAudio->dwAudioMode ? pAudio->dwAudioMode : 1);
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SNFS=%d\r",dwCurAudioMode);
    if (!QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0)) {
        fRet = FALSE;
    }

    // If configured, set audio parameters in all audio modes to 0.
    if (g_dwAlwaysZeroAudioParameters == 1) {
        for (iMode = 2; iMode <= (UINT)NUMBER_OF_AUDIO_MODES; iMode++) {
            if (iMode != pAudio->dwAudioMode) {
                if (g_dwSeparateAudioATCmds == 1) {
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SNFS=%d\r", iMode);
                    (void)QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
                    (void)QueueInternalCmd(HCCMD_STANDARD, this,  GetResetAudioParams(TRUE), CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
                }
                else {
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SNFS=%d;%s\r", iMode, GetResetAudioParams(FALSE));
                    (void)QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
                }
            }
        }
    }

    // Configure ring tones.
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RingToneType"), &dwRTT) && fRet) {
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RingToneVolume"), &dwRTV);
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SRTC=0,%d,%d\r", dwRTT, dwRTV);
        if (!QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 3, 2000)) {
            fRet = FALSE;
        }
    }
    dwRTT = 0;
    dwRTV = 0;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RingToneTypeSMS"), &dwRTT) && fRet) {
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RingToneVolumeSMS"), &dwRTV);
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SRTC=2,%d,%d\r", dwRTT, dwRTV);
        if (!QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 3, 2000)) {
            fRet = FALSE;
        }
    }

    // It may be necessary to do some additional audio mode changes.
    PerformDoubleAudioCheck(this, dwDefaultAudio);

    return fRet;
}

