summaryrefslogtreecommitdiff
path: root/wl1271/TWD/FW_Transfer/RxXfer.c
blob: 9e82964534b3aa94db8058ae82ee9e8382e35dfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
/*
 * RxXfer.c
 *
 * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved.      
 * All rights reserved.                                                  
 *                                                                       
 * Redistribution and use in source and binary forms, with or without    
 * modification, are permitted provided that the following conditions    
 * are met:                                                              
 *                                                                       
 *  * Redistributions of source code must retain the above copyright     
 *    notice, this list of conditions and the following disclaimer.      
 *  * Redistributions in binary form must reproduce the above copyright  
 *    notice, this list of conditions and the following disclaimer in    
 *    the documentation and/or other materials provided with the         
 *    distribution.                                                      
 *  * Neither the name Texas Instruments nor the names of its            
 *    contributors may be used to endorse or promote products derived    
 *    from this software without specific prior written permission.      
 *                                                                       
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/****************************************************************************
 *
 *   MODULE:  rxXfer.c
 *
 *   PURPOSE: Rx Xfer module implementation.Responsible for reading Rx from the FW
 *              and forward it to the upper layers.
 * 
 ****************************************************************************/

#define __FILE_ID__  FILE_ID_106
#include "tidef.h"
#include "osApi.h"
#include "report.h"
#include "rxXfer_api.h"
#include "FwEvent_api.h"
#include "TWDriverInternal.h"
#include "RxQueue_api.h"
#include "TwIf.h"
#include "public_host_int.h"
#include "bmtrace_api.h"

#define RX_DRIVER_COUNTER_ADDRESS 0x300538
#define PLCP_HEADER_LENGTH 8
#define WORD_SIZE   4
#define UNALIGNED_PAYLOAD   0x1
#define RX_DESCRIPTOR_SIZE          (sizeof(RxIfDescriptor_t))
#define MAX_PACKETS_NUMBER          8
#define MAX_CONSECUTIVE_READ_TXN    16
#define MAX_PACKET_SIZE             8192    /* Max Txn size */

#ifdef PLATFORM_SYMBIAN	/* UMAC is using only one buffer and therefore we can't use consecutive reads */
    #define MAX_CONSECUTIVE_READS   1
#else
    #define MAX_CONSECUTIVE_READS   8
#endif

#define SLV_MEM_CP_VALUE(desc, offset)  (((RX_DESC_GET_MEM_BLK(desc) << 8) + offset))
#define ALIGNMENT_SIZE(desc)            ((RX_DESC_GET_UNALIGNED(desc) & UNALIGNED_PAYLOAD) ? 2 : 0)

#if (NUM_RX_PKT_DESC & (NUM_RX_PKT_DESC - 1))
    #error  NUM_RX_PKT_DESC is not a power of 2 which may degrade performance when we calculate modulo!!
#endif


#ifdef TI_DBG
typedef struct
{
    TI_UINT32           uCountFwEvents;
    TI_UINT32           uCountPktsForward;
    TI_UINT32           uCountBufPend;
    TI_UINT32           uCountBufNoMem;
    TI_UINT32           uCountPktAggreg[MAX_XFER_BUFS];

} TRxXferDbgStat;
#endif

typedef struct
{
    TTxnStruct          tTxnStruct;
    TI_UINT32           uRegData;
    TI_UINT32           uRegAdata;

} TRegTxn;

typedef struct
{
    TTxnStruct          tTxnStruct;
    TI_UINT32           uCounter;

} TCounterTxn;

