/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2 of the GNU General Public License ("GPLv2 License")
 * or BSD License.
 *
 * GPLv2 License
 *
 * Copyright(C) 2016 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2016 MediaTek Inc. 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 of the copyright holder 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
 * HOLDER 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.
 *
 *****************************************************************************/
/*! \file   fw_dl.c
 */

/*******************************************************************************
 *                         C O M P I L E R   F L A G S
 *******************************************************************************
 */

/*******************************************************************************
 *                    E X T E R N A L   R E F E R E N C E S
 *******************************************************************************
 */
#include "precomp.h"

/*******************************************************************************
 *                              C O N S T A N T S
 *******************************************************************************
 */


/*******************************************************************************
 *                            P U B L I C   D A T A
 *******************************************************************************
 */

/*******************************************************************************
 *                           P R I V A T E   D A T A
 *******************************************************************************
 */

/*******************************************************************************
 *                              F U N C T I O N S
 *******************************************************************************
 */

#if CFG_ENABLE_FW_DOWNLOAD
uint32_t wlanGetDataMode(IN struct ADAPTER *prAdapter,
	IN enum ENUM_IMG_DL_IDX_T eDlIdx, IN uint8_t ucFeatureSet)
{
	uint32_t u4DataMode = 0;

	if (ucFeatureSet & DOWNLOAD_CONFIG_ENCRYPTION_MODE) {
		u4DataMode |= DOWNLOAD_CONFIG_RESET_OPTION;
		u4DataMode |= (ucFeatureSet &
			       DOWNLOAD_CONFIG_KEY_INDEX_MASK);
		u4DataMode |= DOWNLOAD_CONFIG_ENCRYPTION_MODE;
	}

	if (eDlIdx == IMG_DL_IDX_CR4_FW)
		u4DataMode |= DOWNLOAD_CONFIG_WORKING_PDA_OPTION;

#if CFG_ENABLE_FW_DOWNLOAD_ACK
	u4DataMode |= DOWNLOAD_CONFIG_ACK_OPTION;	/* ACK needed */
#endif
	return u4DataMode;
}

void wlanGetHarvardFwInfo(IN struct ADAPTER *prAdapter,
	IN uint8_t u4SecIdx, IN enum ENUM_IMG_DL_IDX_T eDlIdx,
	OUT uint32_t *pu4Addr, OUT uint32_t *pu4Len,
	OUT uint32_t *pu4DataMode, OUT u_int8_t *pfgIsEMIDownload)
{
	struct TAILER_FORMAT_T *prTailer;

	if (eDlIdx == IMG_DL_IDX_N9_FW)
		prTailer = &prAdapter->rVerInfo.rN9tailer[u4SecIdx];
	else
		prTailer = &prAdapter->rVerInfo.rCR4tailer[u4SecIdx];

	*pu4Addr = prTailer->addr;
	*pu4Len = (prTailer->len + LEN_4_BYTE_CRC);
	*pu4DataMode = wlanGetDataMode(prAdapter, eDlIdx,
				       prTailer->feature_set);
	*pfgIsEMIDownload = FALSE;
}

void wlanGetConnacFwInfo(IN struct ADAPTER *prAdapter,
	IN uint8_t u4SecIdx, IN enum ENUM_IMG_DL_IDX_T eDlIdx,
	OUT uint32_t *pu4Addr, OUT uint32_t *pu4Len,
	OUT uint32_t *pu4DataMode, OUT u_int8_t *pfgIsEMIDownload)
{
	struct TAILER_REGION_FORMAT_T *prTailer =
			&prAdapter->rVerInfo.rRegionTailers[u4SecIdx];

	*pu4Addr = prTailer->u4Addr;
	*pu4Len = prTailer->u4Len;
	*pu4DataMode = wlanGetDataMode(prAdapter, eDlIdx,
				       prTailer->ucFeatureSet);
	*pfgIsEMIDownload = prTailer->ucFeatureSet &
			    DOWNLOAD_CONFIG_EMI;
}

#if CFG_SUPPORT_COMPRESSION_FW_OPTION
void wlanImageSectionGetCompressFwInfo(IN struct ADAPTER
	*prAdapter, IN void *pvFwImageMapFile,
	IN uint32_t u4FwImageFileLength, IN uint8_t ucTotSecNum,
	IN uint8_t ucCurSecNum, IN enum ENUM_IMG_DL_IDX_T eDlIdx,
	OUT uint32_t *pu4Addr, OUT uint32_t *pu4Len,
	OUT uint32_t *pu4DataMode, OUT uint32_t *pu4BlockSize,
	OUT uint32_t *pu4CRC, OUT uint32_t *pu4UncompressedLength)
{
	struct FW_IMAGE_TAILER_T_2 *prFwHead;
	struct TAILER_FORMAT_T_2 *prTailer;
	uint8_t aucBuf[32];

	prFwHead = (struct FW_IMAGE_TAILER_T_2 *)
		   (pvFwImageMapFile + u4FwImageFileLength - sizeof(
			    struct FW_IMAGE_TAILER_T_2));
	if (ucTotSecNum == 1)
		prTailer = &prFwHead->dlm_info;
	else
		prTailer = &prFwHead->ilm_info;

	prTailer = &prTailer[ucCurSecNum];

	*pu4Addr = prTailer->addr;
	*pu4Len = (prTailer->len);
	*pu4BlockSize = (prTailer->block_size);
	*pu4CRC = (prTailer->crc);
	*pu4UncompressedLength = (prTailer->real_size);
	*pu4DataMode = wlanGetDataMode(prAdapter, eDlIdx,
				       prTailer->feature_set);

	/* Dump image information */
	if (ucCurSecNum == 0) {
		DBGLOG(INIT, INFO,
		       "%s INFO: chip_info[%u:E%u] feature[0x%02X]\n",
		       (eDlIdx == IMG_DL_IDX_N9_FW) ? "N9" : "CR4",
		       prTailer->chip_info,
		       prTailer->eco_code, prTailer->feature_set);
		kalMemZero(aucBuf, 32);
		kalStrnCpy(aucBuf, prTailer->ram_version,
			   sizeof(prTailer->ram_version));
		DBGLOG(INIT, INFO, "date[%s] version[%s]\n",
		       prTailer->ram_built_date, aucBuf);
	}
	/* Backup to FW version info */
	if (eDlIdx == IMG_DL_IDX_N9_FW) {
		kalMemCopy(&prAdapter->rVerInfo.rN9Compressedtailer,
			   prTailer, sizeof(struct TAILER_FORMAT_T_2));
		prAdapter->rVerInfo.fgIsN9CompressedFW = TRUE;
	} else {
		kalMemCopy(&prAdapter->rVerInfo.rCR4Compressedtailer,
			   prTailer, sizeof(struct TAILER_FORMAT_T_2));
		prAdapter->rVerInfo.fgIsCR4CompressedFW = TRUE;
	}
}
#endif

void wlanImageSectionGetPatchInfo(IN struct ADAPTER
	*prAdapter,
	IN void *pvFwImageMapFile, IN uint32_t u4FwImageFileLength,
	OUT uint32_t *pu4StartOffset, OUT uint32_t *pu4Addr,
	OUT uint32_t *pu4Len,
	OUT uint32_t *pu4DataMode)
{
	struct PATCH_FORMAT_T *prPatchFormat;
	uint8_t aucBuffer[32];
	struct mt66xx_chip_info *prChipInfo = prAdapter->chip_info;

	prPatchFormat = (struct PATCH_FORMAT_T *) pvFwImageMapFile;

	*pu4StartOffset = offsetof(struct PATCH_FORMAT_T,
				   ucPatchImage);
	*pu4Addr = prChipInfo->patch_addr;
	*pu4Len = u4FwImageFileLength - offsetof(struct
			PATCH_FORMAT_T, ucPatchImage);
	*pu4DataMode = wlanGetDataMode(prAdapter, IMG_DL_IDX_PATCH,
				       0);

	/* Dump image information */
	kalMemZero(aucBuffer, 32);
	kalStrnCpy(aucBuffer, prPatchFormat->aucPlatform, 4);
	DBGLOG(INIT, INFO,
	       "PATCH INFO: platform[%s] HW/SW ver[0x%04X] ver[0x%04X]\n",
	       aucBuffer, prPatchFormat->u4SwHwVersion,
	       prPatchFormat->u4PatchVersion);

	kalStrnCpy(aucBuffer, prPatchFormat->aucBuildDate, 16);
	DBGLOG(INIT, INFO, "date[%s]\n", aucBuffer);

	/* Backup to FW version info */
	kalMemCopy(&prAdapter->rVerInfo.rPatchHeader, prPatchFormat,
		   sizeof(struct PATCH_FORMAT_T));
}

