1/*
2 * txXfer.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   txXfer.c
36 *  \brief  Handle Tx packets transfer to the firmware.
37 *
38 *  This module gets the upper driver's Tx packets after FW resources were
39 *    allocated for them, aggregates them if possible, and handles their transfer
40 *    to the FW via the host slave (indirect) interface, using the TwIf Transaction API.
41 *  The aggregation processing is completed by the BusDrv where the packets are combined
42 *    and sent to the FW in one transaction.
43 *
44 *  \see
45 */
46
47
48#define __FILE_ID__  FILE_ID_108
49#include "tidef.h"
50#include "osApi.h"
51#include "report.h"
52#include "TwIf.h"
53#include "TWDriver.h"
54#include "txXfer_api.h"
55
56
57#ifdef TI_DBG
58#define     DBG_MAX_AGGREG_PKTS     16
59#endif
60
61typedef struct
62{
63    TTxnStruct              tTxnStruct;
64    TI_UINT32               uPktsCntr;
65} TPktsCntrTxn;
66
67/* The TxXfer module object. */
68typedef struct
69{
70    TI_HANDLE               hOs;
71    TI_HANDLE               hReport;
72    TI_HANDLE               hTwIf;
73
74    TI_UINT32               uAggregMaxPkts;          /* Max number of packets that may be aggregated */
75    TI_UINT32               uAggregMaxLen;           /* Max length in bytes of a single aggregation */
76    TI_UINT32               uAggregPktsNum;          /* Number of packets in current aggregation */
77    TI_UINT32               uAggregPktsLen;          /* Aggregated length of current aggregation */
78    TTxCtrlBlk *            pAggregFirstPkt;         /* Pointer to the first packet of current aggregation */
79    TTxCtrlBlk *            pAggregLastPkt;          /* Pointer to the last packet of current aggregation */
80    TSendPacketTranferCb    fSendPacketTransferCb;   /* Upper layer Xfer-Complete callback */
81    TI_HANDLE               hSendPacketTransferHndl; /* Upper layer Xfer-Complete callback handle */
82    TTxnDoneCb              fXferCompleteLocalCb;    /* TxXfer local CB for pkt transfer completion (NULL is not needed!) */
83    TI_UINT32               uPktsCntr;               /* Counts all Tx packets. Written to FW after each packet transaction */
84    TI_UINT32               uPktsCntrTxnIndex;       /* The current indext of the aPktsCntrTxn[] used for the counter workaround transactions */
85    TPktsCntrTxn            aPktsCntrTxn[CTRL_BLK_ENTRIES_NUM]; /* Transaction structures for sending the packets counter */
86#ifdef TI_DBG
87    TI_UINT32               aDbgCountPktAggreg[DBG_MAX_AGGREG_PKTS];
88#endif
89
90} TTxXferObj;
91
92static ETxnStatus txXfer_SendAggregatedPkts (TTxXferObj *pTxXfer, TI_BOOL bLastPktSentNow);
93static void       txXfer_TransferDoneCb     (TI_HANDLE hTxXfer, TTxnStruct *pTxn);
94
95
96/********************************************************************************
97*																				*
98*                       PUBLIC  FUNCTIONS  IMPLEMENTATION						*
99*																				*
100*********************************************************************************/
101
102
103TI_HANDLE txXfer_Create(TI_HANDLE hOs)
104{
105    TTxXferObj *pTxXfer;
106
107    pTxXfer = os_memoryAlloc (hOs, sizeof(TTxXferObj));
108    if (pTxXfer == NULL)
109    {
110        return NULL;
111    }
112
113    os_memoryZero (hOs, pTxXfer, sizeof(TTxXferObj));
114
115    pTxXfer->hOs = hOs;
116
117    return (TI_HANDLE)pTxXfer;
118}
119
120
121TI_STATUS txXfer_Destroy(TI_HANDLE hTxXfer)
122{
123    TTxXferObj *pTxXfer = (TTxXferObj *)hTxXfer;
124
125    if (pTxXfer)
126    {
127        os_memoryFree (pTxXfer->hOs, pTxXfer, sizeof(TTxXferObj));
128    }
129
130    return TI_OK;
131}
132
133
134TI_STATUS txXfer_Init (TI_HANDLE hTxXfer, TI_HANDLE hReport, TI_HANDLE hTwIf)
135{
136    TTxXferObj *pTxXfer = (TTxXferObj *)hTxXfer;
137    TTxnStruct *pTxn;
138    TI_UINT8    i;
139
140    pTxXfer->hReport = hReport;
141    pTxXfer->hTwIf   = hTwIf;
142    pTxXfer->fSendPacketTransferCb = NULL;
143    pTxXfer->fXferCompleteLocalCb  = NULL;
144
145    for (i = 0; i < CTRL_BLK_ENTRIES_NUM; i++)
146    {
147        pTxn = &(pTxXfer->aPktsCntrTxn[i].tTxnStruct);
148        TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
149        BUILD_TTxnStruct(pTxn, HOST_WR_ACCESS_REG, &pTxXfer->aPktsCntrTxn[i].uPktsCntr, REGISTER_SIZE, NULL, NULL)
150    }
151
152    return txXfer_Restart(hTxXfer);
153}
154
155
156TI_STATUS txXfer_Restart (TI_HANDLE hTxXfer)
157{
158    TTxXferObj *pTxXfer = (TTxXferObj *)hTxXfer;
159
160    pTxXfer->uPktsCntr         = 0;
161    pTxXfer->uPktsCntrTxnIndex = 0;
162    pTxXfer->uAggregPktsNum    = 0;
163
164    return TI_OK;
165}
166
167
168void txXfer_SetDefaults (TI_HANDLE hTxXfer, TTwdInitParams *pInitParams)
169{
170    TTxXferObj *pTxXfer = (TTxXferObj *)hTxXfer;
171
172    pTxXfer->uAggregMaxPkts = pInitParams->tGeneral.uTxAggregPktsLimit;
173}
174
175
176void txXfer_SetBusParams (TI_HANDLE hTxXfer, TI_UINT32 uDmaBufLen)
177{
178    TTxXferObj *pTxXfer = (TTxXferObj *)hTxXfer;
179
180    pTxXfer->uAggregMaxLen = uDmaBufLen;
181}
182
183
184void txXfer_RegisterCb (TI_HANDLE hTxXfer, TI_UINT32 CallBackID, void *CBFunc, TI_HANDLE CBObj)
185{
186    TTxXferObj* pTxXfer = (TTxXferObj*)hTxXfer;
187
188    TRACE3(pTxXfer->hReport, REPORT_SEVERITY_INFORMATION, "txXfer_RegisterCb: CallBackID=%d, CBFunc=0x%x, CBObj=0x%x\n", CallBackID, CBFunc, CBObj);
189
190    switch(CallBackID)
191    {
192        /* Save upper layers Transfer-Done callback */
193        case TWD_INT_SEND_PACKET_TRANSFER:
194            pTxXfer->fSendPacketTransferCb   = (TSendPacketTranferCb)CBFunc;
195            pTxXfer->hSendPacketTransferHndl = CBObj;
196            /* Set also the local CB so we are called upon Async transaction completion to call the upper CB */
197            pTxXfer->fXferCompleteLocalCb = (TTxnDoneCb)txXfer_TransferDoneCb;
198            break;
199
200        default:
201            TRACE0(pTxXfer->hReport, REPORT_SEVERITY_ERROR, " - Illegal value\n");
202            break;
203    }
204}
205
206
207ETxnStatus txXfer_SendPacket (TI_HANDLE hTxXfer, TTxCtrlBlk *pPktCtrlBlk)
208{
209    TTxXferObj   *pTxXfer = (TTxXferObj *)hTxXfer;
210    TI_UINT32    uPktLen  = ENDIAN_HANDLE_WORD(pPktCtrlBlk->tTxDescriptor.length << 2); /* swap back for endianess if needed */
211    ETxnStatus   eStatus;
212
213    /* If starting a new aggregation, prepare it, and send packet if aggregation is disabled. */
214    if (pTxXfer->uAggregPktsNum == 0)
215    {
216        pTxXfer->uAggregPktsNum  = 1;
217        pTxXfer->uAggregPktsLen  = uPktLen;
218        pTxXfer->pAggregFirstPkt = pPktCtrlBlk;
219        pTxXfer->pAggregLastPkt  = pPktCtrlBlk;
220        pPktCtrlBlk->pNextAggregEntry = pPktCtrlBlk;  /* First packet points to itself */
221        if (pTxXfer->uAggregMaxPkts <= 1)
222        {
223            eStatus = txXfer_SendAggregatedPkts (pTxXfer, TI_TRUE);
224            pTxXfer->uAggregPktsNum = 0;
225        }
226        else
227        {
228            eStatus = TXN_STATUS_PENDING;
229        }
230    }
231
232    /* Else, if new packet can be added to aggregation, add it and set status as Pending. */
233    else if ((pTxXfer->uAggregPktsNum + 1 <= pTxXfer->uAggregMaxPkts)  &&
234             (pTxXfer->uAggregPktsLen + uPktLen <= pTxXfer->uAggregMaxLen))
235    {
236        pTxXfer->uAggregPktsNum++;
237        pTxXfer->uAggregPktsLen += uPktLen;
238        pTxXfer->pAggregLastPkt->pNextAggregEntry = pPktCtrlBlk;  /* Link new packet to last */
239        pTxXfer->pAggregLastPkt = pPktCtrlBlk;                    /* Save new packet as last */
240        pPktCtrlBlk->pNextAggregEntry = pTxXfer->pAggregFirstPkt; /* Point from last to first */
241        eStatus = TXN_STATUS_PENDING;
242    }
243
244    /* Else, we can't add the new packet, so send current aggregation and start a new one */
245    else
246    {
247        txXfer_SendAggregatedPkts (pTxXfer, TI_FALSE);
248        eStatus = TXN_STATUS_PENDING;  /* The current packet is not sent yet so return Pending */
249        pTxXfer->uAggregPktsNum  = 1;
250        pTxXfer->uAggregPktsLen  = uPktLen;
251        pTxXfer->pAggregFirstPkt = pPktCtrlBlk;
252        pTxXfer->pAggregLastPkt  = pPktCtrlBlk;
253        pPktCtrlBlk->pNextAggregEntry = pPktCtrlBlk;  /* First packet points to itself */
254    }
255
256
257    /* Return the Txn result - COMPLETE or PENDING. */
258    /* Note: For PENDING, a callback function will be called only if registered (needed for WHA) */
259    return eStatus;
260}
261
262
263void txXfer_EndOfBurst (TI_HANDLE hTxXfer)
264{
265    TTxXferObj   *pTxXfer = (TTxXferObj *)hTxXfer;
266
267    if (pTxXfer->uAggregPktsNum > 0)
268    {
269        /* No more packets from TxDataQ so send any aggregated packets and clear aggregation */
270        txXfer_SendAggregatedPkts (pTxXfer, TI_FALSE);
271        pTxXfer->uAggregPktsNum = 0;
272    }
273}
274
275
276/********************************************************************************
277*																				*
278*                       INTERNAL  FUNCTIONS  IMPLEMENTATION						*
279*																				*
280*********************************************************************************/
281
282/**
283 * \fn     txXfer_SendAggregatedPkts
284 * \brief  Send aggregated Tx packets to bus Txn layer
285 *
286 * Send aggregated Tx packets to bus Txn layer one by one.
287 * Increase the packets counter by the number of packets and send it to the FW (generates an interrupt).
288 * If xfer completion CB is registered and status is Complete, call CB for all packets (except last one if inseted now).
289 *
290 * \note   The BusDrv combines the packets and sends them in one transaction.
291 * \param  pTxXfer         - The module's object
292 * \param  bLastPktSentNow - If TRUE, last packet in the aggregation was inserted in current call to txXfer_SendPacket.
293 * \return COMPLETE if transaction completed in this context, PENDING if not, ERROR if failed
294 * \sa
295 */
296static ETxnStatus txXfer_SendAggregatedPkts (TTxXferObj *pTxXfer, TI_BOOL bLastPktSentNow)
297{
298    TTxCtrlBlk   *pCurrPkt;
299    TTxnStruct   *pTxn;
300    TPktsCntrTxn *pPktsCntrTxn;
301    ETxnStatus   eStatus = TXN_STATUS_COMPLETE;
302    TI_UINT32    i;
303
304    /* Prepare and send all aggregated packets (combined and sent in one transaction by the BusDrv) */
305    pCurrPkt = pTxXfer->pAggregFirstPkt;
306    for (i = 0; i < pTxXfer->uAggregPktsNum; i++)
307    {
308        pTxn = (TTxnStruct *)pCurrPkt;
309
310        /* If not last packet, set aggregation flag, clear completion CB and progress to next packet */
311        if (i < pTxXfer->uAggregPktsNum - 1)
312        {
313            TXN_PARAM_SET_AGGREGATE(pTxn, TXN_AGGREGATE_ON);
314            pTxn->fTxnDoneCb = NULL;
315            pCurrPkt = pCurrPkt->pNextAggregEntry;
316        }
317        /* If last packet, clear aggregation flag and set completion CB (exist only if registered) */
318        else
319        {
320            TXN_PARAM_SET_AGGREGATE(pTxn, TXN_AGGREGATE_OFF);
321            pTxn->fTxnDoneCb = pTxXfer->fXferCompleteLocalCb;
322            pTxn->hCbHandle  = (TI_HANDLE)pTxXfer;
323        }
324
325        /* Send packet */
326        pTxn->uHwAddr = SLV_MEM_DATA;
327        eStatus = twIf_Transact (pTxXfer->hTwIf, pTxn);
328    }
329
330#ifdef TI_DBG
331    pTxXfer->aDbgCountPktAggreg[pTxXfer->uAggregPktsNum]++;
332    TRACE5(pTxXfer->hReport, REPORT_SEVERITY_INFORMATION, "txXfer_SendAggregatedPkts: Status=%d, NumPkts=%d, AggregLen=%d, pFirstPkt=0x%x, pLastPkt=0x%x\n", eStatus, pTxXfer->uAggregPktsNum, pTxXfer->uAggregPktsLen, pTxXfer->pAggregFirstPkt, pTxXfer->pAggregLastPkt);
333    if (eStatus == TXN_STATUS_ERROR)
334    {
335        TRACE5(pTxXfer->hReport, REPORT_SEVERITY_ERROR, "txXfer_SendAggregatedPkts: Status=%d, NumPkts=%d, AggregLen=%d, pFirstPkt=0x%x, pLastPkt=0x%x\n", eStatus, pTxXfer->uAggregPktsNum, pTxXfer->uAggregPktsLen, pTxXfer->pAggregFirstPkt, pTxXfer->pAggregLastPkt);
336        return eStatus;
337    }
338#endif  /* TI_DBG */
339
340    /* Write packet counter to FW (generates an interrupt).
341       Note: This may be removed once the host-slave HW counter functionality is verified */
342    pTxXfer->uPktsCntr += pTxXfer->uAggregPktsNum;
343    pTxXfer->uPktsCntrTxnIndex++;
344    if (pTxXfer->uPktsCntrTxnIndex == CTRL_BLK_ENTRIES_NUM)
345    {
346        pTxXfer->uPktsCntrTxnIndex = 0;
347    }
348    pPktsCntrTxn = &(pTxXfer->aPktsCntrTxn[pTxXfer->uPktsCntrTxnIndex]);
349    pPktsCntrTxn->uPktsCntr = ENDIAN_HANDLE_LONG(pTxXfer->uPktsCntr);
350    pPktsCntrTxn->tTxnStruct.uHwAddr = HOST_WR_ACCESS_REG;
351    twIf_Transact(pTxXfer->hTwIf, &pPktsCntrTxn->tTxnStruct);
352
353    /* If xfer completion CB is registered and last packet status is Complete, call the CB for all
354     *     packets except the input one (covered by the return code).
355     */
356    if (pTxXfer->fSendPacketTransferCb && (eStatus == TXN_STATUS_COMPLETE))
357    {
358        /* Don't call CB for last packet if inserted in current Tx */
359        TI_UINT32 uNumCbCalls = bLastPktSentNow ? (pTxXfer->uAggregPktsNum - 1) : pTxXfer->uAggregPktsNum;
360
361        pCurrPkt = pTxXfer->pAggregFirstPkt;
362        for (i = 0; i < uNumCbCalls; i++)
363        {
364            pTxXfer->fSendPacketTransferCb (pTxXfer->hSendPacketTransferHndl, pCurrPkt);
365            pCurrPkt = pCurrPkt->pNextAggregEntry;
366        }
367    }
368
369    /* Return the Txn result - COMPLETE or PENDING. */
370    /* Note: For PENDING, a callback function will be called only if registered (needed for WHA) */
371    return eStatus;
372}
373
374
375/**
376 * \fn     txXfer_TransferDoneCb
377 * \brief  Send aggregated Tx packets to bus Txn layer
378 *
379 * Call the upper layers TranferDone CB for all packets of the completed aggregation
380 * This function is called only if the upper layers registered their CB (used only by WHA)
381 *
382 * \note
383 * \param  pTxXfer - The module's object
384 * \return COMPLETE if completed in this context, PENDING if not, ERROR if failed
385 * \sa
386 */
387static void txXfer_TransferDoneCb (TI_HANDLE hTxXfer, TTxnStruct *pTxn)
388{
389    TTxXferObj *pTxXfer   = (TTxXferObj*)hTxXfer;
390    TTxCtrlBlk *pInputPkt = (TTxCtrlBlk *)pTxn; /* This is the last packet of the aggregation */
391    TTxCtrlBlk *pCurrPkt;
392    TI_UINT32   i;
393
394    /* Call the upper layers TranferDone CB for all packets of the completed aggregation */
395    /* Note: If this CB was called it means that the upper CB exists */
396    pCurrPkt = pInputPkt->pNextAggregEntry;  /* The last packet of the aggregation point to the first one */
397    for (i = 0; i < pTxXfer->uAggregMaxPkts; i++)
398    {
399        pTxXfer->fSendPacketTransferCb (pTxXfer->hSendPacketTransferHndl, pCurrPkt);
400
401        /* If we got back to the input packet we went over all the aggregation */
402        if (pCurrPkt == pInputPkt)
403        {
404            break;
405        }
406
407        pCurrPkt = pCurrPkt->pNextAggregEntry;
408    }
409
410    TRACE3(pTxXfer->hReport, REPORT_SEVERITY_INFORMATION, "txXfer_TransferDoneCb: NumPkts=%d, pInputPkt=0x%x, pCurrPkt=0x%x\n", i + 1, pInputPkt, pCurrPkt);
411}
412
413
414#ifdef TI_DBG
415
416void txXfer_ClearStats (TI_HANDLE hTxXfer)
417{
418    TTxXferObj *pTxXfer = (TTxXferObj*)hTxXfer;
419
420    os_memoryZero (pTxXfer->hOs, &pTxXfer->aDbgCountPktAggreg, sizeof(pTxXfer->aDbgCountPktAggreg));
421}
422
423void txXfer_PrintStats (TI_HANDLE hTxXfer)
424{
425#ifdef REPORT_LOG
426    TTxXferObj *pTxXfer = (TTxXferObj*)hTxXfer;
427    TI_UINT32   i;
428
429    WLAN_OS_REPORT(("Print Tx Xfer module info\n"));
430    WLAN_OS_REPORT(("=========================\n"));
431    WLAN_OS_REPORT(("uAggregMaxPkts     = %d\n", pTxXfer->uAggregMaxPkts));
432    WLAN_OS_REPORT(("uAggregMaxLen      = %d\n", pTxXfer->uAggregMaxLen));
433    WLAN_OS_REPORT(("uAggregPktsNum     = %d\n", pTxXfer->uAggregPktsNum));
434    WLAN_OS_REPORT(("uAggregPktsLen     = %d\n", pTxXfer->uAggregPktsLen));
435    WLAN_OS_REPORT(("uPktsCntr          = %d\n", pTxXfer->uPktsCntr));
436    WLAN_OS_REPORT(("uPktsCntrTxnIndex  = %d\n", pTxXfer->uPktsCntrTxnIndex));
437    for (i = 1; i < DBG_MAX_AGGREG_PKTS; i++)
438    {
439        WLAN_OS_REPORT(("uCountPktAggreg-%2d = %d\n", i, pTxXfer->aDbgCountPktAggreg[i]));
440    }
441#endif
442}
443
444#endif /* TI_DBG */
445