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        QUE_UNLIMITED_SIZE
76#define TXN_DONE_QUE_SIZE   QUE_UNLIMITED_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#ifdef TI_DBG
126    TI_HANDLE       pAggregQueue;       /* While Tx aggregation in progress, saves its queue pointer to ensure continuity */
127#endif
128
129} TTxnQObj;
130
131
132/************************************************************************
133 * Internal functions prototypes
134 ************************************************************************/
135static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
136static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
137static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
138static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
139static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
140
141
142
143/************************************************************************
144 *
145 *   Module functions implementation
146 *
147 ************************************************************************/
148
149TI_HANDLE txnQ_Create (TI_HANDLE hOs)
150{
151    TI_HANDLE  hTxnQ;
152    TTxnQObj  *pTxnQ;
153    TI_UINT32  i;
154
155    hTxnQ = os_memoryAlloc(hOs, sizeof(TTxnQObj));
156    if (hTxnQ == NULL)
157        return NULL;
158
159    pTxnQ = (TTxnQObj *)hTxnQ;
160
161    os_memoryZero(hOs, hTxnQ, sizeof(TTxnQObj));
162
163    pTxnQ->hOs             = hOs;
164    pTxnQ->pCurrTxn        = NULL;
165    pTxnQ->uMinFuncId      = MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */
166    pTxnQ->uMaxFuncId      = 0;             /* Start at minimum and save maximal value in txnQ_Open */
167#ifdef TI_DBG
168    pTxnQ->pAggregQueue    = NULL;
169#endif
170
171    for (i = 0; i < MAX_FUNCTIONS; i++)
172    {
173        pTxnQ->aFuncInfo[i].eState          = FUNC_STATE_NONE;
174        pTxnQ->aFuncInfo[i].uNumPrios       = 0;
175        pTxnQ->aFuncInfo[i].pSingleStep     = NULL;
176        pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL;
177        pTxnQ->aFuncInfo[i].hCbHandle       = NULL;
178    }
179
180    /* Create the Bus-Driver module */
181    pTxnQ->hBusDrv = busDrv_Create (hOs);
182    if (pTxnQ->hBusDrv == NULL)
183    {
184        WLAN_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__));
185        txnQ_Destroy (hTxnQ);
186        return NULL;
187    }
188
189    return pTxnQ;
190}
191
192TI_STATUS txnQ_Destroy (TI_HANDLE hTxnQ)
193{
194    TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ;
195
196    if (pTxnQ)
197    {
198        if (pTxnQ->hBusDrv)
199        {
200            busDrv_Destroy (pTxnQ->hBusDrv);
201        }
202        if (pTxnQ->hTxnDoneQueue)
203        {
204            que_Destroy (pTxnQ->hTxnDoneQueue);
205        }
206        os_memoryFree (pTxnQ->hOs, pTxnQ, sizeof(TTxnQObj));
207    }
208    return TI_OK;
209}
210
211void txnQ_Init (TI_HANDLE hTxnQ, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext)
212{
213    TTxnQObj  *pTxnQ = (TTxnQObj*)hTxnQ;
214    TI_UINT32  uNodeHeaderOffset;
215
216    pTxnQ->hOs             = hOs;
217    pTxnQ->hReport         = hReport;
218    pTxnQ->hContext        = hContext;
219
220    /* Create the TxnDone queue. */
221    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode);
222    pTxnQ->hTxnDoneQueue = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset);
223    if (pTxnQ->hTxnDoneQueue == NULL)
224    {
225        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": TxnDone queue creation failed!\n");
226    }
227
228    busDrv_Init (pTxnQ->hBusDrv, hReport);
229}
230
231TI_STATUS txnQ_ConnectBus (TI_HANDLE  hTxnQ,
232                           TBusDrvCfg *pBusDrvCfg,
233                           TTxnDoneCb fConnectCb,
234                           TI_HANDLE  hConnectCb,
235                           TI_UINT32  *pRxDmaBufLen,
236                           TI_UINT32  *pTxDmaBufLen)
237{
238    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
239
240    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_ConnectBus()\n");
241
242    pTxnQ->fConnectCb = fConnectCb;
243    pTxnQ->hConnectCb = hConnectCb;
244
245    return busDrv_ConnectBus (pTxnQ->hBusDrv, pBusDrvCfg, txnQ_TxnDoneCb, hTxnQ, txnQ_ConnectCB, pRxDmaBufLen, pTxDmaBufLen);
246}
247
248TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
249{
250    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
251
252    return busDrv_DisconnectBus (pTxnQ->hBusDrv);
253}
254
255TI_STATUS txnQ_Open (TI_HANDLE       hTxnQ,
256                     TI_UINT32       uFuncId,
257                     TI_UINT32       uNumPrios,
258                     TTxnQueueDoneCb fTxnQueueDoneCb,
259                     TI_HANDLE       hCbHandle)
260{
261    TTxnQObj     *pTxnQ = (TTxnQObj*) hTxnQ;
262    TI_UINT32     uNodeHeaderOffset;
263    TI_UINT32     i;
264
265    if (uFuncId >= MAX_FUNCTIONS  ||  uNumPrios > MAX_PRIORITY)
266    {
267        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Invalid Params!  uFuncId = %d, uNumPrios = %d\n", uFuncId, uNumPrios);
268        return TI_NOK;
269    }
270
271    context_EnterCriticalSection (pTxnQ->hContext);
272
273    /* Save functional driver info */
274    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = uNumPrios;
275    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = fTxnQueueDoneCb;
276    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = hCbHandle;
277    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_STOPPED;
278
279    /* Create the functional driver's queues. */
280    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode);
281    for (i = 0; i < uNumPrios; i++)
282    {
283        pTxnQ->aTxnQueues[uFuncId][i] = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_QUE_SIZE, uNodeHeaderOffset);
284        if (pTxnQ->aTxnQueues[uFuncId][i] == NULL)
285        {
286            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Queues creation failed!\n");
287            context_LeaveCriticalSection (pTxnQ->hContext);
288            return TI_NOK;
289        }
290    }
291
292    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
293    if (uFuncId < pTxnQ->uMinFuncId)
294    {
295        pTxnQ->uMinFuncId = uFuncId;
296    }
297    if (uFuncId > pTxnQ->uMaxFuncId)
298    {
299        pTxnQ->uMaxFuncId = uFuncId;
300    }
301
302    context_LeaveCriticalSection (pTxnQ->hContext);
303
304    TRACE2(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d registered successfully, uNumPrios = %d\n", uFuncId, uNumPrios);
305
306    return TI_OK;
307}
308
309void txnQ_Close (TI_HANDLE  hTxnQ, TI_UINT32 uFuncId)
310{
311    TTxnQObj     *pTxnQ = (TTxnQObj*)hTxnQ;
312    TI_UINT32     i;
313
314    context_EnterCriticalSection (pTxnQ->hContext);
315
316    /* Destroy the functional driver's queues */
317    for (i = 0; i < pTxnQ->aFuncInfo[uFuncId].uNumPrios; i++)
318    {
319        que_Destroy (pTxnQ->aTxnQueues[uFuncId][i]);
320    }
321
322    /* Clear functional driver info */
323    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = 0;
324    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = NULL;
325    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = NULL;
326    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_NONE;
327
328    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
329    pTxnQ->uMinFuncId      = MAX_FUNCTIONS;
330    pTxnQ->uMaxFuncId      = 0;
331    for (i = 0; i < MAX_FUNCTIONS; i++)
332    {
333        if (pTxnQ->aFuncInfo[i].eState != FUNC_STATE_NONE)
334        {
335            if (i < pTxnQ->uMinFuncId)
336            {
337                pTxnQ->uMinFuncId = i;
338            }
339            if (i > pTxnQ->uMaxFuncId)
340            {
341                pTxnQ->uMaxFuncId = i;
342            }
343        }
344    }
345
346    context_LeaveCriticalSection (pTxnQ->hContext);
347
348    TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d Unregistered\n", uFuncId);
349}
350
351ETxnStatus txnQ_Restart (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
352{
353    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
354
355    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart()\n");
356
357    context_EnterCriticalSection (pTxnQ->hContext);
358
359    /* If a Txn from the calling function is in progress, set state to RESTART return PENDING */
360    if (pTxnQ->pCurrTxn)
361    {
362        if (TXN_PARAM_GET_FUNC_ID(pTxnQ->pCurrTxn) == uFuncId)
363        {
364            pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RESTART;
365
366            context_LeaveCriticalSection (pTxnQ->hContext);
367
368            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart(): pCurrTxn pending\n");
369
370            /* Return PENDING to indicate that the restart will be completed later (in TxnDone) */
371            return TXN_STATUS_PENDING;
372        }
373    }
374
375    context_LeaveCriticalSection (pTxnQ->hContext);
376
377    /* Clear the calling function's queues (call function CB with status=RECOVERY) */
378    txnQ_ClearQueues (hTxnQ, uFuncId);
379
380    /* Return COMPLETE to indicate that the restart was completed */
381    return TXN_STATUS_COMPLETE;
382}
383
384void txnQ_Run (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
385{
386    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
387
388#ifdef TI_DBG
389    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Run()\n");
390    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_STOPPED)
391    {
392        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_WARNING, "txnQ_Run(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
393    }
394#endif
395
396    /* Enable function's queues */
397    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
398
399    /* Send queued transactions as possible */
400    txnQ_RunScheduler (pTxnQ, NULL);
401}
402
403void txnQ_Stop (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
404{
405    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
406
407#ifdef TI_DBG
408    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Stop()\n");
409    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_RUNNING)
410    {
411        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Stop(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
412    }
413#endif
414
415    /* Enable function's queues */
416    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED;
417}
418
419ETxnStatus txnQ_Transact (TI_HANDLE hTxnQ, TTxnStruct *pTxn)
420{
421    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
422    TI_UINT32    uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
423    ETxnStatus   rc;
424
425    if (TXN_PARAM_GET_SINGLE_STEP(pTxn))
426    {
427        pTxnQ->aFuncInfo[uFuncId].pSingleStep = pTxn;
428        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Single step Txn\n");
429    }
430    else
431    {
432        TI_STATUS eStatus;
433        TI_HANDLE hQueue = pTxnQ->aTxnQueues[uFuncId][TXN_PARAM_GET_PRIORITY(pTxn)];
434        context_EnterCriticalSection (pTxnQ->hContext);
435        eStatus = que_Enqueue (hQueue, (TI_HANDLE)pTxn);
436        context_LeaveCriticalSection (pTxnQ->hContext);
437        if (eStatus != TI_OK)
438        {
439            TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Transact(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pTxn, pTxn->uHwAddr, pTxn->aLen[0]);
440            return TXN_STATUS_ERROR;
441        }
442        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Regular Txn\n");
443    }
444
445    /* Send queued transactions as possible */
446    rc = txnQ_RunScheduler (pTxnQ, pTxn);
447
448    return rc;
449}
450
451
452/**
453 * \fn     txnQ_ConnectCB
454 * \brief  Pending Connection completion CB
455 *
456 *  txnQ_ConnectBus CB
457 *
458 * \note
459 * \param  hTxnQ - The module's object
460 * \param  pTxn  - The completed transaction object
461 * \return void
462 * \sa
463 */
464static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn)
465{
466    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
467
468    /* Call the Client Connect CB */
469    pTxnQ->fConnectCb (pTxnQ->hConnectCb, NULL);
470}
471
472
473/**
474 * \fn     txnQ_TxnDoneCb
475 * \brief  Pending Transaction completion CB
476 *
477 * Called back by bus-driver upon pending transaction completion in TxnDone context (external!).
478 * Enqueue completed transaction in TxnDone queue and call scheduler to send queued transactions.
479 *
480 * \note
481 * \param  hTxnQ - The module's object
482 * \param  pTxn  - The completed transaction object
483 * \return void
484 * \sa
485 */
486static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn)
487{
488    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
489    TTxnStruct *pTxn    = (TTxnStruct *)hTxn;
490    TI_UINT32   uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
491
492#ifdef TI_DBG
493    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb()\n");
494    if (pTxn != pTxnQ->pCurrTxn)
495    {
496        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): CB returned pTxn 0x%x  while pCurrTxn is 0x%x !!\n", pTxn, pTxnQ->pCurrTxn);
497    }
498#endif
499
500    /* If the function of the completed Txn is waiting for restart */
501    if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART)
502    {
503        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb(): Handling restart\n");
504
505        /* First, Clear the restarted function queues  */
506        txnQ_ClearQueues (hTxnQ, uFuncId);
507
508        /* Call function CB for current Txn with restart indication */
509        TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_RECOVERY);
510        pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn);
511    }
512
513    /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */
514    else
515    {
516        TI_STATUS eStatus;
517
518        context_EnterCriticalSection (pTxnQ->hContext);
519        eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pTxn);
520        if (eStatus != TI_OK)
521        {
522            TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pTxn, pTxn->uHwAddr, pTxn->aLen[0]);
523        }
524        context_LeaveCriticalSection (pTxnQ->hContext);
525    }
526
527    /* Indicate that no transaction is currently processed in the bus-driver */
528    pTxnQ->pCurrTxn = NULL;
529
530    /* Send queued transactions as possible (TRUE indicates we are in external context) */
531    txnQ_RunScheduler (pTxnQ, NULL);
532}
533
534
535/**
536 * \fn     txnQ_RunScheduler
537 * \brief  Send queued transactions
538 *
539 * Run the scheduler, which issues transactions as long as possible.
540 * Since this function is called from either internal or external (TxnDone) context,
541 *   it handles reentry by setting a bSchedulerPend flag, and running the scheduler again
542 *   when its current iteration is finished.
543 *
544 * \note
545 * \param  pTxnQ     - The module's object
546 * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
547 * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed
548 * \sa
549 */
550static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
551{
552    TI_BOOL bFirstIteration;
553	ETxnStatus eStatus = TXN_STATUS_NONE;
554
555    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler()\n");
556
557    context_EnterCriticalSection (pTxnQ->hContext);
558
559    /* If the scheduler is currently busy, set bSchedulerPend flag and exit */
560    if (pTxnQ->bSchedulerBusy)
561    {
562        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Scheduler is busy\n");
563        pTxnQ->bSchedulerPend = TI_TRUE;
564        context_LeaveCriticalSection (pTxnQ->hContext);
565        return TXN_STATUS_PENDING;
566    }
567
568    /* Indicate that the scheduler is currently busy */
569    pTxnQ->bSchedulerBusy = TI_TRUE;
570
571    context_LeaveCriticalSection (pTxnQ->hContext);
572
573    bFirstIteration = TI_TRUE;
574
575    /*
576     * Run the scheduler while it has work to do
577     */
578    while (1)
579    {
580        /* If first scheduler iteration, save its return code to return the original Txn result */
581        if (bFirstIteration)
582        {
583            eStatus = txnQ_Scheduler (pTxnQ, pInputTxn);
584            bFirstIteration = TI_FALSE;
585        }
586        /* This is for handling pending calls when the scheduler was busy (see above) */
587        else
588        {
589            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Handle pending scheduler call\n");
590            txnQ_Scheduler (pTxnQ, NULL);
591        }
592
593        context_EnterCriticalSection (pTxnQ->hContext);
594
595        /* If no pending calls, clear the busy flag and return the original caller Txn status */
596        if (!pTxnQ->bSchedulerPend)
597        {
598            pTxnQ->bSchedulerBusy = TI_FALSE;
599            context_LeaveCriticalSection (pTxnQ->hContext);
600            return eStatus;
601        }
602        pTxnQ->bSchedulerPend = TI_FALSE;
603
604        context_LeaveCriticalSection (pTxnQ->hContext);
605    }
606}
607
608
609/**
610 * \fn     txnQ_Scheduler
611 * \brief  Send queued transactions
612 *
613 * Issue transactions as long as they are available and the bus is not occupied.
614 * Call CBs of completed transactions, except completion of pInputTxn (covered by the return value).
615 * Note that this function is called from either internal or external (TxnDone) context.
616 * However, the txnQ_RunScheduler which calls it, prevents scheduler reentry.
617 *
618 * \note
619 * \param  pTxnQ     - The module's object
620 * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
621 * \return COMPLETE if pInputTxn completed in this context, PENDING if not, ERROR if failed
622 * \sa     txnQ_RunScheduler
623 */
624static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
625{
626    ETxnStatus eInputTxnStatus;
627
628    /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */
629    eInputTxnStatus = TXN_STATUS_PENDING;
630
631    /* if a previous transaction is in progress, return PENDING */
632    if (pTxnQ->pCurrTxn)
633    {
634        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): pCurrTxn isn't null (0x%x) so exit\n", pTxnQ->pCurrTxn);
635        return TXN_STATUS_PENDING;
636    }
637
638    /* Loop while transactions are available and can be sent to bus driver */
639    while (1)
640    {
641        TTxnStruct   *pSelectedTxn;
642        ETxnStatus    eStatus;
643
644        /* Get next enabled transaction by priority. If none, exit loop. */
645        context_EnterCriticalSection (pTxnQ->hContext);
646        pSelectedTxn = txnQ_SelectTxn (pTxnQ);
647        context_LeaveCriticalSection (pTxnQ->hContext);
648        if (pSelectedTxn == NULL)
649        {
650            break;
651        }
652
653        /* Save transaction in case it will be async (to indicate that the bus driver is busy) */
654        pTxnQ->pCurrTxn = pSelectedTxn;
655
656        /* Send selected transaction to bus driver */
657        eStatus = busDrv_Transact (pTxnQ->hBusDrv, pSelectedTxn);
658
659        /* If we've just sent the input transaction, use the status as the return value */
660        if (pSelectedTxn == pInputTxn)
661        {
662            eInputTxnStatus = eStatus;
663        }
664
665        TRACE3(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Txn 0x%x sent, status = %d, eInputTxnStatus = %d\n", pSelectedTxn, eStatus, eInputTxnStatus);
666
667        /* If transaction completed */
668        if (eStatus != TXN_STATUS_PENDING)
669        {
670            pTxnQ->pCurrTxn = NULL;
671
672            /* If it's not the input transaction, enqueue it in TxnDone queue */
673            if (pSelectedTxn != pInputTxn)
674            {
675                TI_STATUS eStatus;
676
677                context_EnterCriticalSection (pTxnQ->hContext);
678                eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pSelectedTxn);
679                if (eStatus != TI_OK)
680                {
681                    TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Scheduler(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pSelectedTxn, pSelectedTxn->uHwAddr, pSelectedTxn->aLen[0]);
682                }
683                context_LeaveCriticalSection (pTxnQ->hContext);
684            }
685        }
686
687        /* If pending Exit loop! */
688        else
689        {
690            break;
691        }
692    }
693
694    /* Dequeue completed transactions and call their functional driver CB */
695    /* Note that it's the functional driver CB and not the specific CB in the Txn! */
696    while (1)
697    {
698        TTxnStruct      *pCompletedTxn;
699        TI_UINT32        uFuncId;
700        TTxnQueueDoneCb  fTxnQueueDoneCb;
701        TI_HANDLE        hCbHandle;
702
703        context_EnterCriticalSection (pTxnQ->hContext);
704        pCompletedTxn   = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue);
705        context_LeaveCriticalSection (pTxnQ->hContext);
706        if (pCompletedTxn == NULL)
707        {
708            /* Return the status of the input transaction (PENDING unless sent and completed here) */
709            return eInputTxnStatus;
710        }
711
712        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Calling TxnDone for Txn 0x%x\n", pCompletedTxn);
713
714        uFuncId         = TXN_PARAM_GET_FUNC_ID(pCompletedTxn);
715        fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb;
716        hCbHandle       = pTxnQ->aFuncInfo[uFuncId].hCbHandle;
717
718        fTxnQueueDoneCb (hCbHandle, pCompletedTxn);
719    }
720}
721
722
723/**
724 * \fn     txnQ_SelectTxn
725 * \brief  Select transaction to send
726 *
727 * Called from txnQ_RunScheduler() which is protected in critical section.
728 * Select the next enabled transaction by priority.
729 *
730 * \note
731 * \param  pTxnQ - The module's object
732 * \return The selected transaction to send (NULL if none available)
733 * \sa
734 */
735static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ)
736{
737    TTxnStruct *pSelectedTxn;
738    TI_UINT32   uFunc;
739    TI_UINT32   uPrio;
740
741#ifdef TI_DBG
742    /* If within Tx aggregation, dequeue Txn from same queue, and if not NULL return it */
743    if (pTxnQ->pAggregQueue)
744    {
745        pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->pAggregQueue);
746        if (pSelectedTxn != NULL)
747        {
748            /* If aggregation ended, reset the aggregation-queue pointer */
749            if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_OFF)
750            {
751                if ((TXN_PARAM_GET_FIXED_ADDR(pSelectedTxn) != TXN_FIXED_ADDR) ||
752                    (TXN_PARAM_GET_DIRECTION(pSelectedTxn)  != TXN_DIRECTION_WRITE))
753                {
754                    TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_SelectTxn: Mixed transaction during aggregation, HwAddr=0x%x, TxnParams=0x%x\n", pSelectedTxn->uHwAddr, pSelectedTxn->uTxnParams);
755                }
756                pTxnQ->pAggregQueue = NULL;
757            }
758            return pSelectedTxn;
759        }
760        return NULL;
761    }
762#endif
763
764    /* For all functions, if single-step Txn waiting, return it (sent even if function is stopped) */
765    for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
766    {
767        pSelectedTxn = pTxnQ->aFuncInfo[uFunc].pSingleStep;
768        if (pSelectedTxn != NULL)
769        {
770            pTxnQ->aFuncInfo[uFunc].pSingleStep = NULL;
771            return pSelectedTxn;
772        }
773    }
774
775    /* For all priorities from high to low */
776    for (uPrio = 0; uPrio < MAX_PRIORITY; uPrio++)
777    {
778        /* For all functions */
779        for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
780        {
781            /* If function running and uses this priority */
782            if (pTxnQ->aFuncInfo[uFunc].eState == FUNC_STATE_RUNNING  &&
783                pTxnQ->aFuncInfo[uFunc].uNumPrios > uPrio)
784            {
785                /* Dequeue Txn from current func and priority queue, and if not NULL return it */
786                pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFunc][uPrio]);
787                if (pSelectedTxn != NULL)
788                {
789#ifdef TI_DBG
790                    /* If aggregation begins, save the aggregation-queue pointer to ensure continuity */
791                    if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_ON)
792                    {
793                        pTxnQ->pAggregQueue = pTxnQ->aTxnQueues[uFunc][uPrio];
794                    }
795#endif
796                    return pSelectedTxn;
797                }
798            }
799        }
800    }
801
802    /* If no transaction was selected, return NULL */
803    return NULL;
804}
805
806
807/**
808 * \fn     txnQ_ClearQueues
809 * \brief  Clear the function queues
810 *
811 * Clear the specified function queues and call its CB for each Txn with status=RECOVERY.
812 *
813 * \note   Called in critical section.
814 * \param  hTxnQ            - The module's object
815 * \param  uFuncId          - The calling functional driver
816 * \return void
817 * \sa
818 */
819void txnQ_ClearQueues (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
820{
821    TTxnQObj        *pTxnQ = (TTxnQObj*)hTxnQ;
822    TTxnStruct      *pTxn;
823    TI_UINT32        uPrio;
824
825    context_EnterCriticalSection (pTxnQ->hContext);
826
827    pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL;
828
829    /* For all function priorities */
830    for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++)
831    {
832        do
833        {
834            /* Dequeue Txn from current priority queue */
835            pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]);
836
837            /*
838             * Drop on Restart
839             * do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback
840             */
841        } while (pTxn != NULL);
842    }
843
844    /* Clear state - for restart (doesn't call txnQ_Open) */
845    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
846
847    context_LeaveCriticalSection (pTxnQ->hContext);
848}
849
850#ifdef TI_DBG
851void txnQ_PrintQueues (TI_HANDLE hTxnQ)
852{
853    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
854
855    WLAN_OS_REPORT(("Print TXN queues\n"));
856    WLAN_OS_REPORT(("================\n"));
857    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_LOW_PRIORITY]);
858    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_HIGH_PRIORITY]);
859}
860#endif /* TI_DBG */
861