typedef struct
{
    TI_HANDLE           hOs;
    TI_HANDLE           hReport;
    TI_HANDLE           hTwIf;
    TI_HANDLE           hFwEvent;
    TI_HANDLE           hRxQueue;

    TI_UINT32           aRxPktsDesc[NUM_RX_PKT_DESC];           /* Save Rx packets short descriptors from FwStatus */
    TI_UINT32           uFwRxCntr;                              /* Save last FW packets counter from FwStatus */
    TI_UINT32           uDrvRxCntr;                             /* The current driver processed packets counter */
    TI_UINT32           uPacketMemoryPoolStart;                 /* The FW mem-blocks area base address */
    TI_UINT32           uMaxAggregLen;                          /* The max length in bytes of aggregated packets transaction */
    TI_UINT32           uMaxAggregPkts;                         /* The max number of packets that may be aggregated in one transaction */
    TRequestForBufferCb RequestForBufferCB;                     /* Upper layer CB for allocating buffers for packets */
    TI_HANDLE           RequestForBufferCB_handle;              /* The upper later CB handle */
    TI_BOOL             bPendingBuffer;                         /* If TRUE, we exited the Rx handler upon pending-buffer */

    TI_UINT32           uCurrTxnIndex;                          /* The current Txn structures index to use */
    TI_UINT32           uAvailableTxn;                          /* Number of Txn structures currently available */
    TRegTxn             aSlaveRegTxn[MAX_CONSECUTIVE_READ_TXN]; /* Txn structures for writing mem-block address reg */
    TTxnStruct          aTxnStruct[MAX_CONSECUTIVE_READ_TXN];   /* Txn structures for reading the Rx packets */
    TCounterTxn         aCounterTxn[MAX_CONSECUTIVE_READ_TXN];  /* Txn structures for writing the driver counter workaround */

    TI_UINT8            aTempBuffer[MAX_PACKET_SIZE];           /* Dummy buffer to use if we couldn't get a buffer for the packet (so drop the packet) */
    TFailureEventCb     fErrCb;                                 /* The upper layer CB function for error handling */
    TI_HANDLE           hErrCb;                                 /* The CB function handle */

#ifdef TI_DBG
    TRxXferDbgStat      tDbgStat;
#endif

} TRxXfer;


/************************ static function declaration *****************************/
static TI_STATUS rxXfer_Handle(TI_HANDLE hRxXfer);
static void rxXfer_TxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct* pTxn);
static void         rxXfer_PktDropTxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn);
static ETxnStatus   rxXfer_IssueTxn (TI_HANDLE hRxXfer, TI_UINT32 uFirstMemBlkAddr);
static void         rxXfer_ForwardPacket (TRxXfer* pRxXfer, TTxnStruct* pTxn);


/****************************************************************************
 *                      RxXfer_Create()
 ****************************************************************************
 * DESCRIPTION: Create the RxXfer module object 
 * 
 * INPUTS:  None
 * 
 * OUTPUT:  None
 * 
 * RETURNS: The Created object
 ****************************************************************************/
TI_HANDLE rxXfer_Create (TI_HANDLE hOs)
{
    TRxXfer *pRxXfer;

    pRxXfer = os_memoryAlloc (hOs, sizeof(TRxXfer));
    if (pRxXfer == NULL)
        return NULL;

    /* For all the counters */
    os_memoryZero (hOs, pRxXfer, sizeof(TRxXfer));

    pRxXfer->hOs = hOs;

    return (TI_HANDLE)pRxXfer;
}


/****************************************************************************
 *                      RxXfer_Destroy()
 ****************************************************************************
 * DESCRIPTION: Destroy the RxXfer module object 
 * 
 * INPUTS:  hRxXfer - The object to free
 * 
 * OUTPUT:  None
 * 
 * RETURNS: 
 ****************************************************************************/
void rxXfer_Destroy (TI_HANDLE hRxXfer)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    if (pRxXfer)
    {
        os_memoryFree (pRxXfer->hOs, pRxXfer, sizeof(TRxXfer));
    }
}


/****************************************************************************
 *                      rxXfer_init()
 ****************************************************************************
 * DESCRIPTION: Init the module object
 * 
 * INPUTS:      hRxXfer - module handle;
 *              other modules handles.
 * 
 * OUTPUT:  None
 * 
 * RETURNS: None
 ****************************************************************************/
void rxXfer_Init(TI_HANDLE hRxXfer,
                 TI_HANDLE hFwEvent, 
                 TI_HANDLE hReport,
                 TI_HANDLE hTwIf,
                 TI_HANDLE hRxQueue)
{
    TRxXfer *pRxXfer        = (TRxXfer *)hRxXfer;
    pRxXfer->hFwEvent       = hFwEvent;
    pRxXfer->hReport        = hReport;
    pRxXfer->hTwIf          = hTwIf;
    pRxXfer->hRxQueue       = hRxQueue;

    rxXfer_Restart (hRxXfer);

#ifdef TI_DBG   
    rxXfer_ClearStats (pRxXfer);
#endif
}


/****************************************************************************
 *                      rxXfer_SetDefaults()
 ****************************************************************************
 * DESCRIPTION: Set module parameters default setting
 *
 * INPUTS:      hRxXfer - module handle;
 *
 * OUTPUT:  None
 *
 * RETURNS: None
 ****************************************************************************/
void rxXfer_SetDefaults (TI_HANDLE hRxXfer, TTwdInitParams *pInitParams)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    pRxXfer->uMaxAggregPkts = pInitParams->tGeneral.uRxAggregPktsLimit;
}


