/*
* context.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 context.c
* \brief The Context-Engine is an OS independent module, which provides the
* infrustracture for switching from external contexts to the driver's context.
* This includes also the driver task itself (workqueue in Linux), which invokes the
* driver specific handlers in the driver's context.
* The OS specific implementation under this module (e.g. task-switching or
* protection-locking) is provided by the OS abstraction layer (osapi.c).
*
* \see context.h, osapi.c
*/
#define __FILE_ID__ FILE_ID_125
#include "osApi.h"
#include "report.h"
#include "context.h"
#include "bmtrace_api.h"
#define MAX_CLIENTS 8 /* Maximum number of clients using context services */
#define MAX_NAME_SIZE 16 /* Maximum client's name string size */
#ifdef TI_DBG
typedef struct
{
TI_UINT32 uSize; /* Clients' name string size */
char sName [MAX_NAME_SIZE]; /* Clients' name string */
} TClientName;
#endif /* TI_DBG */
/* context module structure */
typedef struct
{
TI_HANDLE hOs;
TI_HANDLE hReport;
TI_BOOL bContextSwitchRequired; /* Indicate if the driver should switch to its */
/* own context or not before handling events */
TI_HANDLE hProtectionLock; /* Handle of protection lock used by context clients */
TI_UINT32 uNumClients; /* Number of registered clients */
TContextCbFunc aClientCbFunc [MAX_CLIENTS]; /* Clients' callback functions */
TI_HANDLE aClientCbHndl [MAX_CLIENTS]; /* Clients' callback handles */
TI_BOOL aClientEnabled[MAX_CLIENTS]; /* Clients' enable/disable flags */
TI_BOOL aClientPending[MAX_CLIENTS]; /* Clients' pending flags */
#ifdef TI_DBG
TClientName aClientName [MAX_CLIENTS]; /* Clients' name string */
TI_UINT32 aRequestCount [MAX_CLIENTS]; /* Clients' schedule requests counter*/
TI_UINT32 aInvokeCount [MAX_CLIENTS]; /* Clients' invocations counter */
#endif
} TContext;
/**
* \fn context_Create
* \brief Create the module
*
* Allocate and clear the module object.
*
* \note
* \param hOs - Handle to Os Abstraction Layer
* \return Handle of the allocated object
* \sa context_Destroy
*/
TI_HANDLE context_Create (TI_HANDLE hOs)
{
TI_HANDLE hContext;
/* allocate module object */
hContext = os_memoryAlloc (hOs, sizeof(TContext));
if (!hContext)
{
WLAN_OS_REPORT (("context_Create(): Allocation failed!!\n"));
return NULL;
}
os_memoryZero (hOs, hContext, (sizeof(TContext)));
return (hContext);
}
/**
* \fn context_Destroy
* \brief Destroy the module.
*
* Free the module memory.
*
* \note
* \param hContext - The module object
* \return TI_OK on success or TI_NOK on failure
* \sa context_Create
*/
TI_STATUS context_Destroy (TI_HANDLE hContext)
{
TContext *pContext = (TContext *)hContext;
/* Destroy the protection handle */
os_protectDestroy (pContext->hOs, pContext->hProtectionLock);
/* free module object */
os_memoryFree (pContext->hOs, pContext, sizeof(TContext));
return TI_OK;
}
/**
* \fn context_Init
* \brief Init required handles
*
* Init required handles.
*
* \note
* \param hContext - The queue object
* \param hOs - Handle to Os Abstraction Layer
* \param hReport - Handle to report module
* \return void
* \sa
*/
void context_Init (TI_HANDLE hContext, TI_HANDLE hOs, TI_HANDLE hReport)
{
TContext *pContext = (TContext *)hContext;
pContext->hOs = hOs;
pContext->hReport = hReport;
/* Create the module's protection lock and save its handle */
pContext->hProtectionLock = os_protectCreate (pContext->hOs);
}
/**
* \fn context_SetDefaults
* \brief Configure module with default settings
*
* Set default setting which indicates if the driver should switch to
* its own context or not before handling events
*
* \note
* \param hContext - The module's object
* \param pContextInitParams - The module's init parameters
* \return TI_OK on success or TI_NOK on failure
* \sa
*/
TI_STATUS context_SetDefaults (TI_HANDLE hContext, TContextInitParams *pContextInitParams)
{
TContext *pContext = (TContext *)hContext;
/* Set parameters */
pContext->bContextSwitchRequired = pContextInitParams->bContextSwitchRequired;
return TI_OK;
}
/**
* \fn context_RegisterClient
* \brief Save client's parameters
*
* This function is used by the Context clients in their init process, for registering
* their information,
* This includes their callback function that should be invoked from the driver context
* when they are pending.
*
* \note
* \param hContext - The module handle
* \param fCbFunc - The client's callback function.
* \param hCbHndl - The client's callback function handle.
* \param bEnable - TRUE = Enabled.
* \return TI_UINT32 - The index allocated for the client
* \sa
*/
TI_UINT32 context_RegisterClient (TI_HANDLE hContext,
TContextCbFunc fCbFunc,
TI_HANDLE hCbHndl,
TI_BOOL bEnable,
char *sName,
TI_UINT32 uNameSize)
{
TContext *pContext = (TContext *)hContext;
TI_UINT32 uClientId = pContext->uNumClients;
/* If max number of clients is exceeded, report error and exit. */
if (uClientId == MAX_CLIENTS)
{
TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_RegisterClient() MAX_CLIENTS limit exceeded!!\n");
return 0;
}
/* Save the new client's parameters. */
pContext->aClientCbFunc[uClientId] = fCbFunc;
pContext->aClientCbHndl[uClientId] = hCbHndl;
pContext->aClientEnabled[uClientId] = bEnable;
pContext->aClientPending[uClientId] = TI_FALSE;
#ifdef TI_DBG
if (uNameSize <= MAX_NAME_SIZE)
{
os_memoryCopy(pContext->hOs,
(void *)(pContext->aClientName[uClientId].sName),
(void *)sName,
uNameSize);
pContext->aClientName[uClientId].uSize = uNameSize;
}
else
{
TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_RegisterClient() MAX_NAME_SIZE limit exceeded!\n");
}
#endif /* TI_DBG */
/* Increment clients number and return new client ID. */
pContext->uNumClients++;
TRACE2(pContext->hReport, REPORT_SEVERITY_INIT , "context_RegisterClient(): Client=, ID=%d, enabled=%d\n", uClientId, bEnable);
return uClientId;
}
/**
* \fn context_RequestSchedule
* \brief Handle client's switch to driver's context.
*
* This function is called by a client from external context event.
* It sets the client's Pending flag and requests the driver's task scheduling.
* Thus, the client's callback will be called afterwards from the driver context.
*
* \note
* \param hContext - The module handle
* \param uClientId - The client's index
* \return void
* \sa context_DriverTask
*/
void context_RequestSchedule (TI_HANDLE hContext, TI_UINT32 uClientId)
{
TContext *pContext = (TContext *)hContext;
#ifdef TI_DBG
pContext->aRequestCount[uClientId]++;
TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_RequestSchedule(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
#endif /* TI_DBG */
/* Set client's Pending flag */
pContext->aClientPending[uClientId] = TI_TRUE;
/* Prevent system from going to sleep */
os_wake_lock(pContext->hOs);
/*
* If configured to switch context, request driver task scheduling.
* Else (context switch not required) call the driver task directly.
*/
if (pContext->bContextSwitchRequired)
{
if (os_RequestSchedule(pContext->hOs) != TI_OK)
os_wake_unlock(pContext->hOs);
}
else
{
context_DriverTask(hContext);
os_wake_unlock(pContext->hOs);
}
}
/**
* \fn context_DriverTask
* \brief The driver task
*
* This function is the driver's main task that always runs in the driver's
* single context, scheduled through the OS (the driver's workqueue in Linux).
* Only one instantiation of this task may run at a time!
*
* \note
* \param hContext - The module handle
* \return void
* \sa context_RequestSchedule
*/
void context_DriverTask (TI_HANDLE hContext)
{
TContext *pContext = (TContext *)hContext;
TContextCbFunc fCbFunc;
TI_HANDLE hCbHndl;
TI_UINT32 i;
CL_TRACE_START_L1();
TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_DriverTask():\n");
/* For all registered clients do: */
for (i = 0; i < pContext->uNumClients; i++)
{
/* If client is pending and enabled */
if (pContext->aClientPending[i] && pContext->aClientEnabled[i])
{
#ifdef TI_DBG
pContext->aInvokeCount[i]++;
TRACE1(pContext->hReport, REPORT_SEVERITY_INFORMATION , "Invoking - Client=, ID=%d\n", i);
#endif /* TI_DBG */
/* Clear client's pending flag */
pContext->aClientPending[i] = TI_FALSE;
/* Call client's callback function */
fCbFunc = pContext->aClientCbFunc[i];
hCbHndl = pContext->aClientCbHndl[i];
fCbFunc(hCbHndl);
}
}
CL_TRACE_END_L1("tiwlan_drv.ko", "CONTEXT", "TASK", "");
}
/**
* \fn context_EnableClient / context_DisableClient
* \brief Enable a specific client activation
*
* Called by the driver main when needed to enble/disable a specific event handling.
* The Enable function also schedules the driver-task if the specified client is pending.
*
* \note
* \param hContext - The module handle
* \param uClientId - The client's index
* \return void
* \sa context_DriverTask
*/
void context_EnableClient (TI_HANDLE hContext, TI_UINT32 uClientId)
{
TContext *pContext = (TContext *)hContext;
#ifdef TI_DBG
if (pContext->aClientEnabled[uClientId])
{
TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_EnableClient() Client already enabled!!\n");
return;
}
TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_EnableClient(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
#endif /* TI_DBG */
/* Enable client */
pContext->aClientEnabled[uClientId] = TI_TRUE;
/* If client is pending, schedule driver task */
if (pContext->aClientPending[uClientId])
{
/* Prevent system from going to sleep */
os_wake_lock(pContext->hOs);
/*
* If configured to switch context, request driver task scheduling.
* Else (context switch not required) call the driver task directly.
*/
if (pContext->bContextSwitchRequired)
{
if (os_RequestSchedule(pContext->hOs) != TI_OK)
os_wake_unlock(pContext->hOs);
}
else
{
context_DriverTask(hContext);
os_wake_unlock(pContext->hOs);
}
}
}
void context_DisableClient (TI_HANDLE hContext, TI_UINT32 uClientId)
{
TContext *pContext = (TContext *)hContext;
#ifdef TI_DBG
if (!pContext->aClientEnabled[uClientId])
{
TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_DisableClient() Client already disabled!!\n");
return;
}
TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_DisableClient(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
#endif /* TI_DBG */
/* Disable client */
pContext->aClientEnabled[uClientId] = TI_FALSE;
}
/**
* \fn context_EnterCriticalSection / context_LeaveCriticalSection
* \brief Lock / Unlock context related critical sections
*
* The context clients should use these functions for protecting their critical sections
* when handling context transition to driver context.
*
* \note
* \param hContext - The module handle
* \return void
* \sa
*/
void context_EnterCriticalSection (TI_HANDLE hContext)
{
TContext *pContext = (TContext *)hContext;
TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_EnterCriticalSection():\n");
/* Start critical section protection */
os_protectLock (pContext->hOs, pContext->hProtectionLock);
}
void context_LeaveCriticalSection (TI_HANDLE hContext)
{
TContext *pContext = (TContext *)hContext;
TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_LeaveCriticalSection():\n");
/* Stop critical section protection */
os_protectUnlock (pContext->hOs, pContext->hProtectionLock);
}
/**
* \fn context_Print
* \brief Print module information
*
* Print the module's clients parameters.
*
* \note
* \param hContext - The queue object
* \return void
* \sa
*/
#ifdef TI_DBG
void context_Print(TI_HANDLE hContext)
{
#ifdef REPORT_LOG
TContext *pContext = (TContext *)hContext;
TI_UINT32 i;
WLAN_OS_REPORT(("context_Print(): %d Clients Registered:\n", pContext->uNumClients));
WLAN_OS_REPORT(("=======================================\n"));
WLAN_OS_REPORT(("bContextSwitchRequired = %d\n", pContext->bContextSwitchRequired));
for (i = 0; i < pContext->uNumClients; i++)
{
WLAN_OS_REPORT(("Client %d - %s: CbFunc=0x%x, CbHndl=0x%x, Enabled=%d, Pending=%d, Requests=%d, Invoked=%d\n",
i,
pContext->aClientName[i].sName,
pContext->aClientCbFunc[i],
pContext->aClientCbHndl[i],
pContext->aClientEnabled[i],
pContext->aClientPending[i],
pContext->aRequestCount[i],
pContext->aInvokeCount[i] ));
}
#endif
}
#endif /* TI_DBG */