/*
 * CmdHndlr.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   CmdHndlr.c 
 *  \brief  The Command-Hnadler module.
 *  
 *  \see    CmdHndlr.h
 */

#define __FILE_ID__  FILE_ID_48
#include "tidef.h"
#include "commonTypes.h"
#include "osApi.h"
#include "report.h"
#include "queue.h"
#include "context.h"
#include "CmdHndlr.h"
#include "CmdInterpret.h"
#include "DrvMainModules.h"


/* The queue may contain only one command per configuration application but set as unlimited */
#define COMMANDS_QUE_SIZE   QUE_UNLIMITED_SIZE   

/* Command module internal data */
typedef struct 
{
   TI_HANDLE       hOs;            
   TI_HANDLE       hReport;        
   TI_HANDLE       hContext;       
   TI_HANDLE       hCmdInterpret;

   TI_HANDLE       hCmdQueue;       /* Handle to the commands queue */
   TI_BOOL         bProcessingCmds; /* Indicates if currently processing commands */
   TI_UINT32       uContextId;      /* ID allocated to this module on registration to context module */
   TConfigCommand *pCurrCmd;        /* Pointer to the command currently being processed */
} TCmdHndlrObj;

/* External functions prototypes */
extern void wlanDrvIf_CommandDone (TI_HANDLE hOs, void *pSignalObject, TI_UINT8 *CmdResp_p);

/** 
 * \fn     cmdHndlr_Create
 * \brief  Create the module
 * 
 * Create the module object
 * 
 * \note   
 * \param  hOs - Handle to the Os Abstraction Layer                           
 * \return Handle to the allocated module (NULL if failed) 
 * \sa     
 */ 
TI_HANDLE cmdHndlr_Create (TI_HANDLE hOs, TI_HANDLE hEvHandler)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *) os_memoryAlloc (hOs, sizeof(TCmdHndlrObj));

    if (pCmdHndlr == NULL) 
    {
        return NULL;
    }

    os_memoryZero (hOs, (void *)pCmdHndlr, sizeof(TCmdHndlrObj));

    pCmdHndlr->hOs = hOs;

    pCmdHndlr->hCmdInterpret = cmdInterpret_Create (hOs);

    if (pCmdHndlr->hCmdInterpret == NULL) 
    {
		cmdHndlr_Destroy ((TI_HANDLE) pCmdHndlr, (TI_HANDLE) hEvHandler);
        return NULL;
    }

    return (TI_HANDLE) pCmdHndlr;
}


/** 
 * \fn     cmdHndlr_Destroy
 * \brief  Destroy the module object
 * 
 * Destroy the module object.
 * 
 * \note   
 * \param  hCmdHndlr - The object                                          
 * \return TI_OK 
 * \sa     
 */ 
TI_STATUS cmdHndlr_Destroy (TI_HANDLE hCmdHndlr, TI_HANDLE hEvHandler)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;

	if (pCmdHndlr->hCmdInterpret)
	{
		cmdInterpret_Destroy (pCmdHndlr->hCmdInterpret, hEvHandler);
	}

    cmdHndlr_ClearQueue (hCmdHndlr);

	if (pCmdHndlr->hCmdQueue)
	{
		que_Destroy (pCmdHndlr->hCmdQueue);
	}

	os_memoryFree (pCmdHndlr->hOs, hCmdHndlr, sizeof(TCmdHndlrObj));

    return TI_OK;
}


/** 
 * \fn     cmdHndlr_ClearQueue
 * \brief  Clear commands queue
 * 
 * Dequeue and free all queued commands.
 * 
 * \note   
 * \param  hCmdHndlr - The object                                          
 * \return void 
 * \sa     
 */ 
void cmdHndlr_ClearQueue (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj    *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;
    TConfigCommand  *pCurrCmd;

    /* Dequeue and free all queued commands */
    do {
        context_EnterCriticalSection (pCmdHndlr->hContext);
        pCurrCmd = (TConfigCommand *)que_Dequeue(pCmdHndlr->hCmdQueue);
        context_LeaveCriticalSection (pCmdHndlr->hContext);
        if (pCurrCmd != NULL) {
            /* Just release the semaphore. The command is freed subsequently. */
            os_SignalObjectSet (pCmdHndlr->hOs, pCurrCmd->pSignalObject);
        }
    } while (pCurrCmd != NULL);
}


