• 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        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 */
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 } TTxnQObj;
126 
127 
128 /************************************************************************
129  * Internal functions prototypes
130  ************************************************************************/
131 static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
132 static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
133 static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
134 static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
135 static void         txnQ_ClearQueues  (TTxnQObj *pTxnQ, TI_UINT32 uFuncId);
136 static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
137 
138 
139 
140 /************************************************************************
141  *
142  *   Module functions implementation
143  *
144  ************************************************************************/
145 
txnQ_Create(TI_HANDLE hOs)146 TI_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 
txnQ_Destroy(TI_HANDLE hTxnQ)186 TI_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 
txnQ_Init(TI_HANDLE hTxnQ,TI_HANDLE hOs,TI_HANDLE hReport,TI_HANDLE hContext)205 void 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 
txnQ_ConnectBus(TI_HANDLE hTxnQ,TBusDrvCfg * pBusDrvCfg,TTxnDoneCb fConnectCb,TI_HANDLE hConnectCb)225 TI_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 
txnQ_DisconnectBus(TI_HANDLE hTxnQ)237 TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
238 {
239     TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
240 
241     return busDrv_DisconnectBus (pTxnQ->hBusDrv);
242 }
243 
txnQ_Open(TI_HANDLE hTxnQ,TI_UINT32 uFuncId,TI_UINT32 uNumPrios,TTxnQueueDoneCb fTxnQueueDoneCb,TI_HANDLE hCbHandle)244 TI_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 
txnQ_Close(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)298 void 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 
txnQ_Restart(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)340 ETxnStatus 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 
txnQ_Run(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)373 void 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 
txnQ_Stop(TI_HANDLE hTxnQ,TI_UINT32 uFuncId)392 void 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 
txnQ_Transact(TI_HANDLE hTxnQ,TTxnStruct * pTxn)408 ETxnStatus 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  */
txnQ_ConnectCB(TI_HANDLE hTxnQ,void * hTxn)447 static 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  */
txnQ_TxnDoneCb(TI_HANDLE hTxnQ,void * hTxn)469 static 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  */
txnQ_RunScheduler(TTxnQObj * pTxnQ,TTxnStruct * pInputTxn)527 static 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  */
txnQ_Scheduler(TTxnQObj * pTxnQ,TTxnStruct * pInputTxn)601 static 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  */
txnQ_SelectTxn(TTxnQObj * pTxnQ)706 static 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  */
txnQ_ClearQueues(TTxnQObj * pTxnQ,TI_UINT32 uFuncId)760 static 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
txnQ_PrintQueues(TI_HANDLE hTxnQ)789 void 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