
//flash 1.0V

/*********************************************************************
 * INCLUDES
 */
#include "bsp_flash.h"
#include "exception.h"
#include "system.h"

/*********************************************************************
 * LOCAL FUNCTIONS DEF
 */
static void fstorageCallbackFunc(nrf_fstorage_evt_t *pFstorageEvent);
static uint32_t getflashEndAddress(void);
static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle);

/*********************************************************************
 * LOCAL VARIABLES
 */
NRF_FSTORAGE_DEF(nrf_fstorage_t s_fstorageHandle) =
{
    /* Set a handler for fstorage events. */
    .evt_handler = fstorageCallbackFunc,

    /* These below are the boundaries of the flash space assigned to this instance of fstorage.
     * You must set these manually, even at runtime, before nrf_fstorage_init() is called.
     * The function nrf5_flash_end_addr_get() can be used to retrieve the last address on the
     * last page of flash available to write data. */
    .start_addr = START_FSTORAGE_ADDR,
    .end_addr   = END_FSTORAGE_ADDR,
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief Fstorage读写内存操作
 @param flashAddr -[in] 闪存地址
 @param readWriteFlag -[in] 读写操作标志
 @param pData -[in&out] 指向需要操作的数据
 @param dataLen -[in] 数据长度
 @return 错误代码
*/
static uint32_t Fstorage_FlashRW(uint32_t flashAddr, uint8_t readWriteFlag, uint32_t *pData, uint32_t dataLen)
{
	ret_code_t errCode;
	
	if(flashAddr%4 != 0 || flashAddr == 0)return FLASH_ERROR_ADDRALIGN;
	if((flashAddr + dataLen) > END_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;	//是否越界
	}
	
	if(readWriteFlag == FSTORAGE_READ)                       							// 读取数据
	{
		errCode = nrf_fstorage_read(&s_fstorageHandle, flashAddr, pData, dataLen);
		if(errCode != NRF_SUCCESS)return FLASH_ERROR_READ;
	}
	else 																				// 写入数据
	{
		if(dataLen %4 != 0 || dataLen == 0)return FLASH_ERROR_DATAALIGN;
		if( FLASH_ZONEADDR_CHECK(flashAddr) )return FLASH_ERROR_ZONEADDR;
		
		errCode = nrf_fstorage_erase(&s_fstorageHandle, flashAddr, 1, NULL);			// 只能写入位值1，不能写入位值0，所以先擦后写	
		if(errCode != NRF_SUCCESS)return FLASH_ERROR_ERASE;
		errCode = nrf_fstorage_write(&s_fstorageHandle, flashAddr, pData, dataLen, NULL);
		if(errCode != NRF_SUCCESS)return FLASH_ERROR_WRITE;
	
		waitForFlashReady(&s_fstorageHandle);											// 等待写完
	}
	
	return FLASH_OP_SUCCESS;
}

/**
@brief	Fstorage写内存操作，调用前FLASH预先已擦除
@param flashAddr -[in] 闪存地址
@param pData -[in&out] 指向需要操作的数据
@param dataLen -[in] 数据长度
@param is_check - [in] 是否检测数据存不存在
@return 0:写入成功  1:4字节不对齐  2:已有数据存在  3:访问越界
*/
static uint32_t Fstorage_FlashOnlyWrite(uint32_t flashAddr, uint32_t *pData, uint32_t dataLen, bool is_check)
{
	ret_code_t errCode;
	uint32_t check,i,len;
	
	if(flashAddr%4 != 0 || flashAddr == 0)return FLASH_ERROR_ADDRALIGN;
	if(dataLen %4 != 0 || dataLen == 0)return FLASH_ERROR_DATAALIGN;
	if((flashAddr + dataLen) > END_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;
	}
	
	if(is_check)
	{
		len = dataLen / 4;
		for(i=0;i<len;i++)
		{
			memcpy(&check, (uint32_t*)flashAddr + i, 4);
			if(check != 0xFFFFFFFF)return FLASH_ERROR_DATAEXIST;
		}
	}

	errCode = nrf_fstorage_write(&s_fstorageHandle, flashAddr, pData, dataLen, NULL);
	if(errCode != NRF_SUCCESS)return FLASH_ERROR_WRITE;
	
	waitForFlashReady(&s_fstorageHandle);											// 等待写完
	
	return FLASH_OP_SUCCESS;
}

/**
 @brief Fstorage擦除一整页
 @param 要擦除的页的首地址
 @return 错误代码
*/
static uint32_t Fstorage_FlashOnlyErasePage(uint32_t zone_addr)
{
	
	ret_code_t errCode;
	
	if(zone_addr%4 != 0 || zone_addr == 0)return FLASH_ERROR_ADDRALIGN;
	if( FLASH_ZONEADDR_CHECK(zone_addr) )return FLASH_ERROR_ZONEADDR;
	
	errCode = nrf_fstorage_erase(&s_fstorageHandle, zone_addr, 1, NULL);			// 只能写入位值1，不能写入位值0，所以先擦后写		
	if(errCode != NRF_SUCCESS)return FLASH_ERROR_ERASE;
	
	waitForFlashReady(&s_fstorageHandle);
	
	return FLASH_OP_SUCCESS;
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief Fstorage事件回调函数
 @param pFstorageEvent -[in] Fstorage事件
 @return 无
*/
static void fstorageCallbackFunc(nrf_fstorage_evt_t *pFstorageEvent)
{
    if(pFstorageEvent->result != NRF_SUCCESS)
    {
        SEGGER_RTT_printf(0,"--> Event received: ERROR while executing an fstorage operation.\n");
        return ;
    }

    switch(pFstorageEvent->id)
    {
	case NRF_FSTORAGE_EVT_WRITE_RESULT:
		//SEGGER_RTT_printf(0,"--> Event received: wrote %d bytes at address 0x%x.\n", pFstorageEvent->len, pFstorageEvent->addr);
		break;

	case NRF_FSTORAGE_EVT_ERASE_RESULT:
		//SEGGER_RTT_printf(0,"--> Event received: erased %d page from address 0x%x.\n", pFstorageEvent->len, pFstorageEvent->addr);
		break;

	default:
		break;
	}
}

/**
 @brief 检索Flash上可用于写入数据的地址
 @param 无
 @return 无
*/
static uint32_t getflashEndAddress(void)
{
	uint32_t const bootloaderAddr = NRF_UICR->NRFFW[0];
	uint32_t const pageSz         = NRF_FICR->CODEPAGESIZE;
	uint32_t const codeSz         = NRF_FICR->CODESIZE;

  return (bootloaderAddr != 0xFFFFFFFF ? bootloaderAddr : (codeSz * pageSz));
}

/**
 @brief 等待写入完成
 @param pFstorageHandle -[in] Fstorage句柄
 @return 无
*/
static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle)
{
    while(nrf_fstorage_is_busy(pFstorageHandle))										// While fstorage is busy, sleep and wait for an event.
    {
       nrf_pwr_mgmt_run();
    }
}

/**
 @brief 从flash中连续读取N个4字节数据
 @param addr-[in] 读取的首地址
 @param pData -[in&out] 指向需要操作的数据
 @param dataLen -[in] 数据长度
 @return 错误代码
*/
static uint32_t Read_N_4Byte_flash(uint32_t addr , uint32_t *pData, uint32_t dataLen)
{
	return Fstorage_FlashRW(addr, FSTORAGE_READ, pData, dataLen);
}

/**
 @brief 获取页号所在的首地址
 @param PageNum-[in] 需要查找的页号
 @param PageAddr-[in/out] 返回的页号首地址
 @return 错误代码
*/
static uint32_t GetPageAddr(uint16_t PageNum, uint32_t *PageAddr)
{
	if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM;
	
	*PageAddr = FLASH_ZONE_ADDR_1 + PageNum * FLASH_PAGE_SIZE;
	
	return FLASH_OP_SUCCESS;
}

/**
 @brief 获取页号所在的尾地址（该页号下一页的首地址）
 @param PageNum-[in] 需要查找的页号
 @param PageAddr-[in/out] 返回的页号首地址
 @return 错误代码
*/
static uint32_t GetPageEndAddr(uint16_t PageNum, uint32_t *PageAddr)
{
	if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM;
	
	*PageAddr = FLASH_ZONE_ADDR_1 + (PageNum+1) * FLASH_PAGE_SIZE;
	
	return FLASH_OP_SUCCESS;
}


/**
 @brief 获取地址距离所在页的偏移量
 @param addr-[in] 需要查找的flash地址
 @param _offset-[in/out] 返回的偏移量
 @return 错误代码
*/
static uint32_t GetDistanceFromPage(uint32_t addr, uint32_t *_offset)
{
	
	if(addr %4 != 0 || addr == 0)return FLASH_ERROR_ADDRALIGN;
	if(addr > END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;	//是否越界
	}
	
		//获取偏移量
	*_offset = ((addr - START_FSTORAGE_ADDR)%FLASH_PAGE_SIZE)/4;
	
	return FLASH_OP_SUCCESS;
}

/**
 @brief 判断是否需要擦除
 @param start_addr-[in] flash首地址
 @param len -[in] 长度
 @param is_erase-[in/out] 1：需要擦除，0：不需要擦除
 @return 错误代码
*/
static uint32_t __NeedErase(uint32_t start_addr , uint32_t len , uint8_t *is_erase)
{
	uint32_t err_code;
	uint32_t i = 0, data;
	
	if((start_addr + len) > END_FSTORAGE_ADDR)return FLASH_ERROR_ADDROVERBOUNDS;	
	if(start_addr%4 != 0 || start_addr == 0)return FLASH_ERROR_ADDRALIGN;
	if(len %4 != 0 || len == 0)return FLASH_ERROR_DATAALIGN;

	len /=4;
	for(;i<len;i++)
	{
		err_code = Fstorage_FlashRW(start_addr, FSTORAGE_READ, &data, 4);
		if(err_code != FLASH_OP_SUCCESS)return err_code;
		
		if(data != 0xFFFFFFFF)break;
		start_addr += 4;
	}
	
	*is_erase = (i==len)?0:1;
	
	return FLASH_OP_SUCCESS;
}

static void bsp_flash_init_process(void)
{
	if(Except_TxError(EXCEPT_FLASH_INIT,"bsp_flash_init_error\r\n") == 0)
	{
		Process_Stop(bsp_flash_init_process);
	}
}

/****************************************************接口****************************************************/

/**
 @brief Fstorage读写内存初始化
 @param 无
 @return 无
*/
void Fstorage_FlashInit(void)
{
	int 				ret = 0;
	ret_code_t 	errCode;

	nrf_fstorage_api_t *pFstorageApi;
	pFstorageApi = &nrf_fstorage_sd;
	
	errCode = nrf_fstorage_init(&s_fstorageHandle, pFstorageApi, NULL);					// Flash处理的开始地址和结束地址初始化
	if(errCode != NRF_SUCCESS)ret = -1;
	
	(void)getflashEndAddress();															// 获取地址，判断为可写地址大小
	
	if(ret == -1)
	{
		Process_Start(0,"bsp_flash_init_process",bsp_flash_init_process);
	}
}

/**
 @brief 从flash中连续读取N个字节数据
 @param addr-[in] 读取的首地址
 @param pData -[in&out] 指向需要操作的数据
 @param dataLen -[in] 数据长度
 @return 错误代码
*/
uint32_t Read_N_Byte_flash(uint32_t addr , uint32_t *pData, uint32_t dataLen)
{
	if(addr > END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;	//是否越界
	}
	
	memcpy(pData, (uint32_t*)addr, dataLen);
	
	return FLASH_OP_SUCCESS;
}

/**
 @brief 获取flash地址所在的页号
 @param addr-[in] 需要查找的flash地址
 @param page_num-[in/out] 返回的页号
 @return 错误代码
*/
uint32_t GetPageNum(uint32_t addr, uint16_t *PageNum)
{
	uint16_t page_num = 0;
	
	if(addr > END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;	//是否越界
	}
	
//	if(addr %4 != 0 || addr == 0)return FLASH_ERROR_ADDRALIGN;
	
	page_num = (addr - START_FSTORAGE_ADDR)/FLASH_PAGE_SIZE;
	if(page_num >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM;
	
	*PageNum = page_num;
	
	return FLASH_OP_SUCCESS;
}

/**
 @brief 擦除1页
 @param PageNum-[in] 页号
 @return 错误代码
*/
uint32_t Erase_OnePage(uint16_t PageNum)
{
	uint32_t PageAddr;
	uint32_t err_code;
	
	err_code = GetPageAddr(PageNum, &PageAddr);
	if(err_code != FLASH_OP_SUCCESS)return err_code;
	
	return Fstorage_FlashOnlyErasePage(PageAddr);
}

/**
 @brief 写N个字到flash中
 @param addr-[in] 写入的flash首地址
 @param pdata-[in] 需要写入的数据
 @param len-[in] 数据长度 
 @return 错误代码
*/
uint32_t Write_N_4Byte_flash(uint32_t addr , uint32_t *pdata, uint32_t len)
{
	uint8_t  is_erase;				//判断是否要擦除
	uint16_t start_page = 0;	//起始页号
	uint32_t page_size = 0;		//需要操作页的大小
	uint32_t _offset = 0;			//页操作页码的起始地址
	uint32_t page_offset = 0; //页偏移
	uint32_t data_offset = 0; //数据偏移
	uint32_t remain_size = 0;	//本次操作页可以使用的剩余大小
	uint32_t base_addr = 0;		//本次操作页的基地址
	uint32_t surplus = 0;			//总的剩余的字节
	uint32_t err_code;				//错误代码
	uint32_t flash_PageBuff[PAGE_INT_SIZE];	//页缓冲区
	
	//是否越界
	if((addr + len) > END_FSTORAGE_ADDR){
		return FLASH_ERROR_ADDROVERBOUNDS;	
	}
	//获取起始页号
	err_code = GetPageNum(addr, &start_page);
	if(err_code != FLASH_OP_SUCCESS)return err_code;
	//获取偏移量
	err_code = GetDistanceFromPage(addr,&_offset);
	if(err_code != FLASH_OP_SUCCESS)return err_code;
	//计算需要写入的页数
	if(((PAGE_INT_SIZE - _offset)*4) >= len)
	{
		page_size = 1;
		surplus = 0;
	}
	else
	{
		page_size = 1;
		surplus = len - ((PAGE_INT_SIZE - _offset)*4);
		page_size += ((surplus/FLASH_PAGE_SIZE) + (surplus%FLASH_PAGE_SIZE>0?1:0));
	}
	//每页写入
	surplus = len;
	for(page_offset = 0; page_offset < page_size; page_offset++)
	{
		
		//获取页首地址
		err_code = GetPageAddr(start_page + page_offset, &base_addr);
		if(err_code != FLASH_OP_SUCCESS)return err_code;
		
		if(page_offset == 0)
		{
			//第一次偏移根据目的地址来确定
			remain_size = (PAGE_INT_SIZE - _offset)*4<surplus ? (PAGE_INT_SIZE - _offset)*4:surplus;
			surplus = len - remain_size;
		}
		else
		{
			//后面操作的偏移都是从首地址开始
			_offset = 0;
			remain_size = surplus > FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : surplus;
			surplus = surplus - remain_size;
		}
		
		//判断是否要擦除
		err_code = __NeedErase(base_addr + _offset*4 , remain_size, &is_erase);
		if(err_code != FLASH_OP_SUCCESS)return err_code;
		
		
		//先擦除后写
		if(is_erase)
		{
			//读取
			err_code = Read_N_4Byte_flash(base_addr , flash_PageBuff, FLASH_PAGE_SIZE);
			if(err_code != FLASH_OP_SUCCESS)return err_code;
			//修改
			memcpy(&flash_PageBuff[_offset], pdata + data_offset, remain_size);
			//写回
			err_code = Fstorage_FlashRW(base_addr, FSTORAGE_WRITE, flash_PageBuff, FLASH_PAGE_SIZE);
			if(err_code != FLASH_OP_SUCCESS)return err_code;
		}
		else
		{
			//不需要擦除，直接写
			err_code = Fstorage_FlashOnlyWrite(base_addr + _offset*4, pdata + data_offset, remain_size, false);	//直接写
			if(err_code != FLASH_OP_SUCCESS)return err_code;
		}

		data_offset += remain_size/4;			//除以4是因为pData指针+1是移动4个字节
		
	}
	
	return FLASH_OP_SUCCESS;
	
}

/**
 @brief 只写N个字到flash中，调用前FLASH预先已擦除
 @param addr-[in] 写入的flash首地址
 @param pdata-[in] 需要写入的数据
 @param len-[in] 数据长度 
 @return 错误代码
*/
uint32_t Only_Write_N_4Byte_flash(uint32_t addr , uint32_t *pdata, uint32_t len, bool is_check)
{
	return Fstorage_FlashOnlyWrite(addr, pdata, len, is_check);
}

/**
 @brief 查找一页中可写的空间（0xFF）
 @param PageNum-[in] 输入的页号
 @param start_addr-[in/out] 返回的空闲空间的起始地址
 @param len-[in/out] 返回的空间的长度
 @return 错误代码
*/
uint32_t Find_FreeSpace_In_Page(uint16_t PageNum, uint32_t *start_addr, uint32_t *len)
{
	uint32_t PageEndAddr,  RetrieveAddr, i;
	uint32_t flash_data;
	
	if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM;
	
	//获取该页的结尾地址
	GetPageEndAddr(PageNum, &PageEndAddr);
	
	//从页尾开始检索空闲空间
	RetrieveAddr = PageEndAddr;
	for(i = 0;i<PAGE_INT_SIZE;i++)
	{
		RetrieveAddr -= 4;
		Read_N_4Byte_flash(RetrieveAddr , &flash_data, 4);
		
		if(flash_data != 0xFFFFFFFF)break;
		
	}
	*start_addr = PageEndAddr - i *4;
	*len        = i*4;
	
	return FLASH_OP_SUCCESS;
}



/****************************************************END OF FILE****************************************************/
