Home | History | Annotate | Download | only in Txn
      1 /*
      2  * SdioBusDrv.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   SdioBusDrv.c
     36  *  \brief  The SDIO bus driver upper layer. Platform independent.
     37  *          Uses the SdioAdapter API.
     38  *          Introduces a generic bus-independent API upwards.
     39  *
     40  *  \see    BusDrv.h, SdioAdapter.h, SdioAdapter.c
     41  */
     42 
     43 #define __FILE_ID__  FILE_ID_122
     44 #include "tidef.h"
     45 #include "report.h"
     46 #include "osApi.h"
     47 #include "TxnDefs.h"
     48 #include "SdioAdapter.h"
     49 #include "BusDrv.h"
     50 #include "bmtrace_api.h"
     51 
     52 
     53 /* remove the chipID check when WL6-PG1.0 becomes obsolete (temporary global variable!!) */
     54 extern TI_BOOL bChipIs1273Pg10;
     55 
     56 
     57 /************************************************************************
     58  * Defines
     59  ************************************************************************/
     60 #define MAX_TXN_PARTS     MAX_XFER_BUFS * 2   /* Max number of txn parts derived from one TxnStruct */
     61 
     62 
     63 /************************************************************************
     64  * Types
     65  ************************************************************************/
     66 
     67 /* A single SDIO bus transaction which is a part of a complete transaction (TTxnStruct) */
     68 typedef struct
     69 {
     70     TI_BOOL          bBlkMode;           /* If TRUE this is a block-mode SDIO transaction */
     71     TI_UINT32        uLength;            /* Length in byte */
     72     TI_UINT32        uHwAddr;            /* The device address to write to or read from */
     73     void *           pHostAddr;          /* The host buffer address to write from or read into */
     74     TI_BOOL          bMore;              /* If TRUE, indicates the lower driver to keep awake for more transactions */
     75 } TTxnPart;
     76 
     77 
     78 /* The busDrv module Object */
     79 typedef struct _TBusDrvObj
     80 {
     81     TI_HANDLE	     hOs;
     82     TI_HANDLE	     hReport;
     83 
     84 	TBusDrvTxnDoneCb fTxnDoneCb;         /* The callback to call upon full transaction completion. */
     85 	TI_HANDLE        hCbHandle;          /* The callback handle */
     86     TTxnStruct *     pCurrTxn;           /* The transaction currently being processed */
     87     ETxnStatus       eCurrTxnStatus;     /* COMPLETE, PENDING or ERROR */
     88     TTxnPart         aTxnParts[MAX_TXN_PARTS]; /* The actual bus transactions of current transaction */
     89     TI_UINT32        uCurrTxnPartsNum;   /* Number of transaction parts composing the current transaction */
     90     TI_UINT32        uCurrTxnPartsCount; /* Number of transaction parts already executed */
     91     TI_UINT32        uCurrTxnPartsCountSync; /* Number of transaction parts completed in Sync mode (returned COMPLETE) */
     92     TI_UINT32        uBlkSizeShift;      /* In block-mode:  uBlkSize = (1 << uBlkSizeShift) = 512 bytes */
     93     TI_UINT32        uBlkSize;           /* In block-mode:  uBlkSize = (1 << uBlkSizeShift) = 512 bytes */
     94     TI_UINT32        uBlkSizeMask;       /* In block-mode:  uBlkSizeMask = uBlkSize - 1 = 0x1FF*/
     95     TI_UINT8 *       pDmaBuffer;         /* DMA-able buffer for buffering all write transactions */
     96 
     97 } TBusDrvObj;
     98 
     99 
    100 /************************************************************************
    101  * Internal functions prototypes
    102  ************************************************************************/
    103 static void busDrv_PrepareTxnParts  (TBusDrvObj *pBusDrv, TTxnStruct *pTxn);
    104 static void busDrv_SendTxnParts     (TBusDrvObj *pBusDrv);
    105 static void busDrv_TxnDoneCb        (TI_HANDLE hBusDrv, TI_INT32 status);
    106 
    107 
    108 
    109 /************************************************************************
    110  *
    111  *   Module functions implementation
    112  *
    113  ************************************************************************/
    114 
    115 /**
    116  * \fn     busDrv_Create
    117  * \brief  Create the module
    118  *
    119  * Create and clear the bus driver's object, and the SDIO-adapter.
    120  *
    121  * \note
    122  * \param  hOs - Handle to Os Abstraction Layer
    123  * \return Handle of the allocated object, NULL if allocation failed
    124  * \sa     busDrv_Destroy
    125  */
    126 TI_HANDLE busDrv_Create (TI_HANDLE hOs)
    127 {
    128     TI_HANDLE   hBusDrv;
    129     TBusDrvObj *pBusDrv;
    130 
    131     hBusDrv = os_memoryAlloc(hOs, sizeof(TBusDrvObj));
    132     if (hBusDrv == NULL)
    133     {
    134         return NULL;
    135     }
    136 
    137     pBusDrv = (TBusDrvObj *)hBusDrv;
    138 
    139     os_memoryZero(hOs, hBusDrv, sizeof(TBusDrvObj));
    140 
    141     pBusDrv->hOs = hOs;
    142 
    143     return pBusDrv;
    144 }
    145 
    146 
    147 /**
    148  * \fn     busDrv_Destroy
    149  * \brief  Destroy the module.
    150  *
    151  * Close SDIO lower bus driver and free the module's object.
    152  *
    153  * \note
    154  * \param  The module's object
    155  * \return TI_OK on success or TI_NOK on failure
    156  * \sa     busDrv_Create
    157  */
    158 TI_STATUS busDrv_Destroy (TI_HANDLE hBusDrv)
    159 {
    160     TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv;
    161 
    162     if (pBusDrv)
    163     {
    164         os_memoryFree (pBusDrv->hOs, pBusDrv, sizeof(TBusDrvObj));
    165     }
    166     return TI_OK;
    167 }
    168 
    169 
    170 /**
    171  * \fn     busDrv_Init
    172  * \brief  Init bus driver
    173  *
    174  * Init module parameters.
    175 
    176  * \note
    177  * \param  hBusDrv - The module's handle
    178  * \param  hReport - report module handle
    179  * \return void
    180  * \sa
    181  */
    182 void busDrv_Init (TI_HANDLE hBusDrv, TI_HANDLE hReport)
    183 {
    184     TBusDrvObj *pBusDrv = (TBusDrvObj*) hBusDrv;
    185 
    186     pBusDrv->hReport = hReport;
    187 }
    188 
    189 
    190 /**
    191  * \fn     busDrv_ConnectBus
    192  * \brief  Configure bus driver
    193  *
    194  * Called by TxnQ.
    195  * Configure the bus driver with its connection configuration (such as baud-rate, bus width etc)
    196  *     and establish the physical connection.
    197  * Done once upon init (and not per functional driver startup).
    198  *
    199  * \note
    200  * \param  hBusDrv    - The module's object
    201  * \param  pBusDrvCfg - A union used for per-bus specific configuration.
    202  * \param  fCbFunc    - CB function for Async transaction completion (after all txn parts are completed).
    203  * \param  hCbArg     - The CB function handle
    204  * \return TI_OK / TI_NOK
    205  * \sa
    206  */
    207 TI_STATUS busDrv_ConnectBus (TI_HANDLE        hBusDrv,
    208                              TBusDrvCfg       *pBusDrvCfg,
    209                              TBusDrvTxnDoneCb fCbFunc,
    210                              TI_HANDLE        hCbArg,
    211                              TBusDrvTxnDoneCb fConnectCbFunc)
    212 {
    213     TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv;
    214     int         iStatus;
    215 
    216     /* Save the parameters (TxnQ callback for TxnDone events, and block-size) */
    217     pBusDrv->fTxnDoneCb    = fCbFunc;
    218     pBusDrv->hCbHandle     = hCbArg;
    219     pBusDrv->uBlkSizeShift = pBusDrvCfg->tSdioCfg.uBlkSizeShift;
    220     pBusDrv->uBlkSize      = 1 << pBusDrv->uBlkSizeShift;
    221     pBusDrv->uBlkSizeMask  = pBusDrv->uBlkSize - 1;
    222     /* This should cover stop send Txn parts in recovery */
    223     pBusDrv->uCurrTxnPartsCount = 0;
    224     pBusDrv->uCurrTxnPartsNum = 0;
    225     pBusDrv->uCurrTxnPartsCountSync = 0;
    226 
    227 
    228     /*
    229      * Configure the SDIO driver parameters and handle SDIO enumeration.
    230      *
    231      * Note: The DMA-able buffer address to use for write transactions is provided from the
    232      *           SDIO driver into pBusDrv->pDmaBuffer.
    233      */
    234     iStatus = sdioAdapt_ConnectBus (busDrv_TxnDoneCb,
    235                                     hBusDrv,
    236                                     pBusDrv->uBlkSizeShift,
    237                                     pBusDrvCfg->tSdioCfg.uBusDrvThreadPriority,
    238                                     &pBusDrv->pDmaBuffer);
    239 
    240     if (pBusDrv->pDmaBuffer == NULL)
    241     {
    242         TRACE0(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_ConnectBus: Didn't get DMA buffer from SDIO driver!!");
    243         return TI_NOK;
    244     }
    245 
    246 
    247     if (iStatus == 0)
    248     {
    249         return TI_OK;
    250     }
    251     else
    252     {
    253         TRACE2(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_ConnectBus: Status = 0x%x, BlkSize = %d\n", iStatus, pBusDrv->uBlkSize);
    254         return TI_NOK;
    255     }
    256 }
    257 
    258 
    259 /**
    260  * \fn     busDrv_DisconnectBus
    261  * \brief  Disconnect SDIO driver
    262  *
    263  * Called by TxnQ. Disconnect the SDIO driver.
    264  *
    265  * \note
    266  * \param  hBusDrv - The module's object
    267  * \return TI_OK / TI_NOK
    268  * \sa
    269  */
    270 TI_STATUS busDrv_DisconnectBus (TI_HANDLE hBusDrv)
    271 {
    272     TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv;
    273 
    274     TRACE0(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_DisconnectBus()\n");
    275 
    276     /* Disconnect SDIO driver */
    277     return sdioAdapt_DisconnectBus ();
    278 }
    279 
    280 
    281 /**
    282  * \fn     busDrv_Transact
    283  * \brief  Process transaction
    284  *
    285  * Called by the TxnQ module to initiate a new transaction.
    286  * Prepare the transaction parts (lower layer single transactions),
    287  *      and send them one by one to the lower layer.
    288  *
    289  * \note   It's assumed that this function is called only when idle (i.e. previous Txn is done).
    290  * \param  hBusDrv - The module's object
    291  * \param  pTxn    - The transaction object
    292  * \return COMPLETE if Txn completed in this context, PENDING if not, ERROR if failed
    293  * \sa     busDrv_PrepareTxnParts, busDrv_SendTxnParts
    294  */
    295 ETxnStatus busDrv_Transact (TI_HANDLE hBusDrv, TTxnStruct *pTxn)
    296 {
    297     TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv;
    298     CL_TRACE_START_L4();
    299 
    300     pBusDrv->pCurrTxn           = pTxn;
    301     pBusDrv->uCurrTxnPartsCount = 0;
    302     pBusDrv->uCurrTxnPartsCountSync = 0;
    303 
    304     /* Prepare the transaction parts in a table. */
    305     busDrv_PrepareTxnParts (pBusDrv, pTxn);
    306 
    307     /* Send the prepared transaction parts. */
    308     busDrv_SendTxnParts (pBusDrv);
    309 
    310     TRACE1(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_Transact: Status = %d\n", pBusDrv->eCurrTxnStatus);
    311 
    312     CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TXN", ".Transact");
    313 
    314     /* return transaction status - COMPLETE, PENDING or ERROR */
    315     /* The status is updated in busDrv_SendTxnParts(). It is Async (pending) if not completed in this context */
    316     return pBusDrv->eCurrTxnStatus;
    317 }
    318 
    319 
    320 /**
    321  * \fn     busDrv_PrepareTxnParts
    322  * \brief  Prepare write or read transaction parts
    323  *
    324  * Called by busDrv_Transact().
    325  * Prepares the actual sequence of SDIO bus transactions in a table.
    326  * Use a DMA-able buffer for the bus transaction, so all data is copied
    327  *     to it from the host buffer(s) before write transactions,
    328  *     or copied from it to the host buffers after read transactions.
    329  *
    330  * \note
    331  * \param  pBusDrv - The module's object
    332  * \param  pTxn    - The transaction object
    333  * \return void
    334  * \sa     busDrv_Transact, busDrv_SendTxnParts,
    335  */
    336 static void busDrv_PrepareTxnParts (TBusDrvObj *pBusDrv, TTxnStruct *pTxn)
    337 {
    338     TI_UINT32 uPartNum = 0;
    339     TI_UINT32 uTxnLength   = 0;
    340     TI_UINT8 *pHostBuf     = pBusDrv->pDmaBuffer; /* Host buffer to use for actual transaction is the DMA buffer */
    341     TI_UINT32 uCurrHwAddr = pTxn->uHwAddr;
    342     TI_BOOL   bFixedHwAddr = TXN_PARAM_GET_FIXED_ADDR(pTxn);
    343     TI_UINT32 uBufNum;
    344     TI_UINT32 uBufLen;
    345     TI_UINT32 uRemainderLen;
    346 
    347     /* Go over the transaction buffers */
    348     for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++)
    349     {
    350         uBufLen = pTxn->aLen[uBufNum];
    351 
    352         /* If no more buffers, exit the loop */
    353         if (uBufLen == 0)
    354         {
    355             break;
    356         }
    357 
    358         /* For write transaction, copy the data to the DMA buffer */
    359         if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_WRITE)
    360         {
    361             os_memoryCopy (pBusDrv->hOs, pHostBuf + uTxnLength, pTxn->aBuf[uBufNum], uBufLen);
    362         }
    363 
    364         /* Add buffer length to total transaction length */
    365         uTxnLength += uBufLen;
    366         }
    367 
    368         /* If current buffer has a remainder, prepare its transaction part */
    369         uRemainderLen = uTxnLength & pBusDrv->uBlkSizeMask;
    370         if (uRemainderLen > 0)
    371         {
    372             pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_FALSE;
    373             pBusDrv->aTxnParts[uPartNum].uLength   = uRemainderLen;
    374             pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
    375             pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)pHostBuf;
    376             pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;
    377 
    378             /* If not fixed HW address, increment it by this part's size */
    379             if (!bFixedHwAddr)
    380             {
    381                 uCurrHwAddr += uRemainderLen;
    382             }
    383 
    384             uPartNum++;
    385         }
    386 
    387         /*  SDIO block-mode doesn't work on PG1.0 so split to 512 bytes blocks!
    388            Remove when PG1.0 is obsolete! */
    389         if (bChipIs1273Pg10)
    390         {
    391             TI_UINT32 uLen;
    392 
    393             for (uLen = uRemainderLen; uLen < uTxnLength; uLen += pBusDrv->uBlkSize)
    394     		{
    395     			pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_FALSE;
    396     			pBusDrv->aTxnParts[uPartNum].uLength   = pBusDrv->uBlkSize;
    397     			pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
    398                 pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uLen);
    399     			pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;
    400 
    401     			/* If not fixed HW address, increment it by this part's size */
    402     			if (!bFixedHwAddr)
    403     			{
    404     				uCurrHwAddr += pBusDrv->uBlkSize;
    405     			}
    406 
    407     			uPartNum++;
    408     		}
    409         }
    410 
    411 
    412         /* For PG2.0, use SDIO block mode */
    413         else
    414         {
    415             /* If current buffer has full SDIO blocks, prepare a block-mode transaction part */
    416             if (uTxnLength >= pBusDrv->uBlkSize)
    417             {
    418                 pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_TRUE;
    419                 pBusDrv->aTxnParts[uPartNum].uLength   = uTxnLength - uRemainderLen;
    420                 pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
    421                 pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uRemainderLen);
    422                 pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;
    423 
    424                 uPartNum++;
    425         }
    426     }
    427 
    428     /* Set last More flag as specified for the whole Txn */
    429     pBusDrv->aTxnParts[uPartNum - 1].bMore = TXN_PARAM_GET_MORE(pTxn);
    430     pBusDrv->uCurrTxnPartsNum = uPartNum;
    431 }
    432 
    433 
    434 /**
    435  * \fn     busDrv_SendTxnParts
    436  * \brief  Send prepared transaction parts
    437  *
    438  * Called first by busDrv_Transact(), and also from TxnDone CB after Async completion.
    439  * Sends the prepared transaction parts in a loop.
    440  * If a transaction part is Async, the loop continues later in the TxnDone ISR context.
    441  * When all parts are done, the upper layer TxnDone CB is called.
    442  *
    443  * \note
    444  * \param  pBusDrv - The module's object
    445  * \return void
    446  * \sa     busDrv_Transact, busDrv_PrepareTxnParts
    447  */
    448 static void busDrv_SendTxnParts (TBusDrvObj *pBusDrv)
    449 {
    450     ETxnStatus  eStatus;
    451     TTxnPart   *pTxnPart;
    452     TTxnStruct *pTxn = pBusDrv->pCurrTxn;
    453 
    454     /* While there are transaction parts to send */
    455     while (pBusDrv->uCurrTxnPartsCount < pBusDrv->uCurrTxnPartsNum)
    456     {
    457         pTxnPart = &(pBusDrv->aTxnParts[pBusDrv->uCurrTxnPartsCount]);
    458         pBusDrv->uCurrTxnPartsCount++;
    459 
    460         /* Assume pending to be ready in case we are preempted by the TxnDon CB !! */
    461         pBusDrv->eCurrTxnStatus = TXN_STATUS_PENDING;
    462 
    463         /* If single step, send ELP byte */
    464         if (TXN_PARAM_GET_SINGLE_STEP(pTxn))
    465         {
    466             /* Overwrite the function id with function 0 - for ELP register !!!! */
    467             eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL,
    468                                                pTxnPart->uHwAddr,
    469                                                pTxnPart->pHostAddr,
    470                                                pTxnPart->uLength,
    471                                                TXN_PARAM_GET_DIRECTION(pTxn),
    472                                                pTxnPart->bMore);
    473 
    474             /* If first write failed try once again (may happen once upon chip wakeup) */
    475             if (eStatus == TXN_STATUS_ERROR)
    476             {
    477                 /* Overwrite the function id with function 0 - for ELP register !!!! */
    478                 eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL,
    479                                                    pTxnPart->uHwAddr,
    480                                                    pTxnPart->pHostAddr,
    481                                                    pTxnPart->uLength,
    482                                                    TXN_PARAM_GET_DIRECTION(pTxn),
    483                                                    pTxnPart->bMore);
    484                 TRACE0(pBusDrv->hReport, REPORT_SEVERITY_WARNING, "busDrv_SendTxnParts: SDIO Single-Step transaction failed once so try again");
    485             }
    486         }
    487         else
    488         {
    489             eStatus = sdioAdapt_Transact (TXN_PARAM_GET_FUNC_ID(pTxn),
    490                                           pTxnPart->uHwAddr,
    491                                           pTxnPart->pHostAddr,
    492                                           pTxnPart->uLength,
    493                                           TXN_PARAM_GET_DIRECTION(pTxn),
    494                                           pTxnPart->bBlkMode,
    495                                           ((TXN_PARAM_GET_FIXED_ADDR(pTxn) == 1) ? 0 : 1),
    496                                           pTxnPart->bMore);
    497         }
    498 
    499         TRACE7(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: PartNum = %d, SingleStep = %d, Direction = %d, HwAddr = 0x%x, HostAddr = 0x%x, Length = %d, BlkMode = %d\n", pBusDrv->uCurrTxnPartsCount-1, TXN_PARAM_GET_SINGLE_STEP(pTxn), TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, pTxnPart->bBlkMode);
    500 
    501         /* If pending TxnDone (Async), continue this loop in the next TxnDone interrupt */
    502         if (eStatus == TXN_STATUS_PENDING)
    503         {
    504             return;
    505         }
    506 
    507         /* Update current transaction status to deduce if it is all finished in the original context (Sync) or not. */
    508         pBusDrv->eCurrTxnStatus = eStatus;
    509         pBusDrv->uCurrTxnPartsCountSync++;
    510 
    511         /* If error, set error in Txn struct, call TxnDone CB if not fully sync, and exit */
    512         if (eStatus == TXN_STATUS_ERROR)
    513         {
    514             TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_ERROR);
    515             if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount)
    516             {
    517                 pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn);
    518             }
    519         	return;
    520         }
    521     }
    522 
    523     /* If we got here we sent all buffers and we don't pend transaction end */
    524     TRACE3(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: Txn finished successfully, Status = %d, PartsCount = %d, SyncCount = %d\n", pBusDrv->eCurrTxnStatus, pBusDrv->uCurrTxnPartsCount, pBusDrv->uCurrTxnPartsCountSync);
    525 
    526     /* For read transaction, copy the data from the DMA-able buffer to the host buffer(s) */
    527     if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_READ)
    528     {
    529         TI_UINT32 uBufNum;
    530         TI_UINT32 uBufLen;
    531         TI_UINT8 *pDmaBuf = pBusDrv->pDmaBuffer; /* After the read transaction the data is in the DMA buffer */
    532 
    533         for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++)
    534         {
    535             uBufLen = pTxn->aLen[uBufNum];
    536 
    537             /* If no more buffers, exit the loop */
    538             if (uBufLen == 0)
    539             {
    540                 break;
    541             }
    542 
    543             os_memoryCopy (pBusDrv->hOs, pTxn->aBuf[uBufNum], pDmaBuf, uBufLen);
    544             pDmaBuf += uBufLen;
    545         }
    546     }
    547 
    548     /* Set status OK in Txn struct, and call TxnDone CB if not fully sync */
    549     TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_OK);
    550     if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount)
    551     {
    552         pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn);
    553     }
    554 }
    555 
    556 
    557 /**
    558  * \fn     busDrv_TxnDoneCb
    559  * \brief  Continue async transaction processing (CB)
    560  *
    561  * Called back by the lower (BSP) bus-driver upon Async transaction completion (TxnDone ISR).
    562  * Call busDrv_SendTxnParts to continue sending the remained transaction parts.
    563  *
    564  * \note
    565  * \param  hBusDrv - The module's object
    566  * \param  status  - The last transaction result - 0 = OK, else Error
    567  * \return void
    568  * \sa     busDrv_SendTxnParts
    569  */
    570 static void busDrv_TxnDoneCb (TI_HANDLE hBusDrv, int iStatus)
    571 {
    572     TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv;
    573     CL_TRACE_START_L1();
    574 
    575     /* If last transaction part failed, set error in Txn struct, call TxnDone CB and exit. */
    576     if (iStatus != 0)
    577     {
    578         TRACE1(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_TxnDoneCb: Status = 0x%x\n", iStatus);
    579 
    580         TXN_PARAM_SET_STATUS(pBusDrv->pCurrTxn, TXN_PARAM_STATUS_ERROR);
    581         pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pBusDrv->pCurrTxn);
    582         CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", "");
    583         return;
    584     }
    585 
    586     TRACE0(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_TxnDoneCb()\n");
    587 
    588     /* Continue sending the remained transaction parts. */
    589     busDrv_SendTxnParts (pBusDrv);
    590 
    591     CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", "");
    592 }
    593