/**
  **********************************************************************************
  * @file    eflash_hal.c
  * @author  Product application department
  * @version V1.0
  * @date    2020.03.20
  * @brief   EfLASH模块HAL层驱动.
  * @note 这个文件为应用层提供了操作eflash的接口:
  *     - eflash初始化接口.
  *     - eflash擦除接口.
  *     - eflash编程接口.
  *     - eflash读数据接口.
  * 
  @verbatim
  ===================================================================================
                                  ##### 更新 #####
  ===================================================================================
  [..] 更新时间:
  [..] 更新人:
  [..] 更新内容:
        1. nothing.
  @endverbatim
  */

#include "eflash_hal.h"
#include "eflash_drv.h"

/*** 全局变量定义 ***************************************************************/
/** @
  * @{volatile
  */

 uint32_t  eflash_buffer[EFLASH_PAGE_SIZE_BYTE/4];//bt_demo的函数中使用
/** @ 
  * @}
  */
/** @ 
  * @{static
  */
/** @ 
  * @}
  */
/*** 常量定义 *******************************************************************/
/** @
  * @{const
  */
/** @ 
  * @}
  */

/*** 函数定义 *******************************************************************/

/**
 * @brief eflash模块初始化.
 * 
 * @param[in] hefm EFM句柄
 * @retval NONE 
 */
void HAL_EFLASH_Init(uint32_t efm_clk)
{
    DRV_EFM_Init(efm_clk/1000);
}
/**
 * @brief 设置eflash模块写权限
 * 
 * @param[in] hefm EFM句柄
 * @retval NONE 
 */
void HAL_EFLASH_SetWritePermission(void)
{
    DRV_EFLASH_SetWritePermission();
}

/**
 * @brief EFLASH解锁编写擦除，禁止EFLASH写功能.
 * 
 * @param[in] hefm EFM句柄
 * @retval NONE 
 */
void HAL_EFLASH_Lock(EFLASH_HandleTypeDef *hefm)
{
    DRV_EFM_MainCmd(hefm->instance,DISABLE);
}

/**
 * @brief EFLASH锁定编写擦，开启EFLASH写功能.
 * 
 * @param[in] hefm EFM句柄
 * @retval NONE 
 */
void HAL_EFLASH_Unlock(EFLASH_HandleTypeDef *hefm)
{
	 DRV_EFM_MainCmd(hefm->instance,ENABLE);
}

/**
 * @brief 在指定的EFLASH地址编写一个字.
 * 
 * @pre 调用该函数之前,必须成功调用 @ref HAL_EFLASH_PageErase 函数,擦除指定flash地址内容.
 * @param[in] Address 待编写的EFLASH地址.
 * @param[in] Data 待写入的数据.
 * @return
 * -0:成功
 * -1:失败
 */
uint8_t HAL_EFLASH_ProgramWord(uint32_t Address, uint32_t data)
{
	if(Address & 0x03)
	{
		return 1;
	}
  return DRV_EFM_ProgramWord(Address,data);
}

/**
 * @brief 页bulk编程.
 * @note 一个flash页的大小是512byte.
 * @pre 调用该函数之前,必须成功调用 @ref HAL_EFLASH_PageErase 函数,擦除指定flash地址内容.
 * @param[in] Address 待编写的EFLASH地址.
 * @param[in] pDate 待写入的数据缓冲指针.
 * @param[in] DataLength 待写入的数据长度.
 * @warning   DataLength只能是128的整数倍,因为一页大小是512byte.
 * @return
 * -0:成功
 * -1:失败
 */
uint8_t HAL_EFLASH_ProgramBulk(uint32_t Address, uint8_t *buffer, uint32_t length)
{
	if(Address & 0x7F)
	{
		return 1;
	}
  return DRV_EFM_ProgramBulk(Address,buffer,length);
}

/**
 * @brief 擦除一个EFLASH页.
 * @note 调用该函数,会擦除一整页flash内容.
 * @post 后续应该调用写接口函数.
 *      @ref HAL_EFLASH_ProgramWord
 *      @ref HAL_EFLASH_ProgramBulk
 * @param[in] Page_Address 待编写的EFLASH页地址.
 * @return
 * -0:成功
 * -1:失败
 */
