/*
* 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);
}