/** 
 * \fn     busDrv_PrepareTxnParts
 * \brief  Prepare write or read transaction parts
 * 
 * Called by busDrv_Transact().
 * Prepares the actual sequence of SDIO bus transactions in a table.
 * Use a DMA-able buffer for the bus transaction, so all data is copied
 *     to it from the host buffer(s) before write transactions,
 *     or copied from it to the host buffers after read transactions.
 * 
 * \note   
 * \param  pBusDrv - The module's object
 * \param  pTxn    - The transaction object 
 * \return TRUE if we are in the middle of a Tx aggregation
 * \sa     busDrv_Transact, busDrv_SendTxnParts, 
 */ 
static TI_BOOL busDrv_PrepareTxnParts (TBusDrvObj *pBusDrv, TTxnStruct *pTxn)
{
    TI_UINT32 uPartNum     = 0;
    TI_UINT32 uCurrHwAddr  = pTxn->uHwAddr;
    TI_BOOL   bFixedHwAddr = TXN_PARAM_GET_FIXED_ADDR(pTxn);
    TI_BOOL   bWrite       = (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_WRITE) ? TI_TRUE : TI_FALSE;
    TI_UINT8 *pHostBuf     = bWrite ? pBusDrv->pTxDmaBuf : pBusDrv->pRxDmaBuf; /* Use DMA buffer (Rx or Tx) for actual transaction */
    TI_UINT32 uBufNum;
    TI_UINT32 uBufLen;
    TI_UINT32 uRemainderLen;

    /* Go over the transaction buffers */
    for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) 
    {
        uBufLen = pTxn->aLen[uBufNum];

        /* If no more buffers, exit the loop */
        if (uBufLen == 0)
        {
            break;
        }

        /* For write transaction, copy the data to the DMA buffer */
        if (bWrite)
        {
            os_memoryCopy (pBusDrv->hOs, pHostBuf + pBusDrv->uTxnLength, pTxn->aBuf[uBufNum], uBufLen);
        }

        /* Add buffer length to total transaction length */
        pBusDrv->uTxnLength += uBufLen;
    }

    /* If in a Tx aggregation, return TRUE (need to accumulate all parts before sending the transaction) */
    if (TXN_PARAM_GET_AGGREGATE(pTxn) == TXN_AGGREGATE_ON)
    {
        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]);
        return TI_TRUE;
    }

    /* If current buffer has a remainder, prepare its transaction part */
    uRemainderLen = pBusDrv->uTxnLength & pBusDrv->uBlkSizeMask;
    if (uRemainderLen > 0)
    {
        pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_FALSE;
        pBusDrv->aTxnParts[uPartNum].uLength   = uRemainderLen;
        pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
        pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)pHostBuf;
        pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;

        /* If not fixed HW address, increment it by this part's size */
        if (!bFixedHwAddr)
        {
            uCurrHwAddr += uRemainderLen;
        }

        uPartNum++;
    }

#ifdef  DISABLE_SDIO_MULTI_BLK_MODE

    /* SDIO multi-block mode is disabled so split to 512 bytes blocks */
    {
        TI_UINT32 uLen;

        for (uLen = uRemainderLen; uLen < pBusDrv->uTxnLength; uLen += pBusDrv->uBlkSize)
        {
            pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_FALSE;
            pBusDrv->aTxnParts[uPartNum].uLength   = pBusDrv->uBlkSize;
            pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
            pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uLen);
            pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;

            /* If not fixed HW address, increment it by this part's size */
            if (!bFixedHwAddr)
            {
                uCurrHwAddr += pBusDrv->uBlkSize;
            }

            uPartNum++;
        }
    }

#else  /* Use SDIO block mode (this is the default behavior) */

    /* If current buffer has full SDIO blocks, prepare a block-mode transaction part */
    if (pBusDrv->uTxnLength >= pBusDrv->uBlkSize)
    {
        pBusDrv->aTxnParts[uPartNum].bBlkMode  = TI_TRUE;
        pBusDrv->aTxnParts[uPartNum].uLength   = pBusDrv->uTxnLength - uRemainderLen;
        pBusDrv->aTxnParts[uPartNum].uHwAddr   = uCurrHwAddr;
        pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uRemainderLen);
        pBusDrv->aTxnParts[uPartNum].bMore     = TI_TRUE;

        uPartNum++;
    }