/****************************************************************************
 *                      rxXfer_SetBusParams()
 ****************************************************************************
 * DESCRIPTION: Configure bus driver DMA-able buffer length to be used as a limit to the aggragation length.
 * 
 * INPUTS:      hRxXfer    - module handle
 *              uDmaBufLen - The bus driver DMA-able buffer length
 * 
 * OUTPUT:  None
 * 
 * RETURNS: None
 ****************************************************************************/
void rxXfer_SetBusParams (TI_HANDLE hRxXfer, TI_UINT32 uDmaBufLen)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    pRxXfer->uMaxAggregLen = uDmaBufLen;
}


/****************************************************************************
 *                      rxXfer_Register_CB()
 ****************************************************************************
 * DESCRIPTION: Register the function to be called for request for buffer.
 * 
 * INPUTS:      hRxXfer       - RxXfer handle;
 * 
 * OUTPUT:  None
 * 
 * RETURNS: None
 ****************************************************************************/
void rxXfer_Register_CB (TI_HANDLE hRxXfer, TI_UINT32 CallBackID, void *CBFunc, TI_HANDLE CBObj)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    TRACE1(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_Register_CB (Value = 0x%x)\n", CallBackID);

    switch(CallBackID)
    {
    case TWD_INT_REQUEST_FOR_BUFFER:       
        pRxXfer->RequestForBufferCB = (TRequestForBufferCb)CBFunc;
        pRxXfer->RequestForBufferCB_handle = CBObj;
        break;

    default:
        TRACE0(pRxXfer->hReport, REPORT_SEVERITY_ERROR, "rxXfer_Register_CB - Illegal value\n");
        return;
    }
}


/****************************************************************************
 *                      rxXfer_ForwardPacket()
 ****************************************************************************
 * DESCRIPTION:  Forward received packet(s) to the upper layers.
 *
 * INPUTS:      
 * 
 * OUTPUT:      
 * 
 * RETURNS:     
 ****************************************************************************/
static void rxXfer_ForwardPacket (TRxXfer *pRxXfer, TTxnStruct *pTxn)
{
    TI_UINT32 uBufNum;
    RxIfDescriptor_t *pRxInfo  = (RxIfDescriptor_t*)(pTxn->aBuf[0]);
#ifdef TI_DBG   /* for packet sanity check */
    TI_UINT16        uLenFromRxInfo;
#endif

    /* Go over all occupied Txn buffers and forward their Rx packets upward */
    for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++)
    {
        /* If no more buffers, exit the loop */
        if (pTxn->aLen[uBufNum] == 0)
        {
            break;
        }

#ifdef TI_DBG   /* Packet sanity check */
        /* Get length from RxInfo, handle endianess and convert to length in bytes */
        pRxInfo = (RxIfDescriptor_t*)(pTxn->aBuf[uBufNum]);
        uLenFromRxInfo = ENDIAN_HANDLE_WORD(pRxInfo->length) << 2;

        /* If the length in the RxInfo is different than in the short descriptor, set error status */
        if (pTxn->aLen[uBufNum] != uLenFromRxInfo)
        {
            TRACE3(pRxXfer->hReport, REPORT_SEVERITY_ERROR , "rxXfer_ForwardPacket: Bad Length!! RxInfoLength=%d, ShortDescLen=%d, RxInfoStatus=0x%x\n", uLenFromRxInfo, pTxn->aLen[uBufNum], pRxInfo->status);

            pRxInfo->status &= ~RX_DESC_STATUS_MASK;
            pRxInfo->status |= RX_DESC_STATUS_DRIVER_RX_Q_FAIL;
            pRxInfo->length = ENDIAN_HANDLE_WORD(pTxn->aLen[uBufNum] >> 2);

            /* If error CB available, trigger recovery !! */
            if (pRxXfer->fErrCb)
            {
                pRxXfer->fErrCb (pRxXfer->hErrCb, RX_XFER_FAILURE);
            }
        }
        else 
        {
            TRACE2(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_ForwardPacket: RxInfoLength=%d, RxInfoStatus=0x%x\n", uLenFromRxInfo, pRxInfo->status);
        }
        pRxXfer->tDbgStat.uCountPktsForward++;
#endif

        /* This is the last packet in the Burst so mark its EndOfBurst flag */
        if (TXN_PARAM_GET_END_OF_BURST(pTxn) && (uBufNum == (MAX_XFER_BUFS - 1) || pTxn->aLen[uBufNum + 1] == 0))
        {
            TXN_PARAM_SET_END_OF_BURST(pTxn, 0);
            pRxInfo->driverFlags |= DRV_RX_FLAG_END_OF_BURST;
        }
        /* Forward received packet to the upper layers */
        RxQueue_ReceivePacket (pRxXfer->hRxQueue, (const void *)pTxn->aBuf[uBufNum]);
    }

    /* reset the aBuf field for clean on recovery purpose */
    pTxn->aBuf[0] = 0;
}


