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