/** 
 * \fn     cmdHndlr_Init 
 * \brief  Init required handles and registries
 * 
 * Init required handles and module variables, create the commands-queue and 
 *     register as the context-engine client.
 * 
 * \note    
 * \param  pStadHandles  - The driver modules handles
 * \return void  
 * \sa     
 */ 
void cmdHndlr_Init (TStadHandlesList *pStadHandles)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)(pStadHandles->hCmdHndlr);
    TI_UINT32     uNodeHeaderOffset;

    pCmdHndlr->hReport  = pStadHandles->hReport;
    pCmdHndlr->hContext = pStadHandles->hContext;

    cmdInterpret_Init (pCmdHndlr->hCmdInterpret, pStadHandles);

    /* The offset of the queue-node-header from the commands structure entry is needed by the queue */
    uNodeHeaderOffset = TI_FIELD_OFFSET(TConfigCommand, tQueNodeHdr); 

    /* Create and initialize the commands queue */
    pCmdHndlr->hCmdQueue = que_Create (pCmdHndlr->hOs, pCmdHndlr->hReport, COMMANDS_QUE_SIZE, uNodeHeaderOffset);

    /* Register to the context engine and get the client ID */
    pCmdHndlr->uContextId = context_RegisterClient (pCmdHndlr->hContext,
                                                    cmdHndlr_HandleCommands,
                                                    (TI_HANDLE)pCmdHndlr,
                                                    TI_FALSE,
                                                    "COMMAND",
                                                    sizeof("COMMAND"));

	if(pCmdHndlr->hReport != NULL)
	{
		os_setDebugOutputToLogger(TI_FALSE);
	}
}


/** 
 * \fn     cmdHndlr_InsertCommand 
 * \brief  Insert a new command to the driver
 * 
 * Insert a new command to the commands queue from user context.
 * If commands are not beeing processed set a request to start processing in the driver context.
 * Wait on the current command's signal until its processing is completed.
 * Note that this prevents the user application from sending further commands before completion.
 * 
 * \note   
 * \param  hCmdHndlr    - The module object
 * \param  cmd          - User request
 * \param  others       - The command flags, data and params
 * \return TI_OK if command processed successfully, TI_NOK if failed in processing or memory allocation.  
 * \sa     cmdHndlr_HandleCommands, cmdHndlr_Complete
 */ 