#endif /* DISABLE_SDIO_MULTI_BLK_MODE */

    /* Set last More flag as specified for the whole Txn */
    pBusDrv->aTxnParts[uPartNum - 1].bMore = TXN_PARAM_GET_MORE(pTxn);
    pBusDrv->uCurrTxnPartsNum = uPartNum;

    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]);

    pBusDrv->uTxnLength = 0;

    /* Return FALSE to indicate that we are not in the middle of a Tx aggregation so the Txn is ready to send */
    return TI_FALSE;
}
/************************************************************************
 *                        findAndInsertSiteEntry									*
 ************************************************************************
DESCRIPTION: Perform the following things:
			-	Compute the site's hash entry based on the site BSSID and hash function
			-	Look for the site entry in the linked list pointed by the hash entry
			-	If the site is found in the site table, returns a pointer to the site entry
			-	If the site is not found in the site table, tries to add the site
				-	If succeeds, returns a pointer to the site entry
				-	Otherwise, returns NULL

INPUT:      pSiteMgr	-	Handle to site mgr
			mac			-	The site BSSID
            band        -   The site band


OUTPUT:

RETURN:     Pointer to the site entry if site found/inserted, NULL otherwise

************************************************************************/
siteEntry_t	*findAndInsertSiteEntry(siteMgr_t		*pSiteMgr,
                                    TMacAddr    	*mac,
                                    ERadioBand      band)
{
	TI_UINT8             i, emptySiteIndex=0, nextSite2Remove=0;
	siteEntry_t         *pSiteEntry, *pPrimarySite=pSiteMgr->pSitesMgmtParams->pPrimarySite;
	sitesMgmtParams_t   *pSitesMgmtParams  = pSiteMgr->pSitesMgmtParams;
	siteTablesParams_t  *pCurrentSiteTable;
	TI_BOOL              firstEmptySiteFound = TI_FALSE;
	TI_UINT32            oldestTS;


	/* choose site table according to AP's band */
	if ( RADIO_BAND_2_4_GHZ == band ) {
		pCurrentSiteTable = &(pSitesMgmtParams->dot11BG_sitesTables);
	} else if (RADIO_BAND_5_0_GHZ == band) {
		pCurrentSiteTable = (siteTablesParams_t*) &(pSitesMgmtParams->dot11A_sitesTables);
	} else {
		TRACE1(pSiteMgr->hReport, REPORT_SEVERITY_ERROR, "Bad band: %d\n\n", band);
		pCurrentSiteTable = &(pSitesMgmtParams->dot11BG_sitesTables);
	}

	/* Set the first TS to a site which is not the Primary site */
	if (pPrimarySite != &(pCurrentSiteTable->siteTable[0])) {
		oldestTS = pCurrentSiteTable->siteTable[0].localTimeStamp;
	} else {
		oldestTS = pCurrentSiteTable->siteTable[1].localTimeStamp;
	}
	/* It looks like it never happens. Anyway decided to check */
	if ( pCurrentSiteTable->maxNumOfSites > MAX_SITES_BG_BAND ) {
		TRACE2( pSiteMgr->hReport, REPORT_SEVERITY_ERROR,
		        "findAndInsertSiteEntry. pCurrentSiteTable->maxNumOfSites=%d exceeds the limit %d\n",
		        pCurrentSiteTable->maxNumOfSites, MAX_SITES_BG_BAND);
		handleRunProblem(PROBLEM_BUF_SIZE_VIOLATION);
		return NULL;
	}
	/* Loop all the sites till the desired MAC is found */
	for (i = 0; i < pCurrentSiteTable->maxNumOfSites; i++) {
		pSiteEntry = &(pCurrentSiteTable->siteTable[i]);

		if (MAC_EQUAL (pSiteEntry->bssid, *mac)) {

			TRACE6(pSiteMgr->hReport, REPORT_SEVERITY_INFORMATION, "FIND success, bssid: %X-%X-%X-%X-%X-%X\n\n", (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5]);

			return pSiteEntry;
		} else if (pSiteEntry->siteType == SITE_NULL) {
			/* Save the first empty site, in case the
			desired MAC is not found */
			if (!firstEmptySiteFound) {
				emptySiteIndex = i;
				firstEmptySiteFound=TI_TRUE;
			}

		} else if (oldestTS == pSiteEntry->localTimeStamp) {
			/* Save the oldest site's index, according to TS */
			nextSite2Remove = i;
		}
	}

	TRACE4(pSiteMgr->hReport, REPORT_SEVERITY_INFORMATION, "INSERT failure, no free entry!, oldestTS=%d, nextSite2Remove=%d, "
	       "[0].localTimeStamp=%d, [1]localTimeStamp%d \n",
	       oldestTS, nextSite2Remove,
	       pCurrentSiteTable->siteTable[0].localTimeStamp,
	       pCurrentSiteTable->siteTable[1].localTimeStamp);

	if ((!firstEmptySiteFound) || (pCurrentSiteTable->numOfSites>=pCurrentSiteTable->maxNumOfSites)) {
		/* No NULL entry has been found. Remove the oldest site */
		pSiteEntry =  &(pCurrentSiteTable->siteTable[nextSite2Remove]);
		TRACE9(pSiteMgr->hReport, REPORT_SEVERITY_INFORMATION, "INSERT failure, no free entry!, numOfSites=%d, removing site index=%d,\n                                bssid: %X-%X-%X-%X-%X-%X, ts=%d \n", pCurrentSiteTable->numOfSites, nextSite2Remove, pSiteEntry->bssid[0], pSiteEntry->bssid[1], pSiteEntry->bssid[2], pSiteEntry->bssid[3], pSiteEntry->bssid[4], pSiteEntry->bssid[5], pSiteEntry->localTimeStamp);
		removeSiteEntry(pSiteMgr, pCurrentSiteTable, pSiteEntry);
		emptySiteIndex = nextSite2Remove;

	}


	pCurrentSiteTable->numOfSites++;

	pSiteEntry = &(pCurrentSiteTable->siteTable[emptySiteIndex]);

	/* fill the entry with the station mac */
	MAC_COPY (pSiteEntry->bssid, *mac);

	/* Some parameters have to be initialized immediately after entry allocation */

	if(pSiteMgr->siteMgrOperationalMode == DOT11_G_MODE)
		pSiteEntry->currentSlotTime = pSiteMgr->pDesiredParams->siteMgrDesiredSlotTime;

	TRACE8(pSiteMgr->hReport, REPORT_SEVERITY_INFORMATION, "INSERT success, bssid: %X-%X-%X-%X-%X-%X, band=%d, index=%d\n\n", (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5], band, emptySiteIndex);


	return pSiteEntry;
}