1/*
2 * TxnQueue.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   TxnQueue.c
36 *  \brief  The transaction-queue module.
37 *
38 * The Transaction Queue encapsulates the bus access from a functional driver (WLAN, BT).
39 * This TI proprietary module presents the same interface and same behavior for different
40 *     bus configuration: SDIO (multi or single function) or SPI and for different modes
41 *     of operation: Synchronous, a-synchronous or combination of both.
42 * It will also be used over the RS232 interface (using wUART protocol) which is applicable
43 *     for RS applications (on PC).
44 *
45 * The TxnQ module provides the following requirements:
46 *     Inter process protection on queue's internal database and synchronization between
47 *         functional drivers that share the bus.
48 *     Support multiple queues per function, handled by priority.
49 *     Support the TTxnStruct API (as the Bus Driver) with the ability to manage commands
50 *         queuing of multiple functions on top of the Bus Driver.
51 *     The TxnQ (as well as the layers above it) is agnostic to the bus driver used beneath it
52 *         (SDIO, WSPI or wUART), since all bus drivers introduce the same API and hide bus details.
53 *     The TxnQ has no OS dependencies. It supports access from multiple OS threads.
54 * Note: It is assumed that any transaction forwarded to the TxnQ has enough resources in HW.
55 *
56 *  \see    TxnQueue.h
57 */
58
59#define __FILE_ID__  FILE_ID_123
60#include "tidef.h"
61#include "report.h"
62#include "context.h"
63#include "osApi.h"
64#include "TxnDefs.h"
65#include "BusDrv.h"
66#include "TxnQueue.h"
67
68
69
70/************************************************************************
71 * Defines
72 ************************************************************************/
73#define MAX_FUNCTIONS       4   /* Maximum 4 functional drivers (including Func 0 which is for bus control) */
74#define MAX_PRIORITY        2   /* Maximum 2 prioritys per functional driver */
75#define TXN_QUE_SIZE        64  /* Txn-queue size */
76#define TXN_DONE_QUE_SIZE   64  /* TxnDone-queue size */
77
78
79/************************************************************************
80 * Types
81 ************************************************************************/
82
83/* Functional driver's SM States */
84typedef enum
85{
86    FUNC_STATE_NONE,              /* Function not registered */
87	FUNC_STATE_STOPPED,           /* Queues are stopped */
88	FUNC_STATE_RUNNING,           /* Queues are running */
89	FUNC_STATE_RESTART            /* Wait for current Txn to finish before restarting queues */
90} EFuncState;
91
92/* The functional drivers registered to TxnQ */
93typedef struct
94{
95    EFuncState      eState;             /* Function crrent state */
96    TI_UINT32       uNumPrios;          /* Number of queues (priorities) for this function */
97	TTxnQueueDoneCb fTxnQueueDoneCb;    /* The CB called by the TxnQueue upon full transaction completion. */
98	TI_HANDLE       hCbHandle;          /* The callback handle */
99    TTxnStruct *    pSingleStep;        /* A single step transaction waiting to be sent */
100
101} TFuncInfo;
102
103
104/* The TxnQueue module Object */
105typedef struct _TTxnQObj
106{
107    TI_HANDLE	    hOs;
108    TI_HANDLE	    hReport;
109    TI_HANDLE	    hContext;
110	TI_HANDLE	    hBusDrv;
111
112    TFuncInfo       aFuncInfo[MAX_FUNCTIONS];  /* Registered functional drivers - see above */
113    TI_HANDLE       aTxnQueues[MAX_FUNCTIONS][MAX_PRIORITY];  /* Handle of the Transactions-Queue */
114    TI_HANDLE       hTxnDoneQueue;      /* Queue for completed transactions not reported to yet to the upper layer */
115    TTxnStruct *    pCurrTxn;           /* The transaction currently processed in the bus driver (NULL if none) */
116    TI_UINT32       uMinFuncId;         /* The minimal function ID actually registered (through txnQ_Open) */
117    TI_UINT32       uMaxFuncId;         /* The maximal function ID actually registered (through txnQ_Open) */
118    TI_BOOL         bSchedulerBusy;     /* If set, the scheduler is currently running so it shouldn't be reentered */
119    TI_BOOL         bSchedulerPend;     /* If set, a call to the scheduler was postponed because it was busy */
120
121    /* Environment dependent: TRUE if needed and allowed to protect TxnDone in critical section */
122    TTxnDoneCb      fConnectCb;
123    TI_HANDLE       hConnectCb;
124
125} TTxnQObj;
126
127
128/************************************************************************
129 * Internal functions prototypes
130 ************************************************************************/
131static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
132static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
133static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
134static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
135static void         txnQ_ClearQueues  (TTxnQObj *pTxnQ, TI_UINT32 uFuncId);
136static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
137
138
139
140/************************************************************************
141 *
142 *   Module functions implementation
143 *
144 ************************************************************************/
145
146TI_HANDLE txnQ_Create (TI_HANDLE hOs)
147{
148    TI_HANDLE  hTxnQ;
149    TTxnQObj  *pTxnQ;
150    TI_UINT32  i;
151
152    hTxnQ = os_memoryAlloc(hOs, sizeof(TTxnQObj));
153    if (hTxnQ == NULL)
154        return NULL;
155
156    pTxnQ = (TTxnQObj *)hTxnQ;
157
158    os_memoryZero(hOs, hTxnQ, sizeof(TTxnQObj));
159
160    pTxnQ->hOs             = hOs;
161    pTxnQ->pCurrTxn        = NULL;
162    pTxnQ->uMinFuncId      = MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */
163    pTxnQ->uMaxFuncId      = 0;             /* Start at minimum and save maximal value in txnQ_Open */
164
165    for (i = 0; i < MAX_FUNCTIONS; i++)
166    {
167        pTxnQ->aFuncInfo[i].eState          = FUNC_STATE_NONE;
168        pTxnQ->aFuncInfo[i].uNumPrios       = 0;
169        pTxnQ->aFuncInfo[i].pSingleStep     = NULL;
170        pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL;
171        pTxnQ->aFuncInfo[i].hCbHandle       = NULL;
172    }
173
174    /* Create the Bus-Driver module */
175    pTxnQ->hBusDrv = busDrv_Create (hOs);
176    if (pTxnQ->hBusDrv == NULL)
177    {
178        WLAN_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__));
179        txnQ_Destroy (hTxnQ);
180        return NULL;
181    }
182
183    return pTxnQ;
184}
185
186TI_STATUS txnQ_Destroy (TI_HANDLE hTxnQ)
187{
188    TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ;
189
190    if (pTxnQ)
191    {
192        if (pTxnQ->hBusDrv)
193        {
194            busDrv_Destroy (pTxnQ->hBusDrv);
195        }
196        if (pTxnQ->hTxnDoneQueue)
197        {
198            que_Destroy (pTxnQ->hTxnDoneQueue);
199        }
200        os_memoryFree (pTxnQ->hOs, pTxnQ, sizeof(TTxnQObj));
201    }
202    return TI_OK;
203}
204
205void txnQ_Init (TI_HANDLE hTxnQ, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext)
206{
207    TTxnQObj  *pTxnQ = (TTxnQObj*)hTxnQ;
208    TI_UINT32  uNodeHeaderOffset;
209
210    pTxnQ->hOs             = hOs;
211    pTxnQ->hReport         = hReport;
212    pTxnQ->hContext        = hContext;
213
214    /* Create the TxnDone queue. */
215    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode);
216    pTxnQ->hTxnDoneQueue = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset);
217    if (pTxnQ->hTxnDoneQueue == NULL)
218    {
219        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": TxnDone queue creation failed!\n");
220    }
221
222    busDrv_Init (pTxnQ->hBusDrv, hReport);
223}
224
225TI_STATUS txnQ_ConnectBus (TI_HANDLE hTxnQ, TBusDrvCfg *pBusDrvCfg, TTxnDoneCb fConnectCb, TI_HANDLE hConnectCb)
226{
227    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
228
229    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_ConnectBus()\n");
230
231    pTxnQ->fConnectCb = fConnectCb;
232    pTxnQ->hConnectCb = hConnectCb;
233
234    return busDrv_ConnectBus (pTxnQ->hBusDrv, pBusDrvCfg, txnQ_TxnDoneCb, hTxnQ, txnQ_ConnectCB);
235}
236
237TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
238{
239    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
240
241    return busDrv_DisconnectBus (pTxnQ->hBusDrv);
242}
243
244TI_STATUS txnQ_Open (TI_HANDLE       hTxnQ,
245                     TI_UINT32       uFuncId,
246                     TI_UINT32       uNumPrios,
247                     TTxnQueueDoneCb fTxnQueueDoneCb,
248                     TI_HANDLE       hCbHandle)
249{
250    TTxnQObj     *pTxnQ = (TTxnQObj*) hTxnQ;
251    TI_UINT32     uNodeHeaderOffset;
252    TI_UINT32     i;
253
254    if (uFuncId >= MAX_FUNCTIONS  ||  uNumPrios > MAX_PRIORITY)
255    {
256        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Invalid Params!  uFuncId = %d, uNumPrios = %d\n", uFuncId, uNumPrios);
257        return TI_NOK;
258    }
259
260    context_EnterCriticalSection (pTxnQ->hContext);
261
262    /* Save functional driver info */
263    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = uNumPrios;
264    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = fTxnQueueDoneCb;
265    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = hCbHandle;
266    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_STOPPED;
267
268    /* Create the functional driver's queues. */
269    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode);
270    for (i = 0; i < uNumPrios; i++)
271    {
272        pTxnQ->aTxnQueues[uFuncId][i] = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_QUE_SIZE, uNodeHeaderOffset);
273        if (pTxnQ->aTxnQueues[uFuncId][i] == NULL)
274        {
275            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Queues creation failed!\n");
276            context_LeaveCriticalSection (pTxnQ->hContext);
277            return TI_NOK;
278        }
279    }
280
281    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
282    if (uFuncId < pTxnQ->uMinFuncId)
283    {
284        pTxnQ->uMinFuncId = uFuncId;
285    }
286    if (uFuncId > pTxnQ->uMaxFuncId)
287    {
288        pTxnQ->uMaxFuncId = uFuncId;
289    }
290
291    context_LeaveCriticalSection (pTxnQ->hContext);
292
293    TRACE2(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d registered successfully, uNumPrios = %d\n", uFuncId, uNumPrios);
294
295    return TI_OK;
296}
297
298void txnQ_Close (TI_HANDLE  hTxnQ, TI_UINT32 uFuncId)
299{
300    TTxnQObj     *pTxnQ = (TTxnQObj*)hTxnQ;
301    TI_UINT32     i;
302
303    context_EnterCriticalSection (pTxnQ->hContext);
304
305    /* Destroy the functional driver's queues */
306    for (i = 0; i < pTxnQ->aFuncInfo[uFuncId].uNumPrios; i++)
307    {
308        que_Destroy (pTxnQ->aTxnQueues[uFuncId][i]);
309    }
310
311    /* Clear functional driver info */
312    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = 0;
313    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = NULL;
314    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = NULL;
315    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_NONE;
316
317    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
318    pTxnQ->uMinFuncId      = MAX_FUNCTIONS;
319    pTxnQ->uMaxFuncId      = 0;
320    for (i = 0; i < MAX_FUNCTIONS; i++)
321    {
322        if (pTxnQ->aFuncInfo[i].eState != FUNC_STATE_NONE)
323        {
324            if (i < pTxnQ->uMinFuncId)
325            {
326                pTxnQ->uMinFuncId = i;
327            }
328            if (i > pTxnQ->uMaxFuncId)
329            {
330                pTxnQ->uMaxFuncId = i;
331            }
332        }
333    }
334
335    context_LeaveCriticalSection (pTxnQ->hContext);
336
337    TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d Unregistered\n", uFuncId);
338}
339
340ETxnStatus txnQ_Restart (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
341{
342    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
343
344    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart()\n");
345
346    context_EnterCriticalSection (pTxnQ->hContext);
347
348    /* If a Txn from the calling function is in progress, set state to RESTART return PENDING */
349    if (pTxnQ->pCurrTxn)
350    {
351        if (TXN_PARAM_GET_FUNC_ID(pTxnQ->pCurrTxn) == uFuncId)
352        {
353            pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RESTART;
354
355            context_LeaveCriticalSection (pTxnQ->hContext);
356
357            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart(): pCurrTxn pending\n");
358
359            /* Return PENDING to indicate that the restart will be completed later (in TxnDone) */
360            return TXN_STATUS_PENDING;
361        }
362    }
363
364    context_LeaveCriticalSection (pTxnQ->hContext);
365
366    /* Clear the calling function's queues (call function CB with status=RECOVERY) */
367    txnQ_ClearQueues (pTxnQ, uFuncId);
368
369    /* Return COMPLETE to indicate that the restart was completed */
370    return TXN_STATUS_COMPLETE;
371}
372
373void txnQ_Run (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
374{
375    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
376
377#ifdef TI_DBG
378    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Run()\n");
379    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_STOPPED)
380    {
381        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_WARNING, "txnQ_Run(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
382    }
383#endif
384
385    /* Enable function's queues */
386    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
387
388    /* Send queued transactions as possible */
389    txnQ_RunScheduler (pTxnQ, NULL);
390}
391
392void txnQ_Stop (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
393{
394    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
395
396#ifdef TI_DBG
397    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Stop()\n");
398    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_RUNNING)
399    {
400        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Stop(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
401    }
402#endif
403
404    /* Enable function's queues */
405    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED;
406}
407
408ETxnStatus txnQ_Transact (TI_HANDLE hTxnQ, TTxnStruct *pTxn)
409{
410    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
411    TI_UINT32    uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
412    ETxnStatus   rc;
413
414    if (TXN_PARAM_GET_SINGLE_STEP(pTxn))
415    {
416        pTxnQ->aFuncInfo[uFuncId].pSingleStep = pTxn;
417        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Single step Txn\n");
418    }
419    else
420    {
421        TI_HANDLE hQueue = pTxnQ->aTxnQueues[uFuncId][TXN_PARAM_GET_PRIORITY(pTxn)];
422        context_EnterCriticalSection (pTxnQ->hContext);
423        que_Enqueue (hQueue, (TI_HANDLE)pTxn);
424        context_LeaveCriticalSection (pTxnQ->hContext);
425        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Regular Txn\n");
426    }
427
428    /* Send queued transactions as possible */
429    rc = txnQ_RunScheduler (pTxnQ, pTxn);
430
431    return rc;
432}
433
434
435/**
436 * \fn     txnQ_ConnectCB
437 * \brief  Pending Connection completion CB
438 *
439 *  txnQ_ConnectBus CB
440 *
441 * \note
442 * \param  hTxnQ - The module's object
443 * \param  pTxn  - The completed transaction object
444 * \return void
445 * \sa
446 */
447static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn)
448{
449    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
450
451    /* Call the Client Connect CB */
452    pTxnQ->fConnectCb (pTxnQ->hConnectCb, NULL);
453}
454
455
456/**
457 * \fn     txnQ_TxnDoneCb
458 * \brief  Pending Transaction completion CB
459 *
460 * Called back by bus-driver upon pending transaction completion in TxnDone context (external!).
461 * Enqueue completed transaction in TxnDone queue and call scheduler to send queued transactions.
462 *
463 * \note
464 * \param  hTxnQ - The module's object
465 * \param  pTxn  - The completed transaction object
466 * \return void
467 * \sa
468 */
469static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn)
470{
471    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
472    TTxnStruct *pTxn    = (TTxnStruct *)hTxn;
473    TI_UINT32   uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
474
475#ifdef TI_DBG
476    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb()\n");
477    if (pTxn != pTxnQ->pCurrTxn)
478    {
479        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): CB returned pTxn 0x%x  while pCurrTxn is 0x%x !!\n", pTxn, pTxnQ->pCurrTxn);
480    }
481#endif
482
483    /* If the function of the completed Txn is waiting for restart */
484    if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART)
485    {
486        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb(): Handling restart\n");
487
488        /* First, Clear the restarted function queues  */
489        txnQ_ClearQueues (pTxnQ, uFuncId);
490
491        /* Call function CB for current Txn with restart indication */
492        TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_RECOVERY);
493        pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn);
494    }
495
496    /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */
497    else
498    {
499        context_EnterCriticalSection (pTxnQ->hContext);
500        que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pTxn);
501        context_LeaveCriticalSection (pTxnQ->hContext);
502    }
503
504    /* Indicate that no transaction is currently processed in the bus-driver */
505    pTxnQ->pCurrTxn = NULL;
506
507    /* Send queued transactions as possible (TRUE indicates we are in external context) */
508    txnQ_RunScheduler (pTxnQ, NULL);
509}
510
511
512/**
513 * \fn     txnQ_RunScheduler
514 * \brief  Send queued transactions
515 *
516 * Run the scheduler, which issues transactions as long as possible.
517 * Since this function is called from either internal or external (TxnDone) context,
518 *   it handles reentry by setting a bSchedulerPend flag, and running the scheduler again
519 *   when its current iteration is finished.
520 *
521 * \note
522 * \param  pTxnQ     - The module's object
523 * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
524 * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed
525 * \sa
526 */
527static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
528{
529    TI_BOOL bFirstIteration;
530	ETxnStatus eStatus = TXN_STATUS_NONE;
531
532    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler()\n");
533
534    context_EnterCriticalSection (pTxnQ->hContext);
535
536    /* If the scheduler is currently busy, set bSchedulerPend flag and exit */
537    if (pTxnQ->bSchedulerBusy)
538    {
539        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Scheduler is busy\n");
540        pTxnQ->bSchedulerPend = TI_TRUE;
541        context_LeaveCriticalSection (pTxnQ->hContext);
542        return TXN_STATUS_PENDING;
543    }
544
545    /* Indicate that the scheduler is currently busy */
546    pTxnQ->bSchedulerBusy = TI_TRUE;
547
548    context_LeaveCriticalSection (pTxnQ->hContext);
549
550    bFirstIteration = TI_TRUE;
551
552    /*
553     * Run the scheduler while it has work to do
554     */
555    while (1)
556    {
557        /* If first scheduler iteration, save its return code to return the original Txn result */
558        if (bFirstIteration)
559        {
560            eStatus = txnQ_Scheduler (pTxnQ, pInputTxn);
561            bFirstIteration = TI_FALSE;
562        }
563        /* This is for handling pending calls when the scheduler was busy (see above) */
564        else
565        {
566            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Handle pending scheduler call\n");
567            txnQ_Scheduler (pTxnQ, NULL);
568        }
569
570        context_EnterCriticalSection (pTxnQ->hContext);
571
572        /* If no pending calls, clear the busy flag and return the original caller Txn status */
573        if (!pTxnQ->bSchedulerPend)
574        {
575            pTxnQ->bSchedulerBusy = TI_FALSE;
576            context_LeaveCriticalSection (pTxnQ->hContext);
577            return eStatus;
578        }
579        pTxnQ->bSchedulerPend = TI_FALSE;
580
581        context_LeaveCriticalSection (pTxnQ->hContext);
582    }
583}
584
585
586/**
587 * \fn     txnQ_Scheduler
588 * \brief  Send queued transactions
589 *
590 * Issue transactions as long as they are available and the bus is not occupied.
591 * Call CBs of completed transactions, except completion of pInputTxn (covered by the return value).
592 * Note that this function is called from either internal or external (TxnDone) context.
593 * However, the txnQ_RunScheduler which calls it, prevents scheduler reentry.
594 *
595 * \note
596 * \param  pTxnQ     - The module's object
597 * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
598 * \return COMPLETE if pInputTxn completed in this context, PENDING if not, ERROR if failed
599 * \sa     txnQ_RunScheduler
600 */
601static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
602{
603    ETxnStatus eInputTxnStatus;
604
605    /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */
606    eInputTxnStatus = TXN_STATUS_PENDING;
607
608    /* if a previous transaction is in progress, return PENDING */
609    if (pTxnQ->pCurrTxn)
610    {
611        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): pCurrTxn isn't null (0x%x) so exit\n", pTxnQ->pCurrTxn);
612        return TXN_STATUS_PENDING;
613    }
614
615    /* Loop while transactions are available and can be sent to bus driver */
616    while (1)
617    {
618        TTxnStruct   *pSelectedTxn;
619        ETxnStatus    eStatus;
620
621        /* Get next enabled transaction by priority. If none, exit loop. */
622        context_EnterCriticalSection (pTxnQ->hContext);
623        pSelectedTxn = txnQ_SelectTxn (pTxnQ);
624        context_LeaveCriticalSection (pTxnQ->hContext);
625        if (pSelectedTxn == NULL)
626        {
627            break;
628        }
629
630        /* Save transaction in case it will be async (to indicate that the bus driver is busy) */
631        pTxnQ->pCurrTxn = pSelectedTxn;
632
633        /* Send selected transaction to bus driver */
634        eStatus = busDrv_Transact (pTxnQ->hBusDrv, pSelectedTxn);
635
636        /* If we've just sent the input transaction, use the status as the return value */
637        if (pSelectedTxn == pInputTxn)
638        {
639            eInputTxnStatus = eStatus;
640        }
641
642        TRACE3(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Txn 0x%x sent, status = %d, eInputTxnStatus = %d\n", pSelectedTxn, eStatus, eInputTxnStatus);
643
644        /* If transaction completed */
645        if (eStatus != TXN_STATUS_PENDING)
646        {
647            pTxnQ->pCurrTxn = NULL;
648
649            /* If it's not the input transaction, enqueue it in TxnDone queue */
650            if (pSelectedTxn != pInputTxn)
651            {
652                context_EnterCriticalSection (pTxnQ->hContext);
653                que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pSelectedTxn);
654                context_LeaveCriticalSection (pTxnQ->hContext);
655            }
656        }
657
658        /* If pending Exit loop! */
659        else
660        {
661            break;
662        }
663    }
664
665    /* Dequeue completed transactions and call their functional driver CB */
666    /* Note that it's the functional driver CB and not the specific CB in the Txn! */
667    while (1)
668    {
669        TTxnStruct      *pCompletedTxn;
670        TI_UINT32        uFuncId;
671        TTxnQueueDoneCb  fTxnQueueDoneCb;
672        TI_HANDLE        hCbHandle;
673
674        context_EnterCriticalSection (pTxnQ->hContext);
675        pCompletedTxn   = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue);
676        context_LeaveCriticalSection (pTxnQ->hContext);
677        if (pCompletedTxn == NULL)
678        {
679            /* Return the status of the input transaction (PENDING unless sent and completed here) */
680            return eInputTxnStatus;
681        }
682
683        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Calling TxnDone for Txn 0x%x\n", pCompletedTxn);
684
685        uFuncId         = TXN_PARAM_GET_FUNC_ID(pCompletedTxn);
686        fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb;
687        hCbHandle       = pTxnQ->aFuncInfo[uFuncId].hCbHandle;
688
689        fTxnQueueDoneCb (hCbHandle, pCompletedTxn);
690    }
691}
692
693
694/**
695 * \fn     txnQ_SelectTxn
696 * \brief  Select transaction to send
697 *
698 * Called from txnQ_RunScheduler() which is protected in critical section.
699 * Select the next enabled transaction by priority.
700 *
701 * \note
702 * \param  pTxnQ - The module's object
703 * \return The selected transaction to send (NULL if none available)
704 * \sa
705 */
706static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ)
707{
708    TTxnStruct *pSelectedTxn;
709    TI_UINT32   uFunc;
710    TI_UINT32   uPrio;
711
712    /* For all functions, if single-step Txn waiting, return it (sent even if function is stopped) */
713    for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
714    {
715        pSelectedTxn = pTxnQ->aFuncInfo[uFunc].pSingleStep;
716        if (pSelectedTxn != NULL)
717        {
718            pTxnQ->aFuncInfo[uFunc].pSingleStep = NULL;
719            return pSelectedTxn;
720        }
721    }
722
723    /* For all priorities from high to low */
724    for (uPrio = 0; uPrio < MAX_PRIORITY; uPrio++)
725    {
726        /* For all functions */
727        for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
728        {
729            /* If function running and uses this priority */
730            if (pTxnQ->aFuncInfo[uFunc].eState == FUNC_STATE_RUNNING  &&
731                pTxnQ->aFuncInfo[uFunc].uNumPrios > uPrio)
732            {
733                /* Dequeue Txn from current func and priority queue, and if not NULL return it */
734                pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFunc][uPrio]);
735                if (pSelectedTxn != NULL)
736                {
737                    return pSelectedTxn;
738                }
739            }
740        }
741    }
742
743    /* If no transaction was selected, return NULL */
744    return NULL;
745}
746
747
748/**
749 * \fn     txnQ_ClearQueues
750 * \brief  Clear the function queues
751 *
752 * Clear the specified function queues and call its CB for each Txn with status=RECOVERY.
753 *
754 * \note   Called in critical section.
755 * \param  pTxnQ            - The module's object
756 * \param  uFuncId          - The calling functional driver
757 * \return void
758 * \sa
759 */
760static void txnQ_ClearQueues (TTxnQObj *pTxnQ, TI_UINT32 uFuncId)
761{
762    TTxnStruct      *pTxn;
763    TI_UINT32        uPrio;
764
765    pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL;
766
767    /* For all function priorities */
768    for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++)
769    {
770        do
771        {
772            context_EnterCriticalSection (pTxnQ->hContext);
773            /* Dequeue Txn from current priority queue */
774            pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]);
775            context_LeaveCriticalSection (pTxnQ->hContext);
776
777            /*
778             * Drop on Restart
779             * do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback
780             */
781        } while (pTxn != NULL);
782    }
783
784    /* Clear state - for restart (doesn't call txnQ_Open) */
785    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
786}
787
788#ifdef TI_DBG
789void txnQ_PrintQueues (TI_HANDLE hTxnQ)
790{
791    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
792
793    WLAN_OS_REPORT(("Print TXN queues\n"));
794    WLAN_OS_REPORT(("================\n"));
795    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_LOW_PRIORITY]);
796    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_HIGH_PRIORITY]);
797}
798#endif /* TI_DBG */
799