uint32_t wlanDownloadSection(IN struct ADAPTER *prAdapter,
			     IN uint32_t u4Addr, IN uint32_t u4Len,
			     IN uint32_t u4DataMode, IN uint8_t *pucStartPtr,
			     IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	uint32_t u4ImgSecSize, u4Offset;
	uint8_t *pucSecBuf;

	if (wlanImageSectionConfig(prAdapter, u4Addr, u4Len,
				   u4DataMode, eDlIdx) != WLAN_STATUS_SUCCESS) {
		DBGLOG(INIT, ERROR,
		       "Firmware download configuration failed!\n");
		return WLAN_STATUS_FAILURE;
	}

	for (u4Offset = 0; u4Offset < u4Len;
	     u4Offset += CMD_PKT_SIZE_FOR_IMAGE) {
		if (u4Offset + CMD_PKT_SIZE_FOR_IMAGE < u4Len)
			u4ImgSecSize = CMD_PKT_SIZE_FOR_IMAGE;
		else
			u4ImgSecSize = u4Len - u4Offset;

		pucSecBuf = (uint8_t *) pucStartPtr + u4Offset;
		if (wlanImageSectionDownload(prAdapter, u4ImgSecSize,
					     pucSecBuf) !=
					     WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, ERROR,
				"Firmware scatter download failed!\n");
			return WLAN_STATUS_FAILURE;
		}
	}

	return WLAN_STATUS_SUCCESS;
}

uint32_t wlanDownloadEMISection(IN struct ADAPTER
				*prAdapter, IN uint32_t u4DestAddr,
				IN uint32_t u4Len, IN uint8_t *pucStartPtr)
{
#if CFG_MTK_ANDROID_EMI
	uint8_t __iomem *pucEmiBaseAddr = NULL;
	uint32_t u4Offset = u4DestAddr & WIFI_EMI_ADDR_MASK;

	if (!gConEmiPhyBase) {
		DBGLOG(INIT, ERROR,
		       "Consys emi memory address gConEmiPhyBase invalid\n");
		return WLAN_STATUS_FAILURE;
	}

	request_mem_region(gConEmiPhyBase, gConEmiSize, "WIFI-EMI");
	kalSetEmiMpuProtection(gConEmiPhyBase, WIFI_EMI_MEM_OFFSET,
			       WIFI_EMI_MEM_SIZE, false);
	pucEmiBaseAddr = ioremap_nocache(gConEmiPhyBase, gConEmiSize);
	DBGLOG(INIT, INFO,
	       "EmiPhyBase:0x%llx offset:0x%x, ioremap region 0x%lX @ 0x%lX\n",
	       (uint64_t)gConEmiPhyBase, u4Offset, gConEmiSize, pucEmiBaseAddr);
	if (!pucEmiBaseAddr) {
		DBGLOG(INIT, ERROR, "ioremap failed\n");
		return WLAN_STATUS_FAILURE;
	}

	kalMemCopy((pucEmiBaseAddr + u4Offset), pucStartPtr, u4Len);

	kalSetEmiMpuProtection(gConEmiPhyBase, WIFI_EMI_MEM_OFFSET,
			       WIFI_EMI_MEM_SIZE, true);
	iounmap(pucEmiBaseAddr);
	release_mem_region(gConEmiPhyBase, gConEmiSize);
#endif /* CFG_MTK_ANDROID_EMI */
	return WLAN_STATUS_SUCCESS;
}

#if CFG_SUPPORT_COMPRESSION_FW_OPTION
u_int8_t wlanImageSectionCheckFwCompressInfo(
	IN struct ADAPTER *prAdapter,
	IN void *pvFwImageMapFile, IN uint32_t u4FwImageFileLength,
	IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	uint8_t ucCompression;
	struct FW_IMAGE_TAILER_CHECK *prCheckInfo;

	if (eDlIdx == IMG_DL_IDX_PATCH)
		return FALSE;

	prCheckInfo = (struct FW_IMAGE_TAILER_CHECK *)
		      (pvFwImageMapFile + u4FwImageFileLength - sizeof(
			       struct FW_IMAGE_TAILER_CHECK));
	DBGLOG(INIT, INFO, "feature_set %d\n",
	       prCheckInfo->feature_set);
	ucCompression = (uint8_t)((prCheckInfo->feature_set &
				   COMPRESSION_OPTION_MASK)
				  >> COMPRESSION_OPTION_OFFSET);
	DBGLOG(INIT, INFO, "Compressed Check INFORMATION %d\n",
	       ucCompression);
	if (ucCompression == 1) {
		DBGLOG(INIT, INFO, "Compressed FW\n");
		return TRUE;
	}
	return FALSE;
}

uint32_t wlanCompressedImageSectionDownloadStage(
	IN struct ADAPTER *prAdapter, IN void *pvFwImageMapFile,
	IN uint32_t u4FwImageFileLength, IN uint8_t ucSectionNumber,
	IN enum ENUM_IMG_DL_IDX_T eDlIdx,
	OUT uint8_t *pucIsCompressed,
	OUT struct INIT_CMD_WIFI_DECOMPRESSION_START *prFwImageInFo)
{
	uint32_t i;
	int32_t  i4TotalLen;
	uint32_t u4FileOffset = 0;
	uint32_t u4StartOffset = 0;
	uint32_t u4DataMode = 0;
	uint32_t u4Addr, u4Len, u4BlockSize, u4CRC;
	uint32_t u4UnCompressedLength;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	uint8_t *pucStartPtr;
	uint32_t u4offset = 0, u4ChunkSize;
	/* 3a. parse file header for decision of
	 * divided firmware download or not
	 */
	if (wlanImageSectionCheckFwCompressInfo(prAdapter,
						pvFwImageMapFile,
						u4FwImageFileLength,
						eDlIdx) == TRUE) {
		for (i = 0; i < ucSectionNumber; ++i) {
			wlanImageSectionGetCompressFwInfo(prAdapter,
				pvFwImageMapFile,
				u4FwImageFileLength, ucSectionNumber, i, eDlIdx,
				&u4Addr, &u4Len, &u4DataMode,
				&u4BlockSize, &u4CRC, &u4UnCompressedLength);
			u4offset = 0;
			if (i == 0) {
				prFwImageInFo->u4BlockSize = u4BlockSize;
				prFwImageInFo->u4Region1Address = u4Addr;
				prFwImageInFo->u4Region1CRC = u4CRC;
				prFwImageInFo->u4Region1length =
					u4UnCompressedLength;
			} else {
				prFwImageInFo->u4Region2Address = u4Addr;
				prFwImageInFo->u4Region2CRC = u4CRC;
				prFwImageInFo->u4Region2length =
					u4UnCompressedLength;
			}
			i4TotalLen = u4Len;
			DBGLOG(INIT, INFO,
			       "DL Offset[%u] addr[0x%08x] len[%u] datamode[0x%08x]\n",
			       u4FileOffset, u4Addr, u4Len, u4DataMode);
			DBGLOG(INIT, INFO, "DL BLOCK[%u]  COMlen[%u] CRC[%u]\n",
			       u4BlockSize, u4UnCompressedLength, u4CRC);
			pucStartPtr =
				(uint8_t *) pvFwImageMapFile + u4StartOffset;
			while (i4TotalLen) {
				u4ChunkSize =  *((unsigned int *)(pucStartPtr +
					u4FileOffset));
				u4FileOffset += 4;
				DBGLOG(INIT, INFO,
					"Downloaded Length %d! Addr %x\n",
				  i4TotalLen, u4Addr + u4offset);
				DBGLOG(INIT, INFO,
					"u4ChunkSize Length %d!\n",
					u4ChunkSize);

				u4Status = wlanDownloadSection(prAdapter,
					u4Addr + u4offset,
					u4ChunkSize,
					u4DataMode,
					pvFwImageMapFile + u4FileOffset,
					eDlIdx);
				/* escape from loop if any
				 * pending error occurs
				 */
				if (u4Status == WLAN_STATUS_FAILURE)
					break;

				i4TotalLen -= u4ChunkSize;
				u4offset += u4BlockSize;
				u4FileOffset += u4ChunkSize;
				if (i4TotalLen < 0) {
					DBGLOG(INIT, ERROR,
						"Firmware scatter download failed!\n");
					u4Status = WLAN_STATUS_FAILURE;
					break;
				}
			}
		}
		*pucIsCompressed = TRUE;
	} else {
		u4Status = wlanImageSectionDownloadStage(prAdapter,
				pvFwImageMapFile, u4FwImageFileLength,
				ucSectionNumber, eDlIdx);
		*pucIsCompressed = FALSE;
	}
	return u4Status;
}
#endif

