1/*
2 * context.c
3 *
4 * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 *  * Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *  * Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 *  * Neither the name Texas Instruments nor the names of its
18 *    contributors may be used to endorse or promote products derived
19 *    from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34
35/** \file   context.c
36 *  \brief  The Context-Engine is an OS independent module, which provides the
37 *            infrustracture for switching from external contexts to the driver's context.
38 *          This includes also the driver task itself (workqueue in Linux), which invokes the
39 *            driver specific handlers in the driver's context.
40 *          The OS specific implementation under this module (e.g. task-switching or
41 *            protection-locking) is provided by the OS abstraction layer (osapi.c).
42 *
43 *  \see    context.h, osapi.c
44 */
45
46
47#define __FILE_ID__  FILE_ID_125
48#include "osApi.h"
49#include "report.h"
50#include "context.h"
51#include "bmtrace_api.h"
52
53
54
55#define MAX_CLIENTS     8   /* Maximum number of clients using context services */
56#define MAX_NAME_SIZE   16  /* Maximum client's name string size */
57
58#ifdef TI_DBG
59typedef struct
60{
61    TI_UINT32       uSize;                  /* Clients' name string size */
62    char            sName [MAX_NAME_SIZE];  /* Clients' name string      */
63} TClientName;
64#endif /* TI_DBG */
65
66/* context module structure */
67typedef struct
68{
69    TI_HANDLE        hOs;
70    TI_HANDLE        hReport;
71
72    TI_BOOL          bContextSwitchRequired;       /* Indicate if the driver should switch to its  */
73                                                   /*   own context or not before handling events  */
74    TI_HANDLE        hProtectionLock;              /* Handle of protection lock used by context clients */
75    TI_UINT32        uNumClients;                  /* Number of registered clients      */
76    TContextCbFunc   aClientCbFunc [MAX_CLIENTS];  /* Clients' callback functions       */
77    TI_HANDLE        aClientCbHndl [MAX_CLIENTS];  /* Clients' callback handles         */
78    TI_BOOL          aClientEnabled[MAX_CLIENTS];  /* Clients' enable/disable flags     */
79    TI_BOOL          aClientPending[MAX_CLIENTS];  /* Clients' pending flags            */
80
81#ifdef TI_DBG
82    TClientName      aClientName   [MAX_CLIENTS];  /* Clients' name string              */
83    TI_UINT32        aRequestCount [MAX_CLIENTS];  /* Clients' schedule requests counter*/
84    TI_UINT32        aInvokeCount  [MAX_CLIENTS];  /* Clients' invocations counter      */
85#endif
86
87} TContext;
88
89
90/**
91 * \fn     context_Create
92 * \brief  Create the module
93 *
94 * Allocate and clear the module object.
95 *
96 * \note
97 * \param  hOs      - Handle to Os Abstraction Layer
98 * \return Handle of the allocated object
99 * \sa     context_Destroy
100 */
101TI_HANDLE context_Create (TI_HANDLE hOs)
102{
103    TI_HANDLE hContext;
104
105    /* allocate module object */
106    hContext = os_memoryAlloc (hOs, sizeof(TContext));
107
108    if (!hContext)
109    {
110        WLAN_OS_REPORT (("context_Create():  Allocation failed!!\n"));
111        return NULL;
112    }
113
114    os_memoryZero (hOs, hContext, (sizeof(TContext)));
115
116    return (hContext);
117}
118
119
120/**
121 * \fn     context_Destroy
122 * \brief  Destroy the module.
123 *
124 * Free the module memory.
125 *
126 * \note
127 * \param  hContext - The module object
128 * \return TI_OK on success or TI_NOK on failure
129 * \sa     context_Create
130 */
131TI_STATUS context_Destroy (TI_HANDLE hContext)
132{
133    TContext *pContext = (TContext *)hContext;
134
135    /* Destroy the protection handle */
136    os_protectDestroy (pContext->hOs, pContext->hProtectionLock);
137
138    /* free module object */
139    os_memoryFree (pContext->hOs, pContext, sizeof(TContext));
140
141    return TI_OK;
142}
143
144
145/**
146 * \fn     context_Init
147 * \brief  Init required handles
148 *
149 * Init required handles.
150 *
151 * \note
152 * \param  hContext  - The queue object
153 * \param  hOs       - Handle to Os Abstraction Layer
154 * \param  hReport   - Handle to report module
155 * \return void
156 * \sa
157 */
158void context_Init (TI_HANDLE hContext, TI_HANDLE hOs, TI_HANDLE hReport)
159{
160    TContext *pContext = (TContext *)hContext;
161
162    pContext->hOs     = hOs;
163    pContext->hReport = hReport;
164
165    /* Create the module's protection lock and save its handle */
166    pContext->hProtectionLock = os_protectCreate (pContext->hOs);
167}
168
169
170/**
171 * \fn     context_SetDefaults
172 * \brief  Configure module with default settings
173 *
174 * Set default setting which indicates if the driver should switch to
175 *     its own context or not before handling events
176 *
177 * \note
178 * \param  hContext           - The module's object
179 * \param  pContextInitParams - The module's init parameters
180 * \return TI_OK on success or TI_NOK on failure
181 * \sa
182 */
183TI_STATUS context_SetDefaults (TI_HANDLE hContext, TContextInitParams *pContextInitParams)
184{
185    TContext *pContext = (TContext *)hContext;
186
187    /* Set parameters */
188    pContext->bContextSwitchRequired = pContextInitParams->bContextSwitchRequired;
189
190    return TI_OK;
191}
192
193
194/**
195 * \fn     context_RegisterClient
196 * \brief  Save client's parameters
197 *
198 * This function is used by the Context clients in their init process, for registering
199 *   their information,
200 * This includes their callback function that should be invoked from the driver context
201 *   when they are pending.
202 *
203 * \note
204 * \param  hContext - The module handle
205 * \param  fCbFunc  - The client's callback function.
206 * \param  hCbHndl  - The client's callback function handle.
207 * \param  bEnable  - TRUE = Enabled.
208 * \return TI_UINT32 - The index allocated for the client
209 * \sa
210 */
211TI_UINT32 context_RegisterClient (TI_HANDLE       hContext,
212                                  TContextCbFunc  fCbFunc,
213                                  TI_HANDLE       hCbHndl,
214                                  TI_BOOL         bEnable,
215                                  char           *sName,
216                                  TI_UINT32       uNameSize)
217{
218    TContext *pContext = (TContext *)hContext;
219    TI_UINT32 uClientId = pContext->uNumClients;
220
221    /* If max number of clients is exceeded, report error and exit. */
222    if (uClientId == MAX_CLIENTS)
223    {
224        TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_RegisterClient() MAX_CLIENTS limit exceeded!!\n");
225        return 0;
226    }
227
228    /* Save the new client's parameters. */
229    pContext->aClientCbFunc[uClientId]  = fCbFunc;
230    pContext->aClientCbHndl[uClientId]  = hCbHndl;
231    pContext->aClientEnabled[uClientId] = bEnable;
232    pContext->aClientPending[uClientId] = TI_FALSE;
233
234#ifdef TI_DBG
235    if (uNameSize <= MAX_NAME_SIZE)
236    {
237        os_memoryCopy(pContext->hOs,
238                      (void *)(pContext->aClientName[uClientId].sName),
239                      (void *)sName,
240                      uNameSize);
241        pContext->aClientName[uClientId].uSize = uNameSize;
242    }
243    else
244    {
245        TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_RegisterClient() MAX_NAME_SIZE limit exceeded!\n");
246    }
247#endif /* TI_DBG */
248
249    /* Increment clients number and return new client ID. */
250    pContext->uNumClients++;
251
252    TRACE2(pContext->hReport, REPORT_SEVERITY_INIT , "context_RegisterClient(): Client=, ID=%d, enabled=%d\n", uClientId, bEnable);
253
254    return uClientId;
255}
256
257
258/**
259 * \fn     context_RequestSchedule
260 * \brief  Handle client's switch to driver's context.
261 *
262 * This function is called by a client from external context event.
263 * It sets the client's Pending flag and requests the driver's task scheduling.
264 * Thus, the client's callback will be called afterwards from the driver context.
265 *
266 * \note
267 * \param  hContext   - The module handle
268 * \param  uClientId  - The client's index
269 * \return void
270 * \sa     context_DriverTask
271 */
272void context_RequestSchedule (TI_HANDLE hContext, TI_UINT32 uClientId)
273{
274    TContext *pContext = (TContext *)hContext;
275
276#ifdef TI_DBG
277    pContext->aRequestCount[uClientId]++;
278    TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_RequestSchedule(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
279#endif /* TI_DBG */
280
281    /* Set client's Pending flag */
282    pContext->aClientPending[uClientId] = TI_TRUE;
283
284    /* Prevent system from going to sleep */
285    os_wake_lock(pContext->hOs);
286
287    /*
288     * If configured to switch context, request driver task scheduling.
289     * Else (context switch not required) call the driver task directly.
290     */
291    if (pContext->bContextSwitchRequired)
292    {
293        if (os_RequestSchedule(pContext->hOs) != TI_OK)
294            os_wake_unlock(pContext->hOs);
295    }
296    else
297    {
298        context_DriverTask(hContext);
299        os_wake_unlock(pContext->hOs);
300    }
301}
302
303
304/**
305 * \fn     context_DriverTask
306 * \brief  The driver task
307 *
308 * This function is the driver's main task that always runs in the driver's
309 * single context, scheduled through the OS (the driver's workqueue in Linux).
310 * Only one instantiation of this task may run at a time!
311 *
312 * \note
313 * \param  hContext   - The module handle
314 * \return void
315 * \sa     context_RequestSchedule
316 */
317void context_DriverTask (TI_HANDLE hContext)
318{
319    TContext       *pContext = (TContext *)hContext;
320    TContextCbFunc  fCbFunc;
321    TI_HANDLE       hCbHndl;
322    TI_UINT32       i;
323    CL_TRACE_START_L1();
324
325    TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_DriverTask():\n");
326
327    /* For all registered clients do: */
328    for (i = 0; i < pContext->uNumClients; i++)
329    {
330        /* If client is pending and enabled */
331        if (pContext->aClientPending[i]  &&  pContext->aClientEnabled[i])
332        {
333#ifdef TI_DBG
334            pContext->aInvokeCount[i]++;
335            TRACE1(pContext->hReport, REPORT_SEVERITY_INFORMATION , "Invoking - Client=, ID=%d\n", i);
336#endif /* TI_DBG */
337
338            /* Clear client's pending flag */
339            pContext->aClientPending[i] = TI_FALSE;
340
341            /* Call client's callback function */
342            fCbFunc = pContext->aClientCbFunc[i];
343            hCbHndl = pContext->aClientCbHndl[i];
344            fCbFunc(hCbHndl);
345        }
346    }
347
348    CL_TRACE_END_L1("tiwlan_drv.ko", "CONTEXT", "TASK", "");
349}
350
351
352/**
353 * \fn     context_EnableClient / context_DisableClient
354 * \brief  Enable a specific client activation
355 *
356 * Called by the driver main when needed to enble/disable a specific event handling.
357 * The Enable function also schedules the driver-task if the specified client is pending.
358 *
359 * \note
360 * \param  hContext   - The module handle
361 * \param  uClientId  - The client's index
362 * \return void
363 * \sa     context_DriverTask
364 */
365void context_EnableClient (TI_HANDLE hContext, TI_UINT32 uClientId)
366{
367    TContext *pContext = (TContext *)hContext;
368
369#ifdef TI_DBG
370    if (pContext->aClientEnabled[uClientId])
371    {
372        TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_EnableClient() Client  already enabled!!\n");
373        return;
374    }
375    TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_EnableClient(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
376#endif /* TI_DBG */
377
378    /* Enable client */
379    pContext->aClientEnabled[uClientId] = TI_TRUE;
380
381    /* If client is pending, schedule driver task */
382    if (pContext->aClientPending[uClientId])
383    {
384        /* Prevent system from going to sleep */
385        os_wake_lock(pContext->hOs);
386
387        /*
388         * If configured to switch context, request driver task scheduling.
389         * Else (context switch not required) call the driver task directly.
390         */
391        if (pContext->bContextSwitchRequired)
392        {
393            if (os_RequestSchedule(pContext->hOs) != TI_OK)
394                os_wake_unlock(pContext->hOs);
395        }
396        else
397        {
398            context_DriverTask(hContext);
399            os_wake_unlock(pContext->hOs);
400        }
401    }
402}
403
404void context_DisableClient (TI_HANDLE hContext, TI_UINT32 uClientId)
405{
406    TContext *pContext = (TContext *)hContext;
407
408#ifdef TI_DBG
409    if (!pContext->aClientEnabled[uClientId])
410    {
411        TRACE0(pContext->hReport, REPORT_SEVERITY_ERROR , "context_DisableClient() Client  already disabled!!\n");
412        return;
413    }
414    TRACE3(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_DisableClient(): Client=, ID=%d, enabled=%d, pending=%d\n", uClientId, pContext->aClientEnabled[uClientId], pContext->aClientPending[uClientId]);
415#endif /* TI_DBG */
416
417    /* Disable client */
418    pContext->aClientEnabled[uClientId] = TI_FALSE;
419}
420
421
422/**
423 * \fn     context_EnterCriticalSection / context_LeaveCriticalSection
424 * \brief  Lock / Unlock context related critical sections
425 *
426 * The context clients should use these functions for protecting their critical sections
427 *   when handling context transition to driver context.
428 *
429 * \note
430 * \param  hContext   - The module handle
431 * \return void
432 * \sa
433 */
434void context_EnterCriticalSection (TI_HANDLE hContext)
435{
436    TContext *pContext = (TContext *)hContext;
437
438    TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_EnterCriticalSection():\n");
439
440    /* Start critical section protection */
441    os_protectLock (pContext->hOs, pContext->hProtectionLock);
442}
443
444void context_LeaveCriticalSection (TI_HANDLE hContext)
445{
446    TContext *pContext = (TContext *)hContext;
447
448    TRACE0(pContext->hReport, REPORT_SEVERITY_INFORMATION , "context_LeaveCriticalSection():\n");
449
450    /* Stop critical section protection */
451    os_protectUnlock (pContext->hOs, pContext->hProtectionLock);
452}
453
454
455/**
456 * \fn     context_Print
457 * \brief  Print module information
458 *
459 * Print the module's clients parameters.
460 *
461 * \note
462 * \param  hContext - The queue object
463 * \return void
464 * \sa
465 */
466
467#ifdef TI_DBG
468
469void context_Print(TI_HANDLE hContext)
470{
471#ifdef REPORT_LOG
472    TContext *pContext = (TContext *)hContext;
473    TI_UINT32 i;
474
475    WLAN_OS_REPORT(("context_Print():  %d Clients Registered:\n", pContext->uNumClients));
476    WLAN_OS_REPORT(("=======================================\n"));
477    WLAN_OS_REPORT(("bContextSwitchRequired = %d\n", pContext->bContextSwitchRequired));
478
479    for (i = 0; i < pContext->uNumClients; i++)
480    {
481        WLAN_OS_REPORT(("Client %d - %s: CbFunc=0x%x, CbHndl=0x%x, Enabled=%d, Pending=%d, Requests=%d, Invoked=%d\n",
482                        i,
483                        pContext->aClientName[i].sName,
484                        pContext->aClientCbFunc[i],
485                        pContext->aClientCbHndl[i],
486                        pContext->aClientEnabled[i],
487                        pContext->aClientPending[i],
488                        pContext->aRequestCount[i],
489                        pContext->aInvokeCount[i] ));
490    }
491#endif
492}
493
494#endif /* TI_DBG */
495