TI_STATUS cmdHndlr_InsertCommand (TI_HANDLE     hCmdHndlr,
                                  TI_UINT32     cmd,
                                  TI_UINT32     flags,
                                  void         *buffer1,
                                  TI_UINT32     buffer1_len,
                                  void         *buffer2,
                                  TI_UINT32     buffer2_len,
                                  TI_UINT32    *param3,
                                  TI_UINT32    *param4)
{
    TCmdHndlrObj     *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;
	TConfigCommand   *pNewCmd;
	TI_STATUS         eStatus;

	/* Allocated command structure */
	pNewCmd = os_memoryAlloc (pCmdHndlr->hOs, sizeof (TConfigCommand));
	if (pNewCmd == NULL)
	{
		return TI_NOK;
	}
    os_memoryZero (pCmdHndlr->hOs, (void *)pNewCmd, sizeof(TConfigCommand));

	/* Copy user request into local structure */
	pNewCmd->cmd = cmd;
	pNewCmd->flags = flags;
	pNewCmd->buffer1 = buffer1;
	pNewCmd->buffer1_len = buffer1_len;
	pNewCmd->buffer2 = buffer2;
	pNewCmd->buffer2_len = buffer2_len;
	pNewCmd->param3 = param3;
	pNewCmd->param4 = param4;
	pNewCmd->pSignalObject = os_SignalObjectCreate (pCmdHndlr->hOs); /* initialize "complete-flag" */

	/* If creating the signal object failed */
	if (pNewCmd->pSignalObject == NULL)
	{
		os_printf("cmdPerform: Failed to create signalling object\n");
		/* free allocated memory and return error */
		os_memoryFree (pCmdHndlr->hOs, pNewCmd, sizeof (TConfigCommand));
		return TI_NOK;
	}

    /* Indicate the start of command process, from adding it to the queue until get return status form it */  
    pNewCmd->bWaitFlag = TI_TRUE;

    /* Enter critical section to protect queue access */
    context_EnterCriticalSection (pCmdHndlr->hContext);

    /* Enqueue the command (if failed, release memory and return NOK) */
    eStatus = que_Enqueue (pCmdHndlr->hCmdQueue, (TI_HANDLE)pNewCmd);
    if (eStatus != TI_OK) 
    {
        os_printf("cmdPerform: Failed to enqueue new command\n");
        os_SignalObjectFree (pCmdHndlr->hOs, pNewCmd->pSignalObject);
        pNewCmd->pSignalObject = NULL;
        os_memoryFree (pCmdHndlr->hOs, pNewCmd, sizeof (TConfigCommand));
        context_LeaveCriticalSection (pCmdHndlr->hContext);  /* Leave critical section */
        return TI_NOK;
    }

    /* 
     * Note: The bProcessingCmds flag is used for indicating if we are already processing
     *           the queued commands, so the context-engine shouldn't invoke cmdHndlr_HandleCommands.
     *       This is important because if we make this decision according to the queue being empty,
     *           there may be a command under processing (already dequeued) while the queue is empty.
     *       Note that although we are blocking the current command's originator, there may be another
     *           application that will issue a command.
     */

    if (pCmdHndlr->bProcessingCmds)
    {
        /* No need to schedule the driver (already handling commands) so just leave critical section */
        context_LeaveCriticalSection (pCmdHndlr->hContext);
    }
    else
    {
        /* Indicate that we are handling queued commands (before leaving critical section!) */
        pCmdHndlr->bProcessingCmds = TI_TRUE;

        /* Leave critical section */
        context_LeaveCriticalSection (pCmdHndlr->hContext);

        /* Request driver task schedule for command handling (after we left critical section!) */
        context_RequestSchedule (pCmdHndlr->hContext, pCmdHndlr->uContextId);
    }

	/* Wait until the command is executed */
	os_SignalObjectWait (pCmdHndlr->hOs, pNewCmd->pSignalObject);

	/* After "wait" - the command has already been processed by the drivers' context */
	/* Indicate the end of command process, from adding it to the queue until get return status form it */  
	pNewCmd->bWaitFlag = TI_FALSE;

	/* Copy the return code */
	eStatus = pNewCmd->return_code;

	/* Free signalling object and command structure */
	os_SignalObjectFree (pCmdHndlr->hOs, pNewCmd->pSignalObject);
	pNewCmd->pSignalObject = NULL;

	/* If command not completed in this context (Async) don't free the command memory */
	if(COMMAND_PENDING != pNewCmd->eCmdStatus)
	{
		os_memoryFree (pCmdHndlr->hOs, pNewCmd, sizeof (TConfigCommand));
	}

	/* Return to calling process with command return code */
	return eStatus;
}



/** 
 * \fn     cmdHndlr_HandleCommands 
 * \brief  Handle queued commands
 * 
 * While there are queued commands, dequeue a command and call the 
 *     commands interpreter (OID or WEXT selected at compile time).
 * If the command processing is not completed in this context (pending), we exit and 
 *     this function is called again upon commnad completion, so it can continue processing 
 *     further queued commands (if any).
 * 
 * \note    
 * \param  hCmdHndlr - The module object
 * \return void
 * \sa     cmdHndlr_InsertCommand, cmdHndlr_Complete
 */ 