/****************************************************************************
 *                      rxXfer_RxEvent()
 ****************************************************************************
 * DESCRIPTION: Called upon Rx event from the FW.calls the SM  
 * 
 * INPUTS:      hRxXfer       - RxXfer handle;
 * 
 * OUTPUT:  None
 * 
 * RETURNS: TWIF_OK in case of Synch mode, or TWIF_PENDING in case of Asynch mode
 *          (when returning TWIF_PENDING, FwEvent module expects the FwEvent_EventComplete()
 *          function call to finish the Rx Client handling 
 *
 ****************************************************************************/
ETxnStatus rxXfer_RxEvent (TI_HANDLE hRxXfer, FwStatus_t *pFwStatus)
{
    TRxXfer        *pRxXfer = (TRxXfer *)hRxXfer;
    TI_UINT32      uTempCounters;
    FwStatCntrs_t  *pFwStatusCounters;
    TI_UINT32       i;
    TI_STATUS   rc;
    CL_TRACE_START_L2();
  
    uTempCounters = ENDIAN_HANDLE_LONG (pFwStatus->counters);
    pFwStatusCounters = (FwStatCntrs_t*)(&uTempCounters);

    TRACE2(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_RxEvent: NewFwCntr=%d, OldFwCntr=%d\n", pFwStatusCounters->fwRxCntr, pRxXfer->uFwRxCntr);

    /* If no new Rx packets - exit */
    if ((pFwStatusCounters->fwRxCntr % NUM_RX_PKT_DESC) == (pRxXfer->uFwRxCntr % NUM_RX_PKT_DESC))
    {
        CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", "");
        return TXN_STATUS_COMPLETE;
    }

#ifdef TI_DBG
    pRxXfer->tDbgStat.uCountFwEvents++;
#endif

    /* Save current FW counter and Rx packets short descriptors for processing */
    pRxXfer->uFwRxCntr = pFwStatusCounters->fwRxCntr;
    for (i = 0; i < NUM_RX_PKT_DESC; i++)
    {
        pRxXfer->aRxPktsDesc[i] = ENDIAN_HANDLE_LONG (pFwStatus->rxPktsDesc[i]); 
    }

    /* Handle all new Rx packets */
    rc = rxXfer_Handle (pRxXfer);

    CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", "");
    return TXN_STATUS_COMPLETE;
}


/****************************************************************************
 *                      rxXfer_Handle()
 ****************************************************************************
 * DESCRIPTION: 
 *
 * INPUTS:      hRxXfer       - RxXfer handle;
 * 
 * OUTPUT:      
 * 
 * RETURNS:     
 ****************************************************************************/
static TI_STATUS rxXfer_Handle(TI_HANDLE hRxXfer)
{
#ifndef _VLCT_
    TRxXfer *        pRxXfer          = (TRxXfer *)hRxXfer;
    TI_BOOL          bIssueTxn        = TI_FALSE; /* If TRUE transact current aggregated packets */
    TI_BOOL          bDropLastPkt     = TI_FALSE; /* If TRUE, need to drop last packet (RX_BUF_ALLOC_OUT_OF_MEM) */
    TI_BOOL          bExit            = TI_FALSE; /* If TRUE, can't process further packets so exit (after serving the other flags) */
    TI_UINT32        uAggregPktsNum   = 0;        /* Number of aggregated packets */
    TI_UINT32        uFirstMemBlkAddr = 0;
    TI_UINT32        uRxDesc          = 0;
    TI_UINT32        uBuffSize        = 0;
    TI_UINT32        uTotalAggregLen  = 0;
    TI_UINT32        uDrvIndex;
    TI_UINT32        uFwIndex;
    TI_UINT8 *       pHostBuf;
    TTxnStruct *     pTxn = NULL;
    ETxnStatus       eTxnStatus;
    ERxBufferStatus  eBufStatus;
    PacketClassTag_e eRxPacketType;
    CL_TRACE_START_L2();


    /* If no Txn structures available exit!! (fatal error - not expected to happen) */
    if (pRxXfer->uAvailableTxn == 0 )
    {
        TRACE0(pRxXfer->hReport, REPORT_SEVERITY_ERROR, "rxXfer_Handle: No available Txn structures left!\n");
        CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", "");
        return TI_NOK;
    }

    uFwIndex = pRxXfer->uFwRxCntr % NUM_RX_PKT_DESC;

    /* Loop while Rx packets can be transfered from the FW */
    while (1)
    {
        uDrvIndex = pRxXfer->uDrvRxCntr % NUM_RX_PKT_DESC;

        /* If there are unprocessed Rx packets */
        if (uDrvIndex != uFwIndex)
        {
            /* Get next packte info */
            uRxDesc       = pRxXfer->aRxPktsDesc[uDrvIndex];
            uBuffSize     = RX_DESC_GET_LENGTH(uRxDesc) << 2;
            eRxPacketType = (PacketClassTag_e)RX_DESC_GET_PACKET_CLASS_TAG (uRxDesc);

            /* If new packet exceeds max aggregation length, set flag to send previous packets (postpone it to next loop) */
            if ((uTotalAggregLen + uBuffSize) > pRxXfer->uMaxAggregLen)
            {
                bIssueTxn = TI_TRUE;
            }

            /* No length limit so try to aggregate new packet */
            else
            {
                /* Allocate host read buffer */
                /* The RxBufAlloc() add an extra word for MAC header alignment in case of QoS MSDU */
                eBufStatus = pRxXfer->RequestForBufferCB(pRxXfer->RequestForBufferCB_handle,
                                                         (void**)&pHostBuf,
                                                         uBuffSize,
                                                         (TI_UINT32)NULL,
                                                         eRxPacketType);

                TRACE6(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_Handle: Index=%d, RxDesc=0x%x, DrvCntr=%d, FwCntr=%d, BufStatus=%d, BuffSize=%d\n", uDrvIndex, uRxDesc, pRxXfer->uDrvRxCntr, pRxXfer->uFwRxCntr, eBufStatus, uBuffSize);

                /* If buffer allocated, add it to current Txn (up to 4 packets aggregation) */
                if (eBufStatus == RX_BUF_ALLOC_COMPLETE)
                {
                    /* If first aggregated packet prepare the next Txn struct */
                    if (uAggregPktsNum == 0)
                    {
                        pTxn = (TTxnStruct*)&(pRxXfer->aTxnStruct[pRxXfer->uCurrTxnIndex]);
                        pTxn->uHwAddr = SLV_MEM_DATA;

                        /* Save first mem-block of first aggregated packet! */
                        uFirstMemBlkAddr = SLV_MEM_CP_VALUE(uRxDesc, pRxXfer->uPacketMemoryPoolStart);
                    }
                    pTxn->aBuf[uAggregPktsNum] = pHostBuf + ALIGNMENT_SIZE(uRxDesc);
                    pTxn->aLen[uAggregPktsNum] = uBuffSize;
                    uAggregPktsNum++;
                    uTotalAggregLen += uBuffSize;
                    if (uAggregPktsNum >= pRxXfer->uMaxAggregPkts)
                    {
                        bIssueTxn = TI_TRUE;
                    }
                    pRxXfer->uDrvRxCntr++;
                }

                /* If buffer pending until freeing previous buffer, set Exit flag and if needed set IssueTxn flag. */
                else if (eBufStatus == RX_BUF_ALLOC_PENDING)
                {
                    bExit = TI_TRUE;
                    pRxXfer->bPendingBuffer = TI_TRUE;
                    if (uAggregPktsNum > 0)
                    {
                        bIssueTxn = TI_TRUE;
                    }
#ifdef TI_DBG
                    pRxXfer->tDbgStat.uCountBufPend++;
#endif
                }

                /* If no buffer due to out-of-memory, set DropLastPkt flag and if needed set IssueTxn flag. */
                else
                {
                    bDropLastPkt = TI_TRUE;
                    if (uAggregPktsNum > 0)
                    {
                        bIssueTxn = TI_TRUE;
                    }
#ifdef TI_DBG
                    pRxXfer->tDbgStat.uCountBufNoMem++;
#endif
                }
            }
        }

        /* If no more packets, set Exit flag and if needed set IssueTxn flag. */
        else
        {
            bExit = TI_TRUE;
            if (uAggregPktsNum > 0)
            {
                bIssueTxn = TI_TRUE;
            }
        }


        /* If required to send Rx packet(s) transaction */
        if (bIssueTxn)
        {
            if (bExit)
            {
                TXN_PARAM_SET_END_OF_BURST(pTxn, 1);
            }
            /* If not all 4 Txn buffers are used, reset first unused buffer length for indication */
            if (uAggregPktsNum < MAX_XFER_BUFS)
            {
                pTxn->aLen[uAggregPktsNum] = 0;
            }

            eTxnStatus = rxXfer_IssueTxn (pRxXfer, uFirstMemBlkAddr);

            if (eTxnStatus == TXN_STATUS_COMPLETE)
            {
                /* Forward received packet to the upper layers */
                rxXfer_ForwardPacket (pRxXfer, pTxn);
            }
            else if (eTxnStatus == TXN_STATUS_PENDING)
            {
                /* Decrease the number of available txn structures */
                pRxXfer->uAvailableTxn--;
            }
            else
            {
                TRACE3(pRxXfer->hReport, REPORT_SEVERITY_ERROR , "rxXfer_Handle: Status=%d, DrvCntr=%d, RxDesc=0x%x\n", eTxnStatus, pRxXfer->uDrvRxCntr, uRxDesc);
            }

#ifdef TI_DBG
            pRxXfer->tDbgStat.uCountPktAggreg[uAggregPktsNum - 1]++;
#endif

            uAggregPktsNum  = 0;
            uTotalAggregLen = 0;
            bIssueTxn       = TI_FALSE;
            pRxXfer->uCurrTxnIndex = (pRxXfer->uCurrTxnIndex + 1) % MAX_CONSECUTIVE_READ_TXN;
        }

        /* If last packet should be dropped (no memory for host buffer) */
        if (bDropLastPkt)
        {
            /* Increment driver packets counter before calling rxXfer_IssueTxn() */
            pRxXfer->uDrvRxCntr++;

            /* Read packet to dummy buffer and ignore it (no callback needed) */
            uFirstMemBlkAddr = SLV_MEM_CP_VALUE(uRxDesc, pRxXfer->uPacketMemoryPoolStart);
            pTxn = (TTxnStruct*)&pRxXfer->aTxnStruct[pRxXfer->uCurrTxnIndex];
            BUILD_TTxnStruct(pTxn, SLV_MEM_DATA, pRxXfer->aTempBuffer, uBuffSize, (TTxnDoneCb)rxXfer_PktDropTxnDoneCb, hRxXfer)
            eTxnStatus = rxXfer_IssueTxn (pRxXfer, uFirstMemBlkAddr);
            if (eTxnStatus == TXN_STATUS_PENDING)
            {
                pRxXfer->uAvailableTxn--;
            }
            pRxXfer->uCurrTxnIndex = (pRxXfer->uCurrTxnIndex + 1) % MAX_CONSECUTIVE_READ_TXN;
            bDropLastPkt = TI_FALSE;
        }

        /* Can't process more packets so exit */
        if (bExit)
        {
            CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", "");
            return TI_OK;
        }

    } /* End of while(1) */

    /* Unreachable code */

#endif
}


/****************************************************************************
 *                      rxXfer_IssueTxn()
 ****************************************************************************
 * DESCRIPTION:
 *
 * INPUTS:
 *
 * OUTPUT:
 *
 * RETURNS:
 ****************************************************************************/
static ETxnStatus rxXfer_IssueTxn (TI_HANDLE hRxXfer, TI_UINT32 uFirstMemBlkAddr)
{
    TRxXfer    *pRxXfer = (TRxXfer *)hRxXfer;
    TI_UINT32   uIndex  = pRxXfer->uCurrTxnIndex;
    TTxnStruct *pTxn;
    ETxnStatus  eStatus;

    /* Write the next mem block that we want to read */
    pTxn = &pRxXfer->aSlaveRegTxn[uIndex].tTxnStruct;
    pTxn->uHwAddr = SLV_REG_DATA;
    pRxXfer->aSlaveRegTxn[uIndex].uRegData  = ENDIAN_HANDLE_LONG(uFirstMemBlkAddr);
    pRxXfer->aSlaveRegTxn[uIndex].uRegAdata = ENDIAN_HANDLE_LONG(uFirstMemBlkAddr + 4);
    twIf_Transact(pRxXfer->hTwIf, pTxn);

    /* Issue the packet(s) read transaction (prepared in rxXfer_Handle) */
    pTxn = &pRxXfer->aTxnStruct[uIndex];
    eStatus = twIf_Transact(pRxXfer->hTwIf, pTxn);

    /* Write driver packets counter to FW. This write automatically generates interrupt to FW */
    /* Note: Workaround for WL6-PG1.0 is still needed for PG2.0   ==>  if (pRxXfer->bChipIs1273Pg10)  */
    pTxn = &pRxXfer->aCounterTxn[uIndex].tTxnStruct;
    pTxn->uHwAddr = RX_DRIVER_COUNTER_ADDRESS;
    pRxXfer->aCounterTxn[uIndex].uCounter = ENDIAN_HANDLE_LONG(pRxXfer->uDrvRxCntr);
    twIf_Transact(pRxXfer->hTwIf, pTxn);

    TRACE5(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_IssueTxn: Counter-Txn: HwAddr=0x%x, Len0=%d, Data0=%d, DrvCount=%d, TxnParams=0x%x\n", pTxn->uHwAddr, pTxn->aLen[0], *(TI_UINT32 *)(pTxn->aBuf[0]), pRxXfer->uDrvRxCntr, pTxn->uTxnParams);

    /* Return the status of the packet(s) transaction - COMPLETE, PENDING or ERROR */
    return eStatus;
}
  

/****************************************************************************
 *                      rxXfer_SetRxDirectAccessParams()
 ****************************************************************************
 * DESCRIPTION: 
 *
 * INPUTS:      
 * 
 * OUTPUT:      
 * 
 * RETURNS:     
 ****************************************************************************/
void rxXfer_SetRxDirectAccessParams (TI_HANDLE hRxXfer, TDmaParams *pDmaParams)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    pRxXfer->uPacketMemoryPoolStart = pDmaParams->PacketMemoryPoolStart;
}


/****************************************************************************
 *                      rxXfer_TxnDoneCb()
 ****************************************************************************
 * DESCRIPTION: Forward the packet to the registered CB
 *
 * INPUTS:      
 * 
 * OUTPUT:      
 * 
 * RETURNS:     
 ****************************************************************************/
static void rxXfer_TxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;
    CL_TRACE_START_L2();
    
    /* Increase the number of available txn structures */
    pRxXfer->uAvailableTxn++;

    /* Forward received packet to the upper layers */
    rxXfer_ForwardPacket (pRxXfer, pTxn);

    /* If we exited the handler upon pending-buffer, call it again to handle further packets if any */
    if (pRxXfer->bPendingBuffer)
    {
        pRxXfer->bPendingBuffer = TI_FALSE;
        rxXfer_Handle (hRxXfer);
    }

    CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "RX", "");
}


