/*
 * PowerMgrKeepAlive.c
 *
 * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.      
 * All rights reserved.                                                  
 *                                                                       
 * Redistribution and use in source and binary forms, with or without    
 * modification, are permitted provided that the following conditions    
 * are met:                                                              
 *                                                                       
 *  * Redistributions of source code must retain the above copyright     
 *    notice, this list of conditions and the following disclaimer.      
 *  * Redistributions in binary form must reproduce the above copyright  
 *    notice, this list of conditions and the following disclaimer in    
 *    the documentation and/or other materials provided with the         
 *    distribution.                                                      
 *  * Neither the name Texas Instruments nor the names of its            
 *    contributors may be used to endorse or promote products derived    
 *    from this software without specific prior written permission.      
 *                                                                       
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/** 
 * \file  PowerMgrKeepAlive.c
 * \brief implement user keep-alive messages
 */

#define __FILE_ID__  FILE_ID_73
#include "osTIType.h"
#include "TWDriver.h"
#include "STADExternalIf.h"
#include "txCtrl_Api.h"

typedef struct
{
    TI_HANDLE           hTWD;
    TI_HANDLE           hReport;
    TI_HANDLE           hOs;
    TI_HANDLE           hTxCtrl;
    TKeepAliveConfig    tCurrentConfig;
    TI_BOOL             bConnected;
    TI_UINT8            wlanHeader[ WLAN_WITH_SNAP_QOS_HEADER_MAX_SIZE + AES_AFTER_HEADER_FIELD_SIZE ];
    TI_UINT32           wlanHeaderLength;
    TI_UINT8            tempBuffer[ KEEP_ALIVE_TEMPLATE_MAX_LENGTH + WLAN_WITH_SNAP_QOS_HEADER_MAX_SIZE + AES_AFTER_HEADER_FIELD_SIZE ];
} TPowerMgrKL;

TI_STATUS powerMgrKLConfigureMessage (TI_HANDLE hPowerMgrKL, TI_UINT32 uMessageIndex);

/** 
 * \fn     powerMgrKL_create 
 * \brief  Creates the power manager keep-alive sub-module
 * 
 * Allocates memory for the keep-alive object
 * 
 * \param  hOS - handle to the os object
 * \return A handle to the power manager keep-alive sub-module 
 * \sa     powerMgrKL_destroy, powerMgrKL_init
 */
TI_HANDLE powerMgrKL_create (TI_HANDLE hOS)
{
    TPowerMgrKL     *pPowerMgrKL;

    /* allocate memory for the power manager keep-alive object */
    pPowerMgrKL = os_memoryAlloc (hOS, sizeof(TPowerMgrKL));
    if ( NULL == pPowerMgrKL)
    {
        return NULL;
    }

    /* store OS handle */
    pPowerMgrKL->hOs = hOS;

    return (TI_HANDLE)pPowerMgrKL;
}

/** 
 * \fn     powerMgrKL_destroy 
 * \brief  Destroys the power manager keep-alive sub-module
 * 
 * De-allocates keep-alive object memory
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \return None
 * \sa     powerMgrKL_create, powerMgrKL_init
 */
void powerMgrKL_destroy (TI_HANDLE hPowerMgrKL)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;

    os_memoryFree (pPowerMgrKL->hOs, hPowerMgrKL, sizeof(TPowerMgrKL));
}

/** 
 * \fn     powerMgrKL_init 
 * \brief  Initailize the power manager keep-alive sub-module
 * 
 * Stores handles to other modules
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \param  hReport - handle to the report object
 * \param  hTWD - handle to the TWD object
 * \param  hTxCtrl - handle to the TX control object
 * \return None
 * \sa     powerMgrKL_destroy, powerMgrKL_create, powerMgrKL_setDefaults
 */
void powerMgrKL_init (TI_HANDLE hPowerMgrKL,
                      TStadHandlesList *pStadHandles)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;

    /* store handles */
    pPowerMgrKL->hTWD       = pStadHandles->hTWD;
    pPowerMgrKL->hReport    = pStadHandles->hReport;
    pPowerMgrKL->hTxCtrl    = pStadHandles->hTxCtrl;
}

/** 
 * \fn     powerMgrKL_setDefaults
 * \brief  Set powr-manager keep-aive default initialization values
 * 
 * Set powr-manager keep-aive default initialization values
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \return None
 * \sa     powerMgrKL_init
 */
void powerMgrKL_setDefaults (TI_HANDLE hPowerMgrKL)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;
    TI_UINT32       uIndex;

    /* mark the global enable / disable flag as enabled */
    pPowerMgrKL->tCurrentConfig.enaDisFlag = TI_TRUE;

    /* mark all messages as disabled */
    for (uIndex = 0; uIndex < KEEP_ALIVE_MAX_USER_MESSAGES; uIndex++)
    {
        pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams.enaDisFlag = TI_FALSE;
    }

    /* mark STA as disconnected */
    pPowerMgrKL->bConnected = TI_FALSE;
}