void cmdHndlr_HandleCommands (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj     *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;

    while (1)
    {
        /* Enter critical section to protect queue access */
        context_EnterCriticalSection (pCmdHndlr->hContext);

        /* Dequeue a command */
        pCmdHndlr->pCurrCmd = (TConfigCommand *) que_Dequeue (pCmdHndlr->hCmdQueue);

        /* If we have got a command */
        if (pCmdHndlr->pCurrCmd) 
        {
            /* Leave critical section */
            context_LeaveCriticalSection (pCmdHndlr->hContext);

            /* Convert to driver structure and execute command */
            pCmdHndlr->pCurrCmd->eCmdStatus = cmdInterpret_convertAndExecute (pCmdHndlr->hCmdInterpret, pCmdHndlr->pCurrCmd);

            /* 
             *  If command not completed in this context (Async), return.
             *    (we'll be called back upon command completion) 
             */
            if(COMMAND_PENDING == pCmdHndlr->pCurrCmd->eCmdStatus)
            {
                return;
            }

            /* Command was completed so free the wait signal and continue to next command */
            wlanDrvIf_CommandDone(pCmdHndlr->hOs, pCmdHndlr->pCurrCmd->pSignalObject, pCmdHndlr->pCurrCmd->CmdRespBuffer); 

            pCmdHndlr->pCurrCmd = NULL;

        }

        /* Else, we don't have commands to handle */
        else
        {
            /* Indicate that we are not handling commands (before leaving critical section!) */
            pCmdHndlr->bProcessingCmds = TI_FALSE;

            /* Leave critical section */
            context_LeaveCriticalSection (pCmdHndlr->hContext);

            /* Exit (no more work) */
            return;
        }
    }
}


/** 
 * \fn     cmdHndlr_Complete 
 * \brief  called whenever a command has finished executing
 * 
 * This routine is called whenever a command has finished executing.
 * Either called by the cmdHndlr_HandleCommands if completed in the same context, 
 *     or by the CmdInterpreter module when tcompleted in a later context (Async).
 * 
 * \note    
 * \param  hCmdHndlr - The module object
 * \return void
 * \sa     cmdHndlr_InsertCommand, cmdHndlr_HandleCommands
 */ 
void cmdHndlr_Complete (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;
    TI_BOOL       bLocalWaitFlag;

    if (pCmdHndlr->pCurrCmd)
    {
        /* set Status to COMPLETE */
        pCmdHndlr->pCurrCmd->eCmdStatus = TI_OK;
    
        /* save the wait flag before free semaphore */
        bLocalWaitFlag = pCmdHndlr->pCurrCmd->bWaitFlag;

        wlanDrvIf_CommandDone(pCmdHndlr->hOs, pCmdHndlr->pCurrCmd->pSignalObject, pCmdHndlr->pCurrCmd->CmdRespBuffer); 

        /* if cmdHndlr_InsertCommand() not wait to cmd complete? */
        if (TI_FALSE == bLocalWaitFlag)
        {
            /* no wait, free the command memory */
            os_memoryFree (pCmdHndlr->hOs, pCmdHndlr->pCurrCmd, sizeof (TConfigCommand));
        }

        pCmdHndlr->pCurrCmd = NULL;

        return;
    }

    TRACE0(pCmdHndlr->hReport, REPORT_SEVERITY_ERROR, "cmdHndlr_Complete(): pCurrCmd is NULL!\n");
}


/** 
 * \fn     cmdHndlr_GetStat
 * \brief  Get driver statistics
 * 
 * Get the driver statistics (Tx, Rx, signal quality).
 *
 * \note   
 * \param  hCmdHndlr - The object                                          
 * \return The driver statistics pointer 
 * \sa     
 */ 
void * cmdHndlr_GetStat (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;

    return cmdInterpret_GetStat (pCmdHndlr->hCmdInterpret);
}


/** 
 * \fn     cmdHndlr_Enable & cmdHndlr_Disable
 * \brief  Enable/Disable invoking CmdHndlr module from driver-task
 * 
 * Called by the Driver-Main Init SM to enable/disable external inputs processing.
 * Calls the context-engine enable/disable function accordingly.
 *
 * \note   
 * \param  hCmdHndlr - The object                                          
 * \return void 
 * \sa     
 */ 
void cmdHndlr_Enable (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;

    context_EnableClient (pCmdHndlr->hContext, pCmdHndlr->uContextId);
}

void cmdHndlr_Disable (TI_HANDLE hCmdHndlr)
{
    TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr;

    context_DisableClient (pCmdHndlr->hContext, pCmdHndlr->uContextId);
}