uint32_t wlanImageSectionDownloadStage(
	IN struct ADAPTER *prAdapter, IN void *pvFwImageMapFile,
	IN uint32_t u4FwImageFileLength, IN uint8_t ucSectionNumber,
	IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	uint32_t u4SecIdx, u4Offset = 0;
	uint32_t u4Addr, u4Len, u4DataMode = 0;
	u_int8_t fgIsEMIDownload = FALSE;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo = prAdapter->chip_info;

	/* 3a. parse file header for decision of
	 * divided firmware download or not
	 */
	if (eDlIdx == IMG_DL_IDX_PATCH) {
		wlanImageSectionGetPatchInfo(prAdapter, pvFwImageMapFile,
					     u4FwImageFileLength,
					     &u4Offset, &u4Addr,
					     &u4Len, &u4DataMode);

		DBGLOG(INIT, INFO,
		       "DL Offset[%u] addr[0x%08x] len[%u] datamode[0x%08x]\n",
		       u4Offset, u4Addr, u4Len, u4DataMode);

		u4Status = wlanDownloadSection(prAdapter, u4Addr, u4Len,
					       u4DataMode,
					       pvFwImageMapFile + u4Offset,
					       eDlIdx);
	} else {
		for (u4SecIdx = 0; u4SecIdx < ucSectionNumber;
		     u4SecIdx++, u4Offset += u4Len) {
			prChipInfo->fw_dl_ops->getFwInfo(prAdapter, u4SecIdx,
				eDlIdx, &u4Addr,
				&u4Len, &u4DataMode, &fgIsEMIDownload);

			DBGLOG(INIT, INFO,
			       "DL Offset[%u] addr[0x%08x] len[%u] datamode[0x%08x]\n",
			       u4Offset, u4Addr, u4Len, u4DataMode);

			if (fgIsEMIDownload)
				u4Status = wlanDownloadEMISection(prAdapter,
					u4Addr, u4Len,
					pvFwImageMapFile + u4Offset);
			else
				u4Status = wlanDownloadSection(prAdapter,
					u4Addr, u4Len,
					u4DataMode,
					pvFwImageMapFile + u4Offset, eDlIdx);

			/* escape from loop if any pending error occurs */
			if (u4Status == WLAN_STATUS_FAILURE)
				break;
		}
	}
	return u4Status;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to confirm the status of
 *        previously patch semaphore control
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *        ucCmdSeqNum    Sequence number of previous firmware scatter
 *
 * @return WLAN_STATUS_SUCCESS
 *         WLAN_STATUS_FAILURE
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanPatchRecvSemaResp(IN struct ADAPTER *prAdapter,
	IN uint8_t ucCmdSeqNum, OUT uint8_t *pucPatchStatus)
{
	struct mt66xx_chip_info *prChipInfo;
	uint8_t *aucBuffer;
	uint32_t u4EventSize;
	struct INIT_WIFI_EVENT *prInitEvent;
	struct INIT_EVENT_CMD_RESULT *prEventCmdResult;
	uint32_t u4RxPktLength;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	if (kalIsCardRemoved(prAdapter->prGlueInfo) == TRUE
	    || fgIsBusAccessFailed == TRUE)
		return WLAN_STATUS_FAILURE;

	u4EventSize = prChipInfo->rxd_size + prChipInfo->init_event_size +
		sizeof(struct INIT_EVENT_CMD_RESULT);
	aucBuffer = kalMemAlloc(u4EventSize, PHY_MEM_TYPE);

	if (nicRxWaitResponse(prAdapter, 0, aucBuffer, u4EventSize,
			      &u4RxPktLength) != WLAN_STATUS_SUCCESS) {

		DBGLOG(INIT, WARN, "Wait patch semaphore response fail\n");
		kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);
		return WLAN_STATUS_FAILURE;
	}

	prInitEvent = (struct INIT_WIFI_EVENT *)
		(aucBuffer + prChipInfo->rxd_size);
	if (prInitEvent->ucEID != INIT_EVENT_ID_PATCH_SEMA_CTRL) {
		DBGLOG(INIT, WARN, "Unexpected EVENT ID, get 0x%0x\n",
		       prInitEvent->ucEID);
		kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);
		return WLAN_STATUS_FAILURE;
	}

	if (prInitEvent->ucSeqNum != ucCmdSeqNum) {
		DBGLOG(INIT, WARN, "Unexpected SeqNum %d, %d\n",
		       ucCmdSeqNum, prInitEvent->ucSeqNum);
		kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);
		return WLAN_STATUS_FAILURE;
	}

	prEventCmdResult = (struct INIT_EVENT_CMD_RESULT *)
		prInitEvent->aucBuffer;

	*pucPatchStatus = prEventCmdResult->ucStatus;

	kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);

	return WLAN_STATUS_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to check the patch semaphore control.
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *
 * @return (none)
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanPatchSendSemaControl(IN struct ADAPTER
				  *prAdapter, OUT uint8_t *pucSeqNum)
{
	struct mt66xx_chip_info *prChipInfo;
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct INIT_CMD_PATCH_SEMA_CONTROL *prPatchSemaControl;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	DEBUGFUNC("wlanImagePatchSemaphoreCheck");

	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo =
		cmdBufAllocateCmdInfo(prAdapter,
			sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
			struct INIT_CMD_PATCH_SEMA_CONTROL));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER)
				  + sizeof(struct INIT_CMD_PATCH_SEMA_CONTROL);

	/* 2. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;
	prInitHifTxHeader->ucHeaderFormat = INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_CMD;

	prInitHifTxHeader->rInitWifiCmd.ucCID =
		INIT_CMD_ID_PATCH_SEMAPHORE_CONTROL;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PDA_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum =
		nicIncreaseCmdSeqNum(prAdapter);

	*pucSeqNum = prInitHifTxHeader->rInitWifiCmd.ucSeqNum;

	/* 3. Setup DOWNLOAD_BUF */
	prPatchSemaControl = (struct INIT_CMD_PATCH_SEMA_CONTROL *)
			     prInitHifTxHeader->rInitWifiCmd.aucBuffer;
	kalMemZero(prPatchSemaControl,
		   sizeof(struct INIT_CMD_PATCH_SEMA_CONTROL));
	prPatchSemaControl->ucGetSemaphore = PATCH_GET_SEMA_CONTROL;

	/* 4. Send FW_Download command */
	if (nicTxInitCmd(prAdapter, prCmdInfo,
			 prChipInfo->u2TxInitCmdPort) != WLAN_STATUS_SUCCESS) {
		u4Status = WLAN_STATUS_FAILURE;
		DBGLOG(INIT, ERROR,
		       "Fail to transmit image download command\n");
	}
	/* 5. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}

u_int8_t wlanPatchIsDownloaded(IN struct ADAPTER *prAdapter)
{
	uint8_t ucSeqNum, ucPatchStatus;
	uint32_t rStatus;
	uint32_t u4Count;

	ucPatchStatus = PATCH_STATUS_NO_SEMA_NEED_PATCH;
	u4Count = 0;

	while (ucPatchStatus == PATCH_STATUS_NO_SEMA_NEED_PATCH) {
		if (u4Count)
			kalMdelay(100);

		rStatus = wlanPatchSendSemaControl(prAdapter, &ucSeqNum);
		if (rStatus != WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, WARN,
			       "Send patch SEMA control CMD failed!!\n");
			break;
		}

		rStatus = wlanPatchRecvSemaResp(prAdapter, ucSeqNum,
						&ucPatchStatus);
		if (rStatus != WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, WARN,
			       "Recv patch SEMA control EVT failed!!\n");
			break;
		}

		u4Count++;

		if (u4Count > 50) {
			DBGLOG(INIT, WARN, "Patch status check timeout!!\n");
			break;
		}
	}

	if (ucPatchStatus == PATCH_STATUS_NO_NEED_TO_PATCH)
		return TRUE;
	else
		return FALSE;
}

uint32_t wlanPatchSendComplete(IN struct ADAPTER *prAdapter)
{
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	uint8_t ucTC, ucCmdSeqNum;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo;
	struct INIT_CMD_PATCH_FINISH *prPatchFinish;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
		sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
		struct INIT_CMD_PATCH_FINISH));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	kalMemZero(prCmdInfo->pucInfoBuffer,
		   sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
			   struct INIT_CMD_PATCH_FINISH));
	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER)
				  + sizeof(struct INIT_CMD_PATCH_FINISH);

#if (CFG_USE_TC4_RESOURCE_FOR_INIT_CMD == 1)
	/* 2. Always use TC4 (TC4 as CPU) */
	ucTC = TC4_INDEX;
#else
	/* 2. Use TC0's resource to send patch finish command.
	 * Only TC0 is allowed because SDIO HW always reports
	 * MCU's TXQ_CNT at TXQ0_CNT in CR4 architecutre)
	 */
	ucTC = TC0_INDEX;
#endif

	/* 3. increase command sequence number */
	ucCmdSeqNum = nicIncreaseCmdSeqNum(prAdapter);

	/* 4. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_CMD;

	prInitHifTxHeader->rInitWifiCmd.ucCID =
		INIT_CMD_ID_PATCH_FINISH;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = ucCmdSeqNum;

	prPatchFinish = (struct INIT_CMD_PATCH_FINISH *)
			prInitHifTxHeader->rInitWifiCmd.aucBuffer;
	prPatchFinish->ucCheckCrc = 0;

	/* 5. Seend WIFI start command */
	while (1) {
		/* 5.1 Acquire TX Resource */
		if (nicTxAcquireResource(prAdapter, ucTC,
					 nicTxGetPageCount(prAdapter,
					 prCmdInfo->u2InfoBufLen, TRUE),
					 TRUE) == WLAN_STATUS_RESOURCES) {
			if (nicTxPollingResource(prAdapter,
						 ucTC) != WLAN_STATUS_SUCCESS) {
				u4Status = WLAN_STATUS_FAILURE;
				DBGLOG(INIT, ERROR,
				       "Fail to get TX resource return within timeout\n");
				goto exit;
			}
			continue;
		}
		/* 5.2 Send CMD Info Packet */
		if (nicTxInitCmd(prAdapter, prCmdInfo,
				 prChipInfo->u2TxInitCmdPort) !=
				 WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			DBGLOG(INIT, ERROR,
			       "Fail to transmit WIFI start command\n");
			goto exit;
		}

		break;
	};

	DBGLOG(INIT, INFO,
	       "PATCH FINISH CMD send, waiting for RSP\n");

	/* kalMdelay(10000); */

	u4Status = wlanConfigWifiFuncStatus(prAdapter, ucCmdSeqNum);

	if (u4Status != WLAN_STATUS_SUCCESS)
		DBGLOG(INIT, INFO, "PATCH FINISH EVT failed\n");
	else
		DBGLOG(INIT, INFO, "PATCH FINISH EVT success!!\n");