uint8_t HAL_EFLASH_PageErase(uint32_t Address)
{
   if(Address & 0x1FF)
   {
	   return 1;
   }
   return DRV_EFM_EreasePage(Address);
}

/**
 * @brief 在指定的EFLASH地址读取数据.
 * 
 * @param[in] Address 待读取的EFLASH地址.
 * @param[in] length：读取数据的长度
 * @param[out] buffer 读取数据缓冲区
 *
 * @return NONE
 */
void HAL_EFLASH_Read(const uint32_t Address, uint8_t *buffer,uint32_t length)
{
    DRV_EFM_Read(Address,buffer,length);
}

/**
 * @brief 检测EFLASH地址合法性
 * 
 * @param[in] flash_addr 目标地址.
 * @param[in] len 目标长度
 * @param[out] -NONE
 * @return 
 *-TRUE:地址合法
 *-FALSE:地址非法
 */
static uint8_t FLASH_checkAddr(uint32_t flash_addr,uint32_t len)//xukai20200408,bt_demo调用的函数
{
    if((flash_addr%4) != 0)
    {
        return FALSE;
    }
    if((flash_addr < (EFCRADD+0x4000)) || (flash_addr+len) > (EFCRADD + EFLASH_BYTE_SIZE - 0x1000))
    {
        return FALSE;
    }
    return TRUE;
}

/**
 * @brief EFLASH写操作
 * 
 * @param[in] flash_addr : FLASH目标地址.
 * @param[in] ram_addr : SRAM源地址
 * @param[in] len : 源长度
 * @param[out] -NONE
 * @return 
 *-TRUE:写入成功
 *-FALSE:写入失败
 */
uint8_t HAL_EFLASH_Write(uint32_t flash_addr, const uint8_t* ram_addr, uint32_t len)//xukai20200408,bt_demo调用的函数
{
    uint16_t offset;
    uint16_t pageleft;
    uint16_t pageright;
    uint32_t dest = flash_addr;
    uint8_t* src  = (uint8_t*)ram_addr;
    uint8_t result;
    
    if (FLASH_checkAddr(flash_addr,len) == FALSE)
    {
        return FALSE;
    }
    
    offset  = ((uint32_t)dest & PAGE_MASK);
    pageleft  = EFLASH_PAGE_SIZE_BYTE - offset;
    pageright = ((uint16_t)(dest + len) & PAGE_MASK);
    
    /*如果页没有对齐，则先对第一页操作*/
    if (offset != 0)
    {
        dest -= offset;/*dest指针跳转到当前页头*/
        memcpy((uint8_t*)eflash_buffer, (uint8_t*)dest, EFLASH_PAGE_SIZE_BYTE);
        if (len>pageleft)
        {
            memcpy((uint8_t*)eflash_buffer + offset, src, pageleft);
            len  -= pageleft;
            src  += pageleft;
        }
        else
        {
            memcpy((uint8_t*)eflash_buffer + offset, src, len);
            len=0;
        }
        result = DRV_EFM_EreasePage(dest);
        result = DRV_EFM_ProgramBulk(dest, (uint8_t *)eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
        dest += EFLASH_PAGE_SIZE_BYTE;
        //offset = 0;
    }
    while (len > 0)
    {
        /*如果存在中间页*/
        if (len >= EFLASH_PAGE_SIZE_BYTE)
        {
            memcpy((uint8_t*)eflash_buffer, src, EFLASH_PAGE_SIZE_BYTE);
            result = DRV_EFM_EreasePage(dest);
            result = DRV_EFM_ProgramBulk(dest, (uint8_t *)eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
            
            dest += EFLASH_PAGE_SIZE_BYTE;
            src  += EFLASH_PAGE_SIZE_BYTE;
            len  -= EFLASH_PAGE_SIZE_BYTE;
        }
        else
        {   /*做最后页操作*/
            memcpy((uint8_t*)eflash_buffer, (uint8_t*)dest, EFLASH_PAGE_SIZE_BYTE);
            memcpy((uint8_t*)eflash_buffer, src, len);
            result = DRV_EFM_EreasePage(dest);
            result = DRV_EFM_ProgramBulk(dest, (uint8_t *)eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
            len = 0;
        }
    }
    
    if (result == 0)
        return TRUE;
    else
        return FALSE;
}