/****************************************************************************
 *                      rxXfer_PktDropTxnDoneCb()
 ****************************************************************************
 * DESCRIPTION: Dummy CB for case of dropping a packet due to out-of-memory.
 *
 * INPUTS:
 *
 * OUTPUT:
 *
 * RETURNS:
 ****************************************************************************/
static void rxXfer_PktDropTxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    /* Increase the number of available txn structures */
    pRxXfer->uAvailableTxn++;

    /* Restore the regular TxnDone callback to the used structure */
    pTxn->fTxnDoneCb = (TTxnDoneCb)rxXfer_TxnDoneCb;
    pTxn->hCbHandle  = hRxXfer;
}


/****************************************************************************
 *                      rxXfer_Restart()
 ****************************************************************************
 * DESCRIPTION:	rxXfer_Restart the RxXfer module object (called by the recovery)
 * 
 * INPUTS:	hRxXfer - The object to free
 * 
 * OUTPUT:	None
 * 
 * RETURNS:	NONE 
 ****************************************************************************/
void rxXfer_Restart(TI_HANDLE hRxXfer)
{
	TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;
    TTxnStruct* pTxn;
    TI_UINT8    i;

    pRxXfer->uFwRxCntr     = 0;
    pRxXfer->uDrvRxCntr    = 0;
    pRxXfer->uCurrTxnIndex = 0;
    pRxXfer->uAvailableTxn = MAX_CONSECUTIVE_READ_TXN - 1;

    /* Scan all transaction array and release only pending transaction */
    for (i = 0; i < MAX_CONSECUTIVE_READ_TXN; i++)
    {
        pTxn = &(pRxXfer->aTxnStruct[i]);

        /* Check if buffer allocated and not the dummy one (has a different callback) */
        if ((pTxn->aBuf[0] != 0) && (pTxn->fTxnDoneCb == (TTxnDoneCb)rxXfer_TxnDoneCb))
        {
            TI_UINT32 uBufNum;
            RxIfDescriptor_t *pRxParams;

            /* Go over the Txn occupied  buffers and mark them as TAG_CLASS_UNKNOWN to be freed */
            for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++)
            {
                /* If no more buffers, exit the loop */
                if (pTxn->aLen[uBufNum] == 0)
                {
                    break;
                }

                pRxParams = (RxIfDescriptor_t *)(pTxn->aBuf[uBufNum]);
                pRxParams->packet_class_tag = TAG_CLASS_UNKNOWN;
            }

            /* Call upper layer only to release the allocated buffer */
            rxXfer_ForwardPacket (pRxXfer, pTxn);
        }
    }

    /* Fill the transaction structures fields that have constant values */
    for (i = 0; i < MAX_CONSECUTIVE_READ_TXN; i++)
    {
        /* First mem-block address (two consecutive registers) */
        pTxn = &(pRxXfer->aSlaveRegTxn[i].tTxnStruct);
        TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
        BUILD_TTxnStruct(pTxn, SLV_REG_DATA, &pRxXfer->aSlaveRegTxn[i].uRegData, REGISTER_SIZE*2, NULL, NULL)

        /* The packet(s) read transaction */
        pTxn = &(pRxXfer->aTxnStruct[i]);
        TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_FIXED_ADDR)
        pTxn->fTxnDoneCb = (TTxnDoneCb)rxXfer_TxnDoneCb;
        pTxn->hCbHandle  = hRxXfer;

        /* The driver packets counter */
        pTxn = &(pRxXfer->aCounterTxn[i].tTxnStruct);
        TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
        BUILD_TTxnStruct(pTxn, RX_DRIVER_COUNTER_ADDRESS, &pRxXfer->aCounterTxn[i].uCounter, REGISTER_SIZE, NULL, NULL)
    }
	
}