/** 
 * \fn     powerMgrKL_start
 * \brief  Notifies the power-manager keep-alive upon connection to a BSS
 * 
 * Set all configured templates to the FW
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \return TI_OK if succesful, TI_NOK otherwise 
 * \sa     powerMgrKL_stop, powerMgrKL_setParam
 */
TI_STATUS powerMgrKL_start (TI_HANDLE hPowerMgrKL)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;
    TI_UINT32       uIndex;
    TI_STATUS       status = TI_OK;

    /* mark STA as connected */
    pPowerMgrKL->bConnected = TI_TRUE;

    /* build WLAN header */
    status = txCtrlServ_buildWlanHeader (pPowerMgrKL->hTxCtrl, &(pPowerMgrKL->wlanHeader[ 0 ]), &(pPowerMgrKL->wlanHeaderLength));

    /* download all enabled templates to the FW (through TWD)*/
    for (uIndex = 0; uIndex < KEEP_ALIVE_MAX_USER_MESSAGES; uIndex++)
    {
        /* 
         * if the message is enabled (disabled messages shouldn't be configured on connection, 
         * as they are disabled by default in the FW) */

        if (TI_TRUE == pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams.enaDisFlag)
        {
            /* configure keep-alive to the FW (through command builder */
            status = powerMgrKLConfigureMessage (hPowerMgrKL, uIndex);
        }
    }
    return status;
}

/** 
 * \fn     powerMgrKL_stop 
 * \brief  Notifies the power-manager keep-alive upon disconnection from a BSS
 * 
 * Delete all configured templates from the FW and internal storage
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \return TI_OK if succesful, TI_NOK otherwise 
 * \sa     powerMgrKL_start, powerMgrKL_setParam
 */
void powerMgrKL_stop (TI_HANDLE hPowerMgrKL, TI_BOOL bDisconnect)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;
    TI_UINT32       uIndex;

    /* mark STA as disconnected */
    pPowerMgrKL->bConnected = TI_FALSE;

    /* if this is a real disconnect */
    if (TI_TRUE == bDisconnect)
    {
        /* for all congfiured messages */
        for (uIndex = 0; uIndex < KEEP_ALIVE_MAX_USER_MESSAGES; uIndex++)
        {
            /* mark the message as disabled */
            pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams.enaDisFlag = TI_FALSE;
        }
    }
    /* for roaming, don't do anything */
}

/** 
 * \fn     powerMgrKL_setParam 
 * \brief  Handles a parametr change from user-mode
 * 
 * Handles addition / removal of a template and global enable / disable flag
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \param  pParam - A pointer to the paramter being set
 * \return TI_OK if succesful, TI_NOK otherwise 
 * \sa     powerMgrKL_start, powerMgrKL_stop
 */
TI_STATUS powerMgrKL_setParam (TI_HANDLE hPowerMgrKL, paramInfo_t *pParam)
{
    TPowerMgrKL         *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;
    TI_UINT32           uIndex;
    TKeepAliveTemplate  *pNewTmpl;
    TI_STATUS           status = TI_OK;

    TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_INFORMATION , "Keep-alive set param called with param type %d\n", pParam->paramType);

    switch (pParam->paramType)
    {
    /* global keep-alive enable / disable flag */
    case POWER_MGR_KEEP_ALIVE_ENA_DIS:

        pPowerMgrKL->tCurrentConfig.enaDisFlag = pParam->content.powerMgrKeepAliveEnaDis;
        return TWD_CfgKeepAliveEnaDis(pPowerMgrKL->hTWD, 
                                      (TI_UINT8)pParam->content.powerMgrKeepAliveEnaDis);
        break;

    /* keep-alive template and parameters configuration */
    case POWER_MGR_KEEP_ALIVE_ADD_REM:

        pNewTmpl = pParam->content.pPowerMgrKeepAliveTemplate;
        uIndex = pNewTmpl->keepAliveParams.index;

        /* if STA is connected */
        if (TI_TRUE == pPowerMgrKL->bConnected)
        {
            /* if keep-alive is already configured for this index */
            if (TI_TRUE == pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams.enaDisFlag)
            {
                /* disable previous keep-alive */
                pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams.enaDisFlag = TI_FALSE;
                status = TWD_CfgKeepAlive (pPowerMgrKL->hTWD, &(pPowerMgrKL->tCurrentConfig.templates[ uIndex ].keepAliveParams));
                if (TI_OK != status)
                {
                    TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "powerMgrKL_setParam: error trying to clear current template %d\n", status);
                    return status;
                }
            }

            /* copy configuration */
            os_memoryCopy (pPowerMgrKL->hOs, &(pPowerMgrKL->tCurrentConfig.templates[ uIndex ]),
                           pNewTmpl, sizeof (TKeepAliveTemplate));

            /* configure keep-alive to the FW (through command builder */
            return powerMgrKLConfigureMessage (hPowerMgrKL, uIndex);
        }
        /* STA disconnected */
        else
        {
            /* copy configuration */
            os_memoryCopy (pPowerMgrKL->hOs, &(pPowerMgrKL->tCurrentConfig.templates[ uIndex ]),
                           pNewTmpl, sizeof (TKeepAliveTemplate));
        }
        return TI_OK;
        break;

    default:
        TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "power manager keep-alive: set param with unknown param type %d\n", pParam->paramType);
        return PARAM_NOT_SUPPORTED;
        break;
    }
}