exit:
	/* 6. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to configure FWDL parameters
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *        u4DestAddr     Address of destination address
 *        u4ImgSecSize   Length of the firmware block
 *        fgReset        should be set to TRUE if this is the 1st configuration
 *
 * @return WLAN_STATUS_SUCCESS
 *         WLAN_STATUS_FAILURE
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanImageSectionConfig(
	IN struct ADAPTER *prAdapter,
	IN uint32_t u4DestAddr, IN uint32_t u4ImgSecSize,
	IN uint32_t u4DataMode,
	IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	struct INIT_CMD_DOWNLOAD_CONFIG *prInitCmdDownloadConfig;
	uint8_t ucTC, ucCmdSeqNum;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	DEBUGFUNC("wlanImageSectionConfig");

	if (u4ImgSecSize == 0)
		return WLAN_STATUS_SUCCESS;
	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
				sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
				struct INIT_CMD_DOWNLOAD_CONFIG));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER)
				  + sizeof(struct INIT_CMD_DOWNLOAD_CONFIG);

#if (CFG_USE_TC4_RESOURCE_FOR_INIT_CMD == 1)
	/* 2. Use TC4's resource to download image. (TC4 as CPU) */
	ucTC = TC4_INDEX;
#else
	/* 2. Use TC0's resource to send init_cmd.
	 * Only TC0 is allowed because SDIO HW always reports
	 * MCU's TXQ_CNT at TXQ0_CNT in CR4 architecutre)
	 */
	ucTC = TC0_INDEX;
#endif

	/* 3. increase command sequence number */
	ucCmdSeqNum = nicIncreaseCmdSeqNum(prAdapter);

	/* 4. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;
	prInitHifTxHeader->ucHeaderFormat = INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_CMD;

	if (eDlIdx == IMG_DL_IDX_PATCH)
		prInitHifTxHeader->rInitWifiCmd.ucCID =
			INIT_CMD_ID_PATCH_START;
	else
		prInitHifTxHeader->rInitWifiCmd.ucCID =
			INIT_CMD_ID_DOWNLOAD_CONFIG;


	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = ucCmdSeqNum;

	/* 5. Setup CMD_DOWNLOAD_CONFIG */
	prInitCmdDownloadConfig =
		(struct INIT_CMD_DOWNLOAD_CONFIG *)
			(prInitHifTxHeader->rInitWifiCmd.aucBuffer);
	prInitCmdDownloadConfig->u4Address = u4DestAddr;
	prInitCmdDownloadConfig->u4Length = u4ImgSecSize;
	prInitCmdDownloadConfig->u4DataMode = u4DataMode;

	/* 6. Send FW_Download command */
	while (1) {
		/* 6.1 Acquire TX Resource */
		if (nicTxAcquireResource(prAdapter, ucTC,
					 nicTxGetPageCount(prAdapter,
					 prCmdInfo->u2InfoBufLen, TRUE),
					 TRUE) == WLAN_STATUS_RESOURCES) {
			if (nicTxPollingResource(prAdapter,
						 ucTC) != WLAN_STATUS_SUCCESS) {
				u4Status = WLAN_STATUS_FAILURE;
				DBGLOG(INIT, ERROR,
				       "Fail to get TX resource return within timeout\n");
				goto exit;
			}
			continue;
		}
		/* 6.2 Send CMD Info Packet */
		if (nicTxInitCmd(prAdapter, prCmdInfo,
				 prChipInfo->u2TxInitCmdPort) !=
				 WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			DBGLOG(INIT, ERROR,
			       "Fail to transmit image download command\n");
			goto exit;
		}

		break;
	};

#if CFG_ENABLE_FW_DOWNLOAD_ACK
	/* 7. Wait for INIT_EVENT_ID_CMD_RESULT */
	u4Status = wlanConfigWifiFuncStatus(prAdapter, ucCmdSeqNum);
#endif

exit:
	/* 8. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to download FW image.
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *
 * @return (none)
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanImageSectionDownload(IN struct ADAPTER
				  *prAdapter, IN uint32_t u4ImgSecSize,
				  IN uint8_t *pucImgSecBuf)
{
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo;

	ASSERT(prAdapter);
	ASSERT(pucImgSecBuf);
	ASSERT(u4ImgSecSize <= CMD_PKT_SIZE_FOR_IMAGE);

	DEBUGFUNC("wlanImageSectionDownload");

	prChipInfo = prAdapter->chip_info;

	if (u4ImgSecSize == 0)
		return WLAN_STATUS_SUCCESS;
	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
				sizeof(struct INIT_HIF_TX_HEADER) +
				u4ImgSecSize);

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER)
				  + (uint16_t) u4ImgSecSize;

	/* 2. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PDA_PQ_ID;
	prInitHifTxHeader->ucHeaderFormat =
		INIT_CMD_PDA_PACKET_TYPE_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_PDA_FWDL;

	prInitHifTxHeader->rInitWifiCmd.ucCID = 0;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PDA_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = 0;

	/* 3. Setup DOWNLOAD_BUF */
	kalMemCopy(prInitHifTxHeader->rInitWifiCmd.aucBuffer,
		   pucImgSecBuf, u4ImgSecSize);

	/* 4. Send FW_Download command */
	if (nicTxInitCmd(prAdapter, prCmdInfo,
			 prChipInfo->u2TxFwDlPort) != WLAN_STATUS_SUCCESS) {
		u4Status = WLAN_STATUS_FAILURE;
		DBGLOG(INIT, ERROR,
		       "Fail to transmit image download command\n");
	}
	/* 5. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to confirm previously firmware
 *        download is done without error
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *
 * @return (none)
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanImageQueryStatus(IN struct ADAPTER *prAdapter)
{
	struct mt66xx_chip_info *prChipInfo;
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	uint8_t *aucBuffer;
	uint32_t u4EventSize;
	uint32_t u4RxPktLength;
	struct INIT_WIFI_EVENT *prInitEvent;
	struct INIT_EVENT_CMD_RESULT *prEventPendingError;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	uint8_t ucTC, ucCmdSeqNum;
	struct HW_MAC_RX_DESC *prRxStatus;
	uint8_t ucUnexpectCnt = 0;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	DEBUGFUNC("wlanImageQueryStatus");

	/* 1. Allocate CMD Info Packet and it Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
					  sizeof(struct INIT_HIF_TX_HEADER));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	u4EventSize = prChipInfo->rxd_size + prChipInfo->init_event_size +
		sizeof(struct INIT_EVENT_CMD_RESULT);
	aucBuffer = kalMemAlloc(u4EventSize, PHY_MEM_TYPE);

	kalMemZero(prCmdInfo->pucInfoBuffer,
		   sizeof(struct INIT_HIF_TX_HEADER));
	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER);

#if (CFG_USE_TC4_RESOURCE_FOR_INIT_CMD == 1)
	/* 2. Always use TC4 */
	ucTC = TC4_INDEX;
#else
	/* 2. Use TC0's resource to send init_cmd
	 * Only TC0 is allowed because SDIO HW always reports
	 * CPU's TXQ_CNT at TXQ0_CNT in CR4 architecutre)
	 */
	ucTC = TC0_INDEX;