/****************************************************************************
 *                      rxXfer_RegisterErrCb()
 ****************************************************************************
 * DESCRIPTION: Register Error CB
 *
 * INPUTS:
 *          hRxXfer - The object
 *          ErrCb   - The upper layer CB function for error handling
 *          hErrCb  - The CB function handle
 *
 * OUTPUT:  None
 *
 * RETURNS: void
 ****************************************************************************/
void rxXfer_RegisterErrCb (TI_HANDLE hRxXfer, void *fErrCb, TI_HANDLE hErrCb)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    /* Save upper layer (health monitor) CB for recovery from fatal error */
    pRxXfer->fErrCb = (TFailureEventCb)fErrCb;
    pRxXfer->hErrCb = hErrCb;
}


#ifdef TI_DBG
/****************************************************************************
 *                      rxXfer_ClearStats()
 ****************************************************************************
 * DESCRIPTION: 
 *
 * INPUTS:  
 *          pRxXfer The object
 * 
 * OUTPUT:  None
 * 
 * RETURNS: TI_OK. 
 ****************************************************************************/
void rxXfer_ClearStats (TI_HANDLE hRxXfer)
{
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;

    os_memoryZero (pRxXfer->hOs, &pRxXfer->tDbgStat, sizeof(TRxXferDbgStat));
}


