• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
84 typedef 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 */
93 typedef 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 */
105 typedef 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  ************************************************************************/
135 static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
136 static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
137 static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
138 static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
139 static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
140 
141 
142 
143 /************************************************************************
144  *
145  *   Module functions implementation
146  *
147  ************************************************************************/
148 
txnQ_Create(TI_HANDLE hOs)149 TI_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 
txnQ_Destroy(TI_HANDLE hTxnQ)192 TI_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 
txnQ_Init(TI_HANDLE hTxnQ,TI_HANDLE hOs,TI_HANDLE hReport,TI_HANDLE hContext)211 void 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 
txnQ_ConnectBus(TI_HANDLE hTxnQ,TBusDrvCfg * pBusDrvCfg,TTxnDoneCb fConnectCb,TI_HANDLE hConnectCb,TI_UINT32 * pRxDmaBufLen,TI_UINT32 * pTxDmaBufLen)231 TI_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 
txnQ_DisconnectBus(TI_HANDLE hTxnQ)248 TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
249 {
250     TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
251 
252     return busDrv_DisconnectBus (pTxnQ->hBusDrv);
253 }
254 
txnQ_Open(TI_HANDLE hTxnQ,TI_UINT32 uFuncId,TI_UINT32 uNumPrios,TTxnQueueDoneCb fTxnQueueDoneCb,TI_HANDLE hCbHandle)255 TI_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 
txnQ_Close(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)309 void 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 
txnQ_Restart(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)351 ETxnStatus 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 
txnQ_Run(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)384 void 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 
txnQ_Stop(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)403 void 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 
txnQ_Transact(TI_HANDLE hTxnQ,TTxnStruct * pTxn)419 ETxnStatus 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  */
txnQ_ConnectCB(TI_HANDLE hTxnQ,void * hTxn)464 static 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  */
txnQ_TxnDoneCb(TI_HANDLE hTxnQ,void * hTxn)486 static 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  */
txnQ_RunScheduler(TTxnQObj * pTxnQ,TTxnStruct * pInputTxn)550 static 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  */
txnQ_Scheduler(TTxnQObj * pTxnQ,TTxnStruct * pInputTxn)624 static 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  */
txnQ_SelectTxn(TTxnQObj * pTxnQ)735 static 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  */
txnQ_ClearQueues(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)819 void 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
txnQ_PrintQueues(TI_HANDLE hTxnQ)851 void 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