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