/****************************************************************************
 *                      rxXfer_PrintStats()
 ****************************************************************************
 * DESCRIPTION: .
 *
 * INPUTS:  
 *          pRxXfer The object
 * 
 * OUTPUT:  None
 * 
 * RETURNS: TI_OK. 
 ****************************************************************************/
void rxXfer_PrintStats (TI_HANDLE hRxXfer)
{
#ifdef REPORT_LOG
    TRxXfer *pRxXfer = (TRxXfer *)hRxXfer;
    
    WLAN_OS_REPORT(("Print RX Xfer module info\n"));
    WLAN_OS_REPORT(("=========================\n"));
    WLAN_OS_REPORT(("uMaxAggregPkts     = %d\n", pRxXfer->uMaxAggregPkts));
    WLAN_OS_REPORT(("uMaxAggregLen      = %d\n", pRxXfer->uMaxAggregLen));
    WLAN_OS_REPORT(("FW counter         = %d\n", pRxXfer->uFwRxCntr));
    WLAN_OS_REPORT(("Drv counter        = %d\n", pRxXfer->uDrvRxCntr));
    WLAN_OS_REPORT(("AvailableTxn       = %d\n", pRxXfer->uAvailableTxn));
    WLAN_OS_REPORT(("uCountFwEvents     = %d\n", pRxXfer->tDbgStat.uCountFwEvents));
    WLAN_OS_REPORT(("uCountPktsForward  = %d\n", pRxXfer->tDbgStat.uCountPktsForward));
    WLAN_OS_REPORT(("uCountBufPend      = %d\n", pRxXfer->tDbgStat.uCountBufPend));
    WLAN_OS_REPORT(("uCountBufNoMem     = %d\n", pRxXfer->tDbgStat.uCountBufNoMem));
    WLAN_OS_REPORT(("uCountPktAggreg-1  = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[0]));
    WLAN_OS_REPORT(("uCountPktAggreg-2  = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[1]));
    WLAN_OS_REPORT(("uCountPktAggreg-3  = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[2]));
    WLAN_OS_REPORT(("uCountPktAggreg-4  = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[3]));
#endif
}
#endif