/** 
 * \fn     powerMgrKL_getParam 
 * \brief  Handles a parametr request from user-mode
 * 
 * Retrieves configuration
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \param  pParam - A pointer to the paramter being retrieved
 * \return TI_OK if succesful, TI_NOK otherwise 
 * \sa     powerMgrKL_start, powerMgrKL_stop
 */
TI_STATUS powerMgrKL_getParam (TI_HANDLE hPowerMgrKL, paramInfo_t *pParam)
{
    TPowerMgrKL     *pPowerMgrKL = (TPowerMgrKL*)hPowerMgrKL;

    TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_INFORMATION , "Keep-alive get param called with param type %d\n", pParam->paramType);

    switch (pParam->paramType)
    {
    case POWER_MGR_KEEP_ALIVE_GET_CONFIG:

        pParam->paramLength = sizeof (TKeepAliveConfig);
        os_memoryCopy (pPowerMgrKL->hOs, (void*)pParam->content.pPowerMgrKeepAliveConfig,
                       (void*)&(pPowerMgrKL->tCurrentConfig), sizeof (TKeepAliveConfig));

        return TI_OK;
        break;

    default:
        TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "power manager keep-alive: get param with unknown param type %d\n", pParam->paramType);
        return PARAM_NOT_SUPPORTED;
        break;
    }
}

/** 
 * \fn     powerMgrKLConfigureMessage 
 * \brief  Configures keep-alive message (template and parameters)
 * 
 * Configures a keepa-live message from internal database.
 * 
 * \param  hPowerMgrKL - handle to the power-manager keep-alive object
 * \param  uMessageIndex - index of message to configure (from internal database)
 * \return TI_OK if succesful, TI_NOK otherwise 
 * \sa     powerMgrKL_start, powerMgrKL_setParam
 */
TI_STATUS powerMgrKLConfigureMessage (TI_HANDLE hPowerMgrKL, TI_UINT32 uMessageIndex)
{
    TPowerMgrKL     *pPowerMgrKL    = (TPowerMgrKL*)hPowerMgrKL;
    TI_STATUS       status          = TI_OK;

    /* if the keep-alive for this index is enabled */
    if (TI_TRUE == pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].keepAliveParams.enaDisFlag)
    {
        /* configure template - first the template itself */
        TSetTemplate    tTemplate;

        tTemplate.type = KEEP_ALIVE_TEMPLATE;
        tTemplate.index = uMessageIndex;
        os_memoryCopy (pPowerMgrKL->hOs, &(pPowerMgrKL->tempBuffer),
                       &(pPowerMgrKL->wlanHeader), pPowerMgrKL->wlanHeaderLength); /* copy WLAN header - was built on connection */
        os_memoryCopy (pPowerMgrKL->hOs, &(pPowerMgrKL->tempBuffer[ pPowerMgrKL->wlanHeaderLength ]),
                       &(pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].msgBuffer[ 0 ]),
                       pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].msgBufferLength); /* copy template data */
        tTemplate.ptr = &(pPowerMgrKL->tempBuffer[ 0 ]);                                                                     
        tTemplate.len = pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].msgBufferLength + pPowerMgrKL->wlanHeaderLength;
        tTemplate.uRateMask = RATE_MASK_UNSPECIFIED;
        status = TWD_CmdTemplate (pPowerMgrKL->hTWD, &tTemplate, NULL, NULL);
        if (TI_OK != status)
        {
            TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "powerMgrKLConfigureMessage: error trying to set new template %d\n", status);
            return status;
        }

        /* and than the parameters */
        status = TWD_CfgKeepAlive (pPowerMgrKL->hTWD, &(pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].keepAliveParams));
        if (TI_OK != status)
        {
            TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "powerMgrKLConfigureMessage: error trying to set new keep-alive params %d\n", status);
            return status;
        }
    }
    /* keep-alive for this index is disabled - just disable it */
    else
    {
        status = TWD_CfgKeepAlive (pPowerMgrKL->hTWD, &(pPowerMgrKL->tCurrentConfig.templates[ uMessageIndex ].keepAliveParams));
        if (TI_OK != status)
        {
            TRACE1(pPowerMgrKL->hReport, REPORT_SEVERITY_ERROR , "powerMgrKLConfigureMessage: error trying to set new keep-alive params %d\n", status);
            return status;
        }
    }

    return status;
}