#endif

	/* 3. increase command sequence number */
	ucCmdSeqNum = nicIncreaseCmdSeqNum(prAdapter);

	/* 4. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);

	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;

	prInitHifTxHeader->rInitWifiCmd.ucCID =
		INIT_CMD_ID_QUERY_PENDING_ERROR;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = ucCmdSeqNum;

	/* 5. Send command */
	while (1) {
		/* 5.1 Acquire TX Resource */
		if (nicTxAcquireResource(prAdapter, ucTC,
					 nicTxGetPageCount(prAdapter,
					 prCmdInfo->u2InfoBufLen, TRUE),
					 TRUE) == WLAN_STATUS_RESOURCES) {
			if (nicTxPollingResource(prAdapter,
						 ucTC) != WLAN_STATUS_SUCCESS) {
				u4Status = WLAN_STATUS_FAILURE;
				DBGLOG(INIT, ERROR,
				       "Fail to get TX resource return within timeout\n");
				break;
			}
			continue;

		}
		/* 5.2 Send CMD Info Packet */
		if (nicTxInitCmd(prAdapter, prCmdInfo,
				 prChipInfo->u2TxInitCmdPort) !=
				 WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			DBGLOG(INIT, ERROR,
			       "Fail to transmit image download command\n");
		}

		break;
	};

	/* 6. Wait for INIT_EVENT_ID_PENDING_ERROR */
	while (TRUE) {
		if (kalIsCardRemoved(prAdapter->prGlueInfo) == TRUE
		    || fgIsBusAccessFailed == TRUE) {
			u4Status = WLAN_STATUS_FAILURE;
			break;
		} else if (nicRxWaitResponse(prAdapter, 0,
					     aucBuffer, u4EventSize,
					     &u4RxPktLength) !=
			   WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			break;
		} else {
			/* header checking .. */
			prRxStatus = (struct HW_MAC_RX_DESC *) aucBuffer;
			if (prRxStatus->u2PktTYpe !=
				RXM_RXD_PKT_TYPE_SW_EVENT) {
				DBGLOG(INIT, ERROR,
					"%s: skip unexpected Rx pkt type[0x%04x]\n",
					__func__, prRxStatus->u2PktTYpe);

				ucUnexpectCnt++;

				if (ucUnexpectCnt > 5) {
					DBGLOG(INIT, WARN,
					"%s: break since ucUnexpectCnt > %d\n",
					__func__, ucUnexpectCnt);
					u4Status = WLAN_STATUS_FAILURE;
					break;
				}
				continue;
			}

			prInitEvent = (struct INIT_WIFI_EVENT *)
				(aucBuffer + prChipInfo->rxd_size);

			/* EID / SeqNum check */
			if (prInitEvent->ucEID != INIT_EVENT_ID_PENDING_ERROR) {
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}	else if (prInitEvent->ucSeqNum != ucCmdSeqNum) {
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}
			prEventPendingError =
				(struct INIT_EVENT_CMD_RESULT *)
				prInitEvent->aucBuffer;
			/* 0 for download success */
			if (prEventPendingError->ucStatus != 0) {
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}
			u4Status = WLAN_STATUS_SUCCESS;
			break;
		}
	}

	/* 7. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);

	return u4Status;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is called to confirm the status of
 *        previously downloaded firmware scatter
 *
 * @param prAdapter      Pointer to the Adapter structure.
 *        ucCmdSeqNum    Sequence number of previous firmware scatter
 *
 * @return WLAN_STATUS_SUCCESS
 *         WLAN_STATUS_FAILURE
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanConfigWifiFuncStatus(IN struct ADAPTER
				  *prAdapter, IN uint8_t ucCmdSeqNum)
{
	struct mt66xx_chip_info *prChipInfo;
	uint8_t *aucBuffer;
	uint32_t u4EventSize;
	struct INIT_WIFI_EVENT *prInitEvent;
	struct INIT_EVENT_CMD_RESULT *prEventCmdResult;
	uint32_t u4RxPktLength;
	uint32_t u4Status;
	uint8_t ucPortIdx = IMG_DL_STATUS_PORT_IDX;
	struct HW_MAC_RX_DESC *prRxStatus;
	uint8_t ucUnexpectCnt = 0;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	u4EventSize = prChipInfo->rxd_size + prChipInfo->init_event_size +
		sizeof(struct INIT_EVENT_CMD_RESULT);
	aucBuffer = kalMemAlloc(u4EventSize, PHY_MEM_TYPE);

	while (TRUE) {
		if (kalIsCardRemoved(prAdapter->prGlueInfo) == TRUE
		    || fgIsBusAccessFailed == TRUE) {
			u4Status = WLAN_STATUS_FAILURE;
			break;
		} else if (nicRxWaitResponse(prAdapter, ucPortIdx,
					     aucBuffer, u4EventSize,
					     &u4RxPktLength) !=
			   WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			break;
		} else {
			/* header checking .. */
			prRxStatus = (struct HW_MAC_RX_DESC *) aucBuffer;
			if (prRxStatus->u2PktTYpe !=
				RXM_RXD_PKT_TYPE_SW_EVENT) {
				DBGLOG(INIT, ERROR,
					"%s: skip unexpected Rx pkt type[0x%04x]\n",
					__func__, prRxStatus->u2PktTYpe);

				ucUnexpectCnt++;

				if (ucUnexpectCnt > 5) {
					DBGLOG(INIT, WARN,
					"%s: break since ucUnexpectCnt > %d\n",
					__func__, ucUnexpectCnt);
					u4Status = WLAN_STATUS_FAILURE;
					break;
				}
				continue;
			}

			prInitEvent = (struct INIT_WIFI_EVENT *)
				(aucBuffer + prChipInfo->rxd_size);

			/* EID / SeqNum check */
			if (prInitEvent->ucEID != INIT_EVENT_ID_CMD_RESULT) {
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}	else if (prInitEvent->ucSeqNum != ucCmdSeqNum) {
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}
			prEventCmdResult =
				(struct INIT_EVENT_CMD_RESULT *)
				prInitEvent->aucBuffer;

			/* 0 for download success */
			if (prEventCmdResult->ucStatus != 0) {
				DBGLOG(INIT, ERROR,
					"Start CMD failed, status[%u]\n",
				  prEventCmdResult->ucStatus);
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
				if (prEventCmdResult->ucStatus ==
				    WIFI_FW_DECOMPRESSION_FAILED)
					DBGLOG(INIT, ERROR,
						"Start Decompression CMD failed, status[%u]\n",
					  prEventCmdResult->ucStatus);
#endif
				u4Status = WLAN_STATUS_FAILURE;
				break;
			}
			u4Status = WLAN_STATUS_SUCCESS;
			break;
		}
	}

	kalMemFree(aucBuffer, PHY_MEM_TYPE, u4EventSize);

	return u4Status;
}

uint32_t wlanConfigWifiFunc(IN struct ADAPTER *prAdapter,
			    IN u_int8_t fgEnable, IN uint32_t u4StartAddress,
			    IN uint8_t ucPDA)
{
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	struct INIT_CMD_WIFI_START *prInitCmdWifiStart;
	uint8_t ucTC, ucCmdSeqNum;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	DEBUGFUNC("wlanConfigWifiFunc");

	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
					  sizeof(struct INIT_HIF_TX_HEADER) +
					  sizeof(
						struct INIT_CMD_WIFI_START));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	kalMemZero(prCmdInfo->pucInfoBuffer,
		   sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
			   struct INIT_CMD_WIFI_START));
	prCmdInfo->u2InfoBufLen = sizeof(struct INIT_HIF_TX_HEADER)
				  + sizeof(struct INIT_CMD_WIFI_START);

#if (CFG_USE_TC4_RESOURCE_FOR_INIT_CMD == 1)
	/* 2. Always use TC4 (TC4 as CPU) */
	ucTC = TC4_INDEX;
#else
	/* 2. Use TC0's resource to send init_cmd.
	 * Only TC0 is allowed because SDIO HW always reports
	 * CPU's TXQ_CNT at TXQ0_CNT in CR4 architecutre)
	 */
	ucTC = TC0_INDEX;
#endif

	/* 3. increase command sequence number */
	ucCmdSeqNum = nicIncreaseCmdSeqNum(prAdapter);

	/* 4. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_CMD;

	prInitHifTxHeader->rInitWifiCmd.ucCID =
		INIT_CMD_ID_WIFI_START;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = ucCmdSeqNum;

	prInitCmdWifiStart = (struct INIT_CMD_WIFI_START *) (
				     prInitHifTxHeader->rInitWifiCmd.aucBuffer);
	prInitCmdWifiStart->u4Override = 0;
	if (fgEnable)
		prInitCmdWifiStart->u4Override |=
			START_OVERRIDE_START_ADDRESS;

	/* 5G cal until send efuse buffer mode CMD */
#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)
	if (prAdapter->fgIsSupportDelayCal == TRUE)
		prInitCmdWifiStart->u4Override |= START_DELAY_CALIBRATION;
#endif

	if (ucPDA == PDA_CR4)
		prInitCmdWifiStart->u4Override |= START_WORKING_PDA_OPTION;

	prInitCmdWifiStart->u4Address = u4StartAddress;

	/* 5. Seend WIFI start command */
	while (1) {
		/* 5.1 Acquire TX Resource */
		if (nicTxAcquireResource(prAdapter, ucTC,
					 nicTxGetPageCount(prAdapter,
						prCmdInfo->u2InfoBufLen, TRUE),
					 TRUE) == WLAN_STATUS_RESOURCES) {
			if (nicTxPollingResource(prAdapter,
						 ucTC) != WLAN_STATUS_SUCCESS) {
				u4Status = WLAN_STATUS_FAILURE;
				DBGLOG(INIT, ERROR,
				       "Fail to get TX resource return within timeout\n");
				goto exit;
			}
			continue;
		}
		/* 5.2 Send CMD Info Packet */
		if (nicTxInitCmd(prAdapter, prCmdInfo,
				 prChipInfo->u2TxInitCmdPort)
					!= WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			DBGLOG(INIT, ERROR,
			       "Fail to transmit WIFI start command\n");
			goto exit;
		}

		break;
	};

	DBGLOG(INIT, INFO, "FW_START CMD send, waiting for RSP\n");

	u4Status = wlanConfigWifiFuncStatus(prAdapter, ucCmdSeqNum);

	if (u4Status != WLAN_STATUS_SUCCESS)
		DBGLOG(INIT, INFO, "FW_START EVT failed\n");
	else
		DBGLOG(INIT, INFO, "FW_START EVT success!!\n");

