/* * 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 */