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