exit:
	/* 6. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
uint32_t
wlanCompressedFWConfigWifiFunc(IN struct ADAPTER *prAdapter,
	IN u_int8_t fgEnable,
	IN uint32_t u4StartAddress, IN uint8_t ucPDA,
	IN struct INIT_CMD_WIFI_DECOMPRESSION_START *prFwImageInFo)
{
	struct CMD_INFO *prCmdInfo;
	struct INIT_HIF_TX_HEADER *prInitHifTxHeader;
	struct INIT_CMD_WIFI_DECOMPRESSION_START
		*prInitCmdWifiStart;
	uint8_t ucTC, ucCmdSeqNum;
	uint32_t u4Status = WLAN_STATUS_SUCCESS;
	struct mt66xx_chip_info *prChipInfo;

	ASSERT(prAdapter);
	prChipInfo = prAdapter->chip_info;

	DEBUGFUNC("wlanConfigWifiFunc");
	/* 1. Allocate CMD Info Packet and its Buffer. */
	prCmdInfo = cmdBufAllocateCmdInfo(prAdapter,
		sizeof(struct INIT_HIF_TX_HEADER) +
		sizeof(struct INIT_CMD_WIFI_DECOMPRESSION_START));

	if (!prCmdInfo) {
		DBGLOG(INIT, ERROR, "Allocate CMD_INFO_T ==> FAILED.\n");
		return WLAN_STATUS_FAILURE;
	}

	kalMemZero(prCmdInfo->pucInfoBuffer,
		   sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
			   struct INIT_CMD_WIFI_DECOMPRESSION_START));
	prCmdInfo->u2InfoBufLen =
		sizeof(struct INIT_HIF_TX_HEADER) + sizeof(
			struct INIT_CMD_WIFI_DECOMPRESSION_START);

	/* 2. Always use TC0 */
	ucTC = TC0_INDEX;

	/* 3. increase command sequence number */
	ucCmdSeqNum = nicIncreaseCmdSeqNum(prAdapter);

	/* 4. Setup common CMD Info Packet */
	prInitHifTxHeader = (struct INIT_HIF_TX_HEADER *) (
				    prCmdInfo->pucInfoBuffer);
	prInitHifTxHeader->u2TxByteCount = prCmdInfo->u2InfoBufLen;
	prInitHifTxHeader->u2PQ_ID = INIT_CMD_PQ_ID;
	prInitHifTxHeader->ucPktFt = INIT_PKT_FT_CMD;
	prInitHifTxHeader->rInitWifiCmd.ucCID =
		INIT_CMD_ID_DECOMPRESSED_WIFI_START;
	prInitHifTxHeader->rInitWifiCmd.ucPktTypeID =
		INIT_CMD_PACKET_TYPE_ID;
	prInitHifTxHeader->rInitWifiCmd.ucSeqNum = ucCmdSeqNum;

	prInitCmdWifiStart = (struct
			      INIT_CMD_WIFI_DECOMPRESSION_START *) (
				     prInitHifTxHeader->rInitWifiCmd.aucBuffer);
	prInitCmdWifiStart->u4Override = 0;
	if (fgEnable)
		prInitCmdWifiStart->u4Override |=
			START_OVERRIDE_START_ADDRESS;

	/* 5G cal until send efuse buffer mode CMD */
#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)
	if (prAdapter->fgIsSupportDelayCal == TRUE)
		prInitCmdWifiStart->u4Override |= START_DELAY_CALIBRATION;
#endif
	if (ucPDA == PDA_CR4)
		prInitCmdWifiStart->u4Override |= START_WORKING_PDA_OPTION;

#if CFG_COMPRESSION_DEBUG
	prInitCmdWifiStart->u4Override |= START_CRC_CHECK;
#endif
#if CFG_DECOMPRESSION_TMP_ADDRESS
	prInitCmdWifiStart->u4Override |=
		CHANGE_DECOMPRESSION_TMP_ADDRESS;
	prInitCmdWifiStart->u4DecompressTmpAddress = 0xE6000;
#endif
	prInitCmdWifiStart->u4Address = u4StartAddress;
	prInitCmdWifiStart->u4Region1Address =
		prFwImageInFo->u4Region1Address;
	prInitCmdWifiStart->u4Region1CRC =
		prFwImageInFo->u4Region1CRC;
	prInitCmdWifiStart->u4BlockSize =
		prFwImageInFo->u4BlockSize;
	prInitCmdWifiStart->u4Region1length =
		prFwImageInFo->u4Region1length;
	prInitCmdWifiStart->u4Region2Address =
		prFwImageInFo->u4Region2Address;
	prInitCmdWifiStart->u4Region2CRC =
		prFwImageInFo->u4Region2CRC;
	prInitCmdWifiStart->u4Region2length =
		prFwImageInFo->u4Region2length;

	while (1) {
		/* 5.1 Acquire TX Resource */
		if (nicTxAcquireResource(prAdapter, ucTC,
					 nicTxGetPageCount(prAdapter,
						prCmdInfo->u2InfoBufLen, TRUE),
					 TRUE) == WLAN_STATUS_RESOURCES) {
			if (nicTxPollingResource(prAdapter,
						 ucTC) != WLAN_STATUS_SUCCESS) {
				u4Status = WLAN_STATUS_FAILURE;
				DBGLOG(INIT, ERROR,
				       "Fail to get TX resource return within timeout\n");
				break;
			}
			continue;

		}
		/* 5.2 Send CMD Info Packet */
		if (nicTxInitCmd(prAdapter, prCmdInfo,
				 prChipInfo->u2TxInitCmdPort)
				 != WLAN_STATUS_SUCCESS) {
			u4Status = WLAN_STATUS_FAILURE;
			DBGLOG(INIT, ERROR,
			       "Fail to transmit WIFI start command\n");
		}

		break;
	};

	DBGLOG(INIT, INFO, "FW_START CMD send, waiting for RSP\n");

	u4Status = wlanConfigWifiFuncStatus(prAdapter, ucCmdSeqNum);

	if (u4Status != WLAN_STATUS_SUCCESS)
		DBGLOG(INIT, INFO, "FW_START EVT failed\n");
	else
		DBGLOG(INIT, INFO, "FW_START EVT success!!\n");


	/* 6. Free CMD Info Packet. */
	cmdBufFreeCmdInfo(prAdapter, prCmdInfo);

	return u4Status;
}
#endif
#if 0
/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is used to generate CRC32 checksum
 *
 * @param buf Pointer to the data.
 * @param len data length
 *
 * @return crc32 value
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanCRC32(uint8_t *buf, uint32_t len)
{
	uint32_t i, crc32 = 0xFFFFFFFF;
	const uint32_t crc32_ccitt_table[256] = {
		0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
		0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
		0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
		0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
		0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
		0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
		0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
		0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
		0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
		0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
		0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
		0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
		0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
		0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
		0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
		0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
		0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
		0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
		0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
		0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
		0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
		0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
		0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
		0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
		0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
		0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
		0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
		0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
		0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
		0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
		0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
		0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
		0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
		0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
		0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
		0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
		0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
		0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
		0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
		0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
		0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
		0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
		0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
		0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
		0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
		0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
		0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
		0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
		0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
		0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
		0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
		0x2d02ef8d
	};

	for (i = 0; i < len; i++)
		crc32 = crc32_ccitt_table[(crc32 ^ buf[i]) & 0xff] ^
			(crc32 >> 8);

	return ~crc32;
}
#endif

uint32_t wlanGetHarvardTailerInfo(IN struct ADAPTER
	*prAdapter, IN void *prFwBuffer, IN uint32_t u4FwSize,
	IN uint32_t ucTotSecNum, IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	struct TAILER_FORMAT_T *prTailers;
	uint8_t *pucStartPtr;
	uint32_t u4SecIdx;
	uint8_t aucBuf[32];

	pucStartPtr = prFwBuffer + u4FwSize - sizeof(
			      struct TAILER_FORMAT_T) * ucTotSecNum;
	if (eDlIdx == IMG_DL_IDX_N9_FW) {
		kalMemCopy(&prAdapter->rVerInfo.rN9tailer, pucStartPtr,
			   sizeof(struct TAILER_FORMAT_T) * ucTotSecNum);
		prTailers = prAdapter->rVerInfo.rN9tailer;
	} else {
		kalMemCopy(&prAdapter->rVerInfo.rCR4tailer, pucStartPtr,
			   sizeof(struct TAILER_FORMAT_T) * ucTotSecNum);
		prTailers = prAdapter->rVerInfo.rCR4tailer;
	}

	for (u4SecIdx = 0; u4SecIdx < ucTotSecNum; u4SecIdx++) {
		/* Dump image information */
		DBGLOG(INIT, INFO,
		       "%s Section[%d]: chip_info[%u:E%u] feature[0x%02X]\n",
		       (eDlIdx == IMG_DL_IDX_N9_FW) ? "N9" : "CR4", u4SecIdx,
		       prTailers[u4SecIdx].chip_info,
		       prTailers[u4SecIdx].eco_code + 1,
		       prTailers[u4SecIdx].feature_set);

		kalMemZero(aucBuf, 32);
		kalStrnCpy(aucBuf, prTailers[u4SecIdx].ram_version,
			   sizeof(prTailers[u4SecIdx].ram_version));
		DBGLOG(INIT, INFO, "date[%s] version[%s]\n",
		       prTailers[u4SecIdx].ram_built_date, aucBuf);
	}

	return WLAN_STATUS_SUCCESS;
}

uint32_t wlanGetConnacTailerInfo(IN struct ADAPTER
	*prAdapter, IN void *prFwBuffer,
	IN uint32_t u4FwSize, IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	struct WIFI_VER_INFO *prVerInfo = &prAdapter->rVerInfo;
	struct TAILER_COMMON_FORMAT_T *prComTailer;
	struct TAILER_REGION_FORMAT_T *prRegTailer;
	uint8_t *pucImgPtr;
	uint8_t *pucTailertPtr;
	uint8_t *pucStartPtr;
	uint32_t u4SecIdx;
	uint8_t aucBuf[32];

	pucImgPtr = prFwBuffer;
	pucStartPtr = prFwBuffer + u4FwSize -
		sizeof(struct TAILER_COMMON_FORMAT_T);
	prComTailer = (struct TAILER_COMMON_FORMAT_T *) pucStartPtr;
	kalMemCopy(&prVerInfo->rCommonTailer, prComTailer,
		   sizeof(struct TAILER_COMMON_FORMAT_T));

	/* Dump image information */
	DBGLOG(INIT, INFO,
	       "%s INFO: chip_info[%u:E%u] region_num[%d]\n",
	       (eDlIdx == IMG_DL_IDX_N9_FW) ? "N9" : "CR4",
	       prComTailer->ucChipInfo,
	       prComTailer->ucEcoCode + 1, prComTailer->ucRegionNum);

	kalMemZero(aucBuf, 32);
	kalStrnCpy(aucBuf, prComTailer->aucRamVersion,
		   sizeof(prComTailer->aucRamVersion));
	DBGLOG(INIT, INFO, "date[%s] version[%s]\n",
	       prComTailer->aucRamBuiltDate, aucBuf);

	if (prComTailer->ucRegionNum > MAX_FWDL_SECTION_NUM) {
		DBGLOG(INIT, INFO,
		       "Regions number[%d] > max section number[%d]\n",
		       prComTailer->ucRegionNum, MAX_FWDL_SECTION_NUM);
		return WLAN_STATUS_FAILURE;
	}

	pucStartPtr -= (prComTailer->ucRegionNum *
			sizeof(struct TAILER_REGION_FORMAT_T));
	pucTailertPtr = pucStartPtr;
	for (u4SecIdx = 0; u4SecIdx < prComTailer->ucRegionNum; u4SecIdx++) {
		prRegTailer = (struct TAILER_REGION_FORMAT_T *) pucStartPtr;
		kalMemCopy(&prVerInfo->rRegionTailers[u4SecIdx],
			   prRegTailer, sizeof(struct TAILER_REGION_FORMAT_T));

		/* Dump image information */
		DBGLOG(INIT, TRACE,
		       "Region[%d]: addr[0x%08X] feature[0x%02X] size[%u]\n",
		       u4SecIdx, prRegTailer->u4Addr,
		       prRegTailer->ucFeatureSet, prRegTailer->u4Len);
		DBGLOG(INIT, TRACE,
		       "uncompress_crc[0x%08X] uncompress_size[0x%08X] block_size[0x%08X]\n",
		       prRegTailer->u4CRC, prRegTailer->u4RealSize,
		       prRegTailer->u4BlockSize);
		pucImgPtr += prRegTailer->u4Len;
		pucStartPtr += sizeof(struct TAILER_REGION_FORMAT_T);
	}

	if (prComTailer->ucFormatFlag && pucImgPtr < pucTailertPtr)
		fwDlGetReleaseInfoSection(prAdapter, pucImgPtr);

	return WLAN_STATUS_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/*!
 * @brief This function is used to extract the wifi ram code start address
 *
 * @param prVerInfo      Pointer to the P_WIFI_VER_INFO_T structure.
 *
 * @return addr          The ram code entry address.
 *                       0: use firmware defaut setting
 *                       others: use it as the start address
 */
/*----------------------------------------------------------------------------*/

uint32_t wlanDetectRamEntry(IN struct WIFI_VER_INFO
			    *prVerInfo)
{
	uint32_t addr = 0;
	uint32_t u4SecIdx;
	struct TAILER_COMMON_FORMAT_T *prComTailer =
			&prVerInfo->rCommonTailer;
	struct TAILER_REGION_FORMAT_T *prRegTailer;

	for (u4SecIdx = 0; u4SecIdx < prComTailer->ucRegionNum;
	     u4SecIdx++) {
		prRegTailer = &(prVerInfo->rRegionTailers[u4SecIdx]);

		if (prRegTailer->ucFeatureSet &
		    DOWNLOAD_CONFIG_VALID_RAM_ENTRY) {
			addr = prRegTailer->u4Addr;
			break;
		}
	}

	return addr;
}

uint32_t wlanHarvardFormatDownload(IN struct ADAPTER
				   *prAdapter, IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	uint32_t u4FwSize = 0;
	void *prFwBuffer = NULL;
	uint32_t rDlStatus = 0;
	uint32_t rCfgStatus = 0;
	uint32_t ucTotSecNum;
	uint8_t ucPDA;
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
	u_int8_t fgIsCompressed = FALSE;
	struct INIT_CMD_WIFI_DECOMPRESSION_START rFwImageInFo;
#endif

	if (eDlIdx == IMG_DL_IDX_N9_FW) {
		ucTotSecNum = N9_FWDL_SECTION_NUM;
		ucPDA = PDA_N9;
	} else {
		ucTotSecNum = CR4_FWDL_SECTION_NUM;
		ucPDA = PDA_CR4;
	}

	kalFirmwareImageMapping(prAdapter->prGlueInfo, &prFwBuffer,
				&u4FwSize, eDlIdx);
	if (prFwBuffer == NULL) {
		DBGLOG(INIT, WARN, "FW[%u] load error!\n", eDlIdx);
		return WLAN_STATUS_FAILURE;
	}

	wlanGetHarvardTailerInfo(prAdapter, prFwBuffer, u4FwSize,
				 ucTotSecNum, eDlIdx);
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
	rDlStatus = wlanCompressedImageSectionDownloadStage(
			    prAdapter, prFwBuffer, u4FwSize, ucTotSecNum,
			    eDlIdx, &fgIsCompressed, &rFwImageInFo);
	if (eDlIdx == IMG_DL_IDX_CR4_FW)
		prAdapter->fgIsCr4FwDownloaded = TRUE;
	if (fgIsCompressed == TRUE)
		rCfgStatus = wlanCompressedFWConfigWifiFunc(prAdapter,
				FALSE, 0, ucPDA, &rFwImageInFo);
	else
		rCfgStatus = wlanConfigWifiFunc(prAdapter, FALSE, 0, ucPDA);
#else
	rDlStatus = wlanImageSectionDownloadStage(prAdapter,
			prFwBuffer, u4FwSize, ucTotSecNum, eDlIdx);
	if (eDlIdx == IMG_DL_IDX_CR4_FW)
		prAdapter->fgIsCr4FwDownloaded = TRUE;
	rCfgStatus = wlanConfigWifiFunc(prAdapter, FALSE, 0, ucPDA);
#endif
	kalFirmwareImageUnmapping(prAdapter->prGlueInfo, NULL,
				  prFwBuffer);

	if ((rDlStatus != WLAN_STATUS_SUCCESS)
	    || (rCfgStatus != WLAN_STATUS_SUCCESS))
		return WLAN_STATUS_FAILURE;

	return WLAN_STATUS_SUCCESS;
}

uint32_t wlanConnacFormatDownload(IN struct ADAPTER
				  *prAdapter, IN enum ENUM_IMG_DL_IDX_T eDlIdx)
{
	void *prFwBuffer = NULL;
	uint32_t u4FwSize = 0;
	uint32_t ram_entry = 0;
	uint32_t rDlStatus = 0;
	uint32_t rCfgStatus = 0;
	uint8_t ucRegionNum;
	uint8_t ucPDA;

	kalFirmwareImageMapping(prAdapter->prGlueInfo, &prFwBuffer,
				&u4FwSize, eDlIdx);
	if (prFwBuffer == NULL) {
		DBGLOG(INIT, WARN, "FW[%u] load error!\n", eDlIdx);
		return WLAN_STATUS_FAILURE;
	}

	if (wlanGetConnacTailerInfo(prAdapter, prFwBuffer, u4FwSize,
				    eDlIdx) != WLAN_STATUS_SUCCESS) {
		DBGLOG(INIT, WARN, "Get tailer info error!\n");
		return WLAN_STATUS_FAILURE;
	}

	ucRegionNum = prAdapter->rVerInfo.rCommonTailer.ucRegionNum;
	ucPDA = (eDlIdx == IMG_DL_IDX_N9_FW) ? PDA_N9 : PDA_CR4;

	rDlStatus = wlanImageSectionDownloadStage(prAdapter,
			prFwBuffer, u4FwSize, ucRegionNum, eDlIdx);

	ram_entry = wlanDetectRamEntry(&prAdapter->rVerInfo);
	rCfgStatus = wlanConfigWifiFunc(prAdapter,
					(ram_entry == 0) ? FALSE : TRUE,
					ram_entry, ucPDA);

	kalFirmwareImageUnmapping(prAdapter->prGlueInfo, NULL,
				  prFwBuffer);

	if ((rDlStatus != WLAN_STATUS_SUCCESS)
	    || (rCfgStatus != WLAN_STATUS_SUCCESS))
		return WLAN_STATUS_FAILURE;

	return WLAN_STATUS_SUCCESS;
}

uint32_t wlanDownloadFW(IN struct ADAPTER *prAdapter)
{
	uint32_t rStatus = 0;
	u_int8_t fgReady;
	struct mt66xx_chip_info *prChipInfo;
	struct FWDL_OPS_T *prFwDlOps;

	if (!prAdapter)
		return WLAN_STATUS_FAILURE;

	prChipInfo = prAdapter->chip_info;
	prFwDlOps = prChipInfo->fw_dl_ops;

	DBGLOG(INIT, INFO,
	       "wlanDownloadFW:: Check ready_bits(=0x%x)\n",
	       prChipInfo->sw_ready_bits);
	HAL_WIFI_FUNC_READY_CHECK(prAdapter,
				  prChipInfo->sw_ready_bits, &fgReady);

	if (fgReady) {
		DBGLOG(INIT, INFO,
		       "Wi-Fi is already ON!, turn off before FW DL!\n");

		if (wlanPowerOffWifi(prAdapter) != WLAN_STATUS_SUCCESS)
			return WLAN_STATUS_FAILURE;

		nicpmWakeUpWiFi(prAdapter);
		HAL_HIF_INIT(prAdapter);
	}

	HAL_ENABLE_FWDL(prAdapter, TRUE);

	if (prFwDlOps->downloadPatch)
		prFwDlOps->downloadPatch(prAdapter);

	DBGLOG(INIT, INFO, "FW download Start\n");

	if (prFwDlOps->downloadFirmware) {
		rStatus = prFwDlOps->downloadFirmware(prAdapter,
						      IMG_DL_IDX_N9_FW);
		if (prChipInfo->is_support_cr4
		    && rStatus == WLAN_STATUS_SUCCESS)
			rStatus = prFwDlOps->downloadFirmware(prAdapter,
						IMG_DL_IDX_CR4_FW);
	} else
		DBGLOG(INIT, WARN, "Without downlaod firmware Ops\n");

	DBGLOG(INIT, INFO, "FW download End\n");

	HAL_ENABLE_FWDL(prAdapter, FALSE);

	return rStatus;
}

uint32_t wlanDownloadPatch(IN struct ADAPTER *prAdapter)
{
	uint32_t u4FwSize = 0;
	void *prFwBuffer = NULL;
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
	uint8_t ucIsCompressed;
#endif
	if (!prAdapter)
		return WLAN_STATUS_FAILURE;


	DBGLOG(INIT, INFO, "Patch download start\n");

	prAdapter->rVerInfo.fgPatchIsDlByDrv = FALSE;

	kalFirmwareImageMapping(prAdapter->prGlueInfo, &prFwBuffer,
				&u4FwSize, IMG_DL_IDX_PATCH);
	if (prFwBuffer == NULL) {
		DBGLOG(INIT, WARN, "FW[%u] load error!\n",
		       IMG_DL_IDX_PATCH);
		return WLAN_STATUS_FAILURE;
	}

	if (wlanPatchIsDownloaded(prAdapter)) {
		kalFirmwareImageUnmapping(prAdapter->prGlueInfo, NULL,
					  prFwBuffer);
		DBGLOG(INIT, INFO, "No need to download patch\n");
		return WLAN_STATUS_SUCCESS;
	}

	/* Patch DL */
	do {
#if CFG_SUPPORT_COMPRESSION_FW_OPTION
		wlanCompressedImageSectionDownloadStage(prAdapter,
							prFwBuffer, u4FwSize, 1,
							IMG_DL_IDX_PATCH,
							&ucIsCompressed, NULL);
#else
		wlanImageSectionDownloadStage(prAdapter, prFwBuffer,
					      u4FwSize, 1, IMG_DL_IDX_PATCH);
#endif
		wlanPatchSendComplete(prAdapter);
		kalFirmwareImageUnmapping(prAdapter->prGlueInfo, NULL,
					  prFwBuffer);

		prAdapter->rVerInfo.fgPatchIsDlByDrv = TRUE;
	} while (0);

	DBGLOG(INIT, INFO, "Patch download end\n");

	return WLAN_STATUS_SUCCESS;
}

uint32_t wlanGetPatchInfo(IN struct ADAPTER *prAdapter)
{
	uint32_t u4FwSize = 0;
	void *prFwBuffer = NULL;
	uint32_t u4StartOffset, u4Addr, u4Len, u4DataMode;

	if (!prAdapter)
		return WLAN_STATUS_FAILURE;

	kalFirmwareImageMapping(prAdapter->prGlueInfo, &prFwBuffer,
				&u4FwSize, IMG_DL_IDX_PATCH);
	if (prFwBuffer == NULL) {
		DBGLOG(INIT, WARN, "FW[%u] load error!\n",
		       IMG_DL_IDX_PATCH);
		return WLAN_STATUS_FAILURE;
	}

	wlanImageSectionGetPatchInfo(prAdapter, prFwBuffer,
				     u4FwSize, &u4StartOffset,
				     &u4Addr, &u4Len, &u4DataMode);

	kalFirmwareImageUnmapping(prAdapter->prGlueInfo, NULL,
				  prFwBuffer);

	return WLAN_STATUS_SUCCESS;
}

uint32_t fwDlGetFwdlInfo(struct ADAPTER *prAdapter,
			 char *pcBuf, int i4TotalLen)
{
	struct WIFI_VER_INFO *prVerInfo = &prAdapter->rVerInfo;
	struct FWDL_OPS_T *prFwDlOps;
	uint32_t u4Offset = 0;
	uint8_t aucBuf[32], aucDate[32];

	prFwDlOps = prAdapter->chip_info->fw_dl_ops;

	kalMemZero(aucBuf, 32);
	kalStrnCpy(aucBuf, prVerInfo->aucFwBranchInfo, 4);
	kalMemZero(aucDate, 32);
	kalStrnCpy(aucDate, prVerInfo->aucFwDateCode, 16);

	u4Offset += snprintf(pcBuf + u4Offset,
			i4TotalLen - u4Offset,
			"\nN9 FW version %s-%u.%u.%u[DEC] (%s)\n",
			aucBuf,
			(uint32_t)(prVerInfo->u2FwOwnVersion >> 8),
			(uint32_t)(prVerInfo->u2FwOwnVersion & BITS(0, 7)),
			prVerInfo->ucFwBuildNumber, aucDate);

	if (prFwDlOps->getFwDlInfo)
		u4Offset += prFwDlOps->getFwDlInfo(prAdapter,
						   pcBuf + u4Offset,
						   i4TotalLen - u4Offset);

	if (!prVerInfo->fgPatchIsDlByDrv) {
		u4Offset += snprintf(pcBuf + u4Offset,
				     i4TotalLen - u4Offset,
				     "MCU patch is not downloaded by wlan driver, read patch info\n");
		wlanGetPatchInfo(prAdapter);
	}

	kalMemZero(aucBuf, 32);
	kalMemZero(aucDate, 32);
	kalStrnCpy(aucBuf, prVerInfo->rPatchHeader.aucPlatform, 12);
	kalStrnCpy(aucDate, prVerInfo->rPatchHeader.aucBuildDate,
		   16);
	u4Offset += snprintf(pcBuf + u4Offset,
			     i4TotalLen - u4Offset,
			     "Patch platform %s. Date %s\n",
			     aucBuf, aucDate);

	u4Offset += snprintf(pcBuf + u4Offset, i4TotalLen - u4Offset,
			"Drv version %u.%u[DEC]\n",
			(uint32_t)(prVerInfo->u2FwPeerVersion >> 8),
			(uint32_t)(prVerInfo->u2FwPeerVersion & BITS(0, 7)));
	return u4Offset;
}

void fwDlGetReleaseInfoSection(struct ADAPTER *prAdapter, uint8_t *pucStartPtr)
{
	struct HEADER_RELEASE_INFO *prFirstInfo;
	struct HEADER_RELEASE_INFO *prRelInfo;
	uint8_t *pucCurPtr = pucStartPtr + RELEASE_INFO_SEPARATOR_LEN;
	uint16_t u2Len = 0, u2Offset = 0;

	prFirstInfo = (struct HEADER_RELEASE_INFO *)pucCurPtr;
	DBGLOG(INIT, INFO, "Release info tag[%u] len[%u]\n",
	       prFirstInfo->ucTag, prFirstInfo->u2Len);

	pucCurPtr += sizeof(struct HEADER_RELEASE_INFO);
	while (u2Offset < prFirstInfo->u2Len) {
		prRelInfo = (struct HEADER_RELEASE_INFO *)pucCurPtr;
		DBGLOG(INIT, INFO, "Release info tag[%u] len[%u] padding[%u]\n",
		       prRelInfo->ucTag, prRelInfo->u2Len,
		       prRelInfo->ucPaddingLen);

		pucCurPtr += sizeof(struct HEADER_RELEASE_INFO);
		switch (prRelInfo->ucTag) {
		case 0x01:
			fwDlGetReleaseManifest(prAdapter, prRelInfo, pucCurPtr);
			break;
		default:
			DBGLOG(INIT, WARN, "Not support release info tag[%u]\n",
			       prRelInfo->ucTag);
		}

		u2Len = prRelInfo->u2Len + prRelInfo->ucPaddingLen;
		pucCurPtr += u2Len;
		u2Offset += u2Len + sizeof(struct HEADER_RELEASE_INFO);
	}
}

void fwDlGetReleaseManifest(struct ADAPTER *prAdapter,
			    struct HEADER_RELEASE_INFO *prRelInfo,
			    uint8_t *pucStartPtr)
{
	kalMemZero(&prAdapter->rVerInfo.aucReleaseManifest,
		   sizeof(prAdapter->rVerInfo.aucReleaseManifest));
	kalMemCopy(&prAdapter->rVerInfo.aucReleaseManifest,
		   pucStartPtr, prRelInfo->u2Len);
	DBGLOG(INIT, INFO, "Release manifest: %s\n",
	       prAdapter->rVerInfo.aucReleaseManifest);
}

#endif  /* CFG_ENABLE_FW_DOWNLOAD */
