#include "stdio.h"
#include "string.h"
#include "cmsis.h"
#ifdef RTOS
#include "cmsis_os.h"
#else
#include "hal_timer.h"
#endif
#include "pmu.h"
#include "hal_sleep.h"
#include "hal_trace.h"
#include "hal_norflash.h"
#include "norflash_api.h"

#if 0
#define NORFLASH_API_TRACE(str,...) TRACE(str, ##__VA_ARGS__)
#else
#define NORFLASH_API_TRACE(str,...)
#endif

#define API_IS_ALIGN(v,size)  (((v/size)*size) == v)

static NORFLASH_API_INFO norflash_api_info = {false,};
static OPERA_INFO_LIST opera_info_list[NORFLASH_API_OPRA_LIST_LEN];
static DATA_LIST      data_list[NORFLASH_API_WRITE_BUFF_LEN];
static int suspend_number = 0;

static void* _norflash_api_malloc(uint32_t size)
{
    uint32_t i;

    if(size == sizeof(OPRA_INFO))
    {
        for(i = 0; i < NORFLASH_API_OPRA_LIST_LEN; i++)
        {
            if(opera_info_list[i].is_used == false)
            {
                opera_info_list[i].is_used = true;
                return (void*)&opera_info_list[i].opera_info;
            }
        }
        return NULL;
    }
    else if(size == NORFLASH_API_SECTOR_SIZE)
    {
        for(i = 0; i < NORFLASH_API_WRITE_BUFF_LEN; i++)
        {
            if(data_list[i].is_used == false)
            {
                data_list[i].is_used = true;
                return (void*)data_list[i].buffer;
            }
        }
        return NULL;
    }
    else
    {
        ASSERT(0,"%s: size(0x%x) error!", __func__, size);
    }
}

static void _norflash_api_free(void *p)
{
    uint32_t i;

    for(i = 0; i < NORFLASH_API_OPRA_LIST_LEN; i++)
    {
        if((uint8_t*)&opera_info_list[i].opera_info == p)
        {
            opera_info_list[i].is_used = false;
            return;
        }
    }

    for(i = 0; i < NORFLASH_API_WRITE_BUFF_LEN; i++)
    {
        if(data_list[i].buffer == p)
        {
            data_list[i].is_used = false;
            return;
        }
    }

    ASSERT(0,"%s: p(%p) error!", __func__, p);
}

static MODULE_INFO* _get_module_info(enum NORFLASH_API_MODULE_ID_T mod_id)
{
    return &norflash_api_info.mod_info[mod_id];
}

static OPRA_INFO* _get_tail(MODULE_INFO *mod_info,bool is_remove)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *pre_node = NULL;
    OPRA_INFO *tmp;

    pre_node = mod_info->opera_info;
    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        tmp = opera_node->next;
        if(tmp)
        {
            pre_node = opera_node;
        }
    }
    if(is_remove)
    {
        if(pre_node)
        {
            pre_node->next = NULL;
        }
    }
    if(opera_node)
    {
        opera_node->lock = true;
    }
    return opera_node;
}

static void _opera_del(MODULE_INFO *mod_info,OPRA_INFO *node)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *pre_node = NULL;
    OPRA_INFO *tmp;

    pre_node = mod_info->opera_info;
    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        if(opera_node == node)
        {
            if(mod_info->opera_info == opera_node)
            {
                mod_info->opera_info = NULL;
            }
            else
            {
                pre_node->next = NULL;
            }
            if(node->buff)
            {
                _norflash_api_free(node->buff);
            }
            _norflash_api_free(node);
            break;
        }
        tmp = opera_node->next;
        if(tmp)
        {
            pre_node = opera_node;
        }
    }
}

static uint32_t _get_ew_count(MODULE_INFO *mod_info)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *tmp;
    uint32_t count = 0;

    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        count ++;
        tmp = opera_node->next;
    }
    return count;
}

static uint32_t _get_w_count(MODULE_INFO *mod_info)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *tmp;
    uint32_t count = 0;

    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        if(opera_node->type == NORFLASH_API_WRITTING)
        {
            count ++;
        }
        tmp = opera_node->next;
    }
    return count;
}

static uint32_t _get_e_count(MODULE_INFO *mod_info)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *tmp;
    uint32_t count = 0;

    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        if(opera_node->type == NORFLASH_API_ERASING)
        {
            count ++;
        }
        tmp = opera_node->next;
    }
    return count;
}

static MODULE_INFO* _get_cur_mod(void)
{
    uint32_t i;
    MODULE_INFO *mod_info = NULL;
    uint32_t tmp_mod_id = NORFLASH_API_MODULE_ID_COUNT;

    if(norflash_api_info.cur_mod)
    {
        return norflash_api_info.cur_mod;
    }

    tmp_mod_id = norflash_api_info.cur_mod_id;
    for(i = 0; i < NORFLASH_API_MODULE_ID_COUNT; i++)
    {
        tmp_mod_id =  tmp_mod_id + 1 >= NORFLASH_API_MODULE_ID_COUNT ? 0 : tmp_mod_id + 1;
        mod_info = _get_module_info((enum NORFLASH_API_MODULE_ID_T)tmp_mod_id);
        if(mod_info->is_inited)
        {
            if(_get_ew_count(mod_info) > 0)
            {
                return mod_info;
            }
        }
    }
    return NULL;
}

static enum NORFLASH_API_MODULE_ID_T _get_mod_id(MODULE_INFO *mod_info)
{
    uint32_t i;
    enum NORFLASH_API_MODULE_ID_T mod_id = NORFLASH_API_MODULE_ID_COUNT;
    MODULE_INFO *tmp_mod_info = NULL;

    for(i = 0; i < NORFLASH_API_MODULE_ID_COUNT; i++)
    {
        tmp_mod_info = _get_module_info((enum NORFLASH_API_MODULE_ID_T)i);
        if(tmp_mod_info == mod_info)
        {
            mod_id = (enum NORFLASH_API_MODULE_ID_T)i;
            break;
        }
    }
    return mod_id;
}

#ifdef FLASH_REMAP
static void _flash_remap_start(enum HAL_NORFLASH_ID_T id,uint32_t addr, uint32_t len)
{
    uint32_t remap_addr;
    uint32_t remap_len;
    uint32_t remap_offset;
    enum HAL_NORFLASH_RET_T ret;

    remap_addr = OTA_CODE_OFFSET;
    remap_len = OTA_REMAP_OFFSET - OTA_CODE_OFFSET;
    remap_offset = OTA_REMAP_OFFSET;

    // NORFLASH_API_TRACE(3,"%s: id = %d,addr = 0x%x,len = 0x%x.", __func__,id,addr,len);
    if((addr & 0x3ffffff) + len <= (remap_addr & 0x3ffffff) ||
       (addr & 0x3ffffff) >= (remap_addr & 0x3ffffff) + remap_len)
    {
        // NORFLASH_API_TRACE(3,"%s: Not in the remap area.",__func__);
        return;
    }

    if(((addr & 0x3ffffff)  < (remap_addr & 0x3ffffff) &&
       (addr & 0x3ffffff) + len > (remap_addr & 0x3ffffff)) ||
       ((addr & 0x3ffffff)  < (remap_addr & 0x3ffffff) + remap_len  &&
       (addr & 0x3ffffff) + len > (remap_addr & 0x3ffffff) + remap_len))
    {
        ASSERT(0,"%s: Address ranges bad!addr = 0x%x, len=0x%x,remap_addr=0x%x,remap_len=0x%x",
            __func__, addr, len, remap_addr, remap_len);
    }

    if(!hal_norflash_get_remap_status(id))
    {
        // NORFLASH_API_TRACE(3,"%s: Unremap to enable remap.",__func__);
        ret = hal_norflash_enable_remap(id, remap_addr, remap_len, remap_offset);
        ASSERT(ret == HAL_NORFLASH_OK, "%s: Failed to enable remap(0x%x,0x%x,0x%x), ret = %d",
            __func__, remap_addr, remap_len, remap_offset, ret);
    }
    else
    {
        // NORFLASH_API_TRACE(3,"%s: Remaped to disable remap.",__func__);
        ret = hal_norflash_disable_remap(id);
        ASSERT(ret == HAL_NORFLASH_OK, "%s: Failed to disable remap, ret = %d",
            __func__, ret);
    }
}

static void _flash_remap_done(enum HAL_NORFLASH_ID_T id,uint32_t addr, uint32_t len)
{
    uint32_t remap_addr;
    uint32_t remap_len;
    uint32_t remap_offset;
    enum HAL_NORFLASH_RET_T ret;

    remap_addr = OTA_CODE_OFFSET;
    remap_len = OTA_REMAP_OFFSET - OTA_CODE_OFFSET;
    remap_offset = OTA_REMAP_OFFSET;

    // NORFLASH_API_TRACE(3, "%s: id = %d,addr = 0x%x,len = 0x%x.", __func__,id,addr,len);
    if((addr & 0x3ffffff) + len <= (remap_addr & 0x3ffffff) ||
       (addr & 0x3ffffff) >= (remap_addr & 0x3ffffff) + remap_len)
    {
        // NORFLASH_API_TRACE(3,"%s: Not in the remap area.",__func__);
        return;
    }

    if(((addr & 0x3ffffff)  < (remap_addr & 0x3ffffff) &&
       (addr & 0x3ffffff) + len > (remap_addr & 0x3ffffff)) ||
       ((addr & 0x3ffffff)  < (remap_addr & 0x3ffffff) + remap_len  &&
       (addr & 0x3ffffff) + len > (remap_addr & 0x3ffffff) + remap_len))
    {
        ASSERT(0,"%s: Address ranges bad!addr = 0x%x, len=0x%x,remap_addr=0x%x,remap_len=0x%x",
            __func__, addr, len, remap_addr, remap_len);
    }

    if(!hal_norflash_get_remap_status(id))
    {
        // NORFLASH_API_TRACE(3, "%s: Unremap to enable remap.",__func__);
        ret = hal_norflash_enable_remap(id, remap_addr, remap_len, remap_offset);
        ASSERT(ret == HAL_NORFLASH_OK, "%s: Failed to enable remap(0x%x,0x%x,0x%x), ret = %d",
            __func__, remap_addr, remap_len, remap_offset, ret);
    }
    else
    {
        // NORFLASH_API_TRACE(3, "%s: Remaped to disable remap.",__func__);
        ret = hal_norflash_disable_remap(id);
        ASSERT(ret == HAL_NORFLASH_OK, "%s: Failed to disable remap, ret = %d",
            __func__, ret);
    }
}


#define FLASH_REMAP_START _flash_remap_start
#define FLASH_REMAP_DONE _flash_remap_done
#else
#define FLASH_REMAP_START(...)
#define FLASH_REMAP_DONE(...)
#endif

static int32_t _opera_read(MODULE_INFO *mod_info,
               uint32_t addr,
               uint8_t *buff,
               uint32_t len)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *e_node = NULL;
    OPRA_INFO *w_node = NULL;
    OPRA_INFO *tmp;
    uint32_t r_offs;
    uint32_t sec_start;
    uint32_t sec_len;

    sec_len = mod_info->mod_sector_len;
    sec_start = (addr/sec_len)*sec_len;
    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        tmp = opera_node->next;
        if(opera_node->addr == sec_start)
        {
            if(opera_node->type == NORFLASH_API_WRITTING)
            {
                w_node = opera_node;
                break;
            }
            else
            {
                e_node = opera_node;
                break;
            }
        }
    }

    if(w_node)
    {
        r_offs = addr - sec_start;
        memcpy(buff,w_node->buff + r_offs,len);
    }
    else
    {
        if(e_node)
        {
            memset(buff,0xff,len);
        }
        else
        {
            FLASH_REMAP_START(mod_info->dev_id,addr,len);
            memcpy(buff,(uint8_t*)addr,len);
            FLASH_REMAP_DONE(mod_info->dev_id,addr,len);
            /*
            HAL_NORFLASH_RET_T result;
            result = hal_norflash_read(mod_info->dev_id,addr,buff,len);
            if(result != HAL_NORFLASH_OK)
            {
                NORFLASH_API_TRACE(2,"%s: hal_norflash_read failed,result = %d.",
                        __func__,result);
                return result;
            }
            */
        }
    }
    return 0;
}

static int32_t _e_opera_add(MODULE_INFO *mod_info,
               uint32_t addr,
               uint32_t len
               )
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *pre_node = NULL;
    OPRA_INFO *tmp;
    int32_t ret = 0;

    // delete opera nodes with the same address when add the erase opera node.
    pre_node = mod_info->opera_info;
    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        tmp = opera_node->next;

        if(opera_node->addr == addr)
        {
            if(opera_node->lock == false)
            {
                if(opera_node == mod_info->opera_info)
                {
                    mod_info->opera_info = tmp;
                }
                else
                {
                    pre_node->next = tmp;
                }
                if(opera_node->type == NORFLASH_API_WRITTING)
                {
                    if(opera_node->buff)
                    {
                        _norflash_api_free(opera_node->buff);
                    }
                }
                _norflash_api_free(opera_node);
            }
            else
            {
                if(opera_node->type == NORFLASH_API_ERASING)
                {
                    NORFLASH_API_TRACE(3,"%s: erase is merged! addr = 0x%x,len = 0x%x.",
                        __func__,
                        opera_node->addr,
                        opera_node->len);
                    ret = 0;
                    goto _func_end;
                }
            }
        }

        if(tmp)
        {
            pre_node = opera_node;
        }
    }

    // add new node to header.
    opera_node = (OPRA_INFO*)_norflash_api_malloc(sizeof(OPRA_INFO));
    if(opera_node == NULL)
    {
        NORFLASH_API_TRACE(3,"%s:%d,_norflash_api_malloc failed! size = %d.",
                __func__,__LINE__,sizeof(OPRA_INFO));
        ret = 1;
        goto _func_end;
    }
    opera_node->type = NORFLASH_API_ERASING;
    opera_node->addr = addr;
    opera_node->len = len;
    opera_node->w_offs = 0;
    opera_node->w_len = 0;
    opera_node->buff = NULL;
    opera_node->lock = false;
    opera_node->next = mod_info->opera_info;
    mod_info->opera_info = opera_node;
    ret = 0;
_func_end:

    return ret;
}

static int32_t _w_opera_add(MODULE_INFO *mod_info,
               uint32_t addr,
               uint32_t len,
               uint8_t *buff)
{
    OPRA_INFO *opera_node = NULL;
    OPRA_INFO *e_node = NULL;
    OPRA_INFO *w_node = NULL;
    OPRA_INFO *tmp;
    uint32_t w_offs;
    uint32_t w_len;
    uint32_t sec_start;
    uint32_t sec_len;
    uint32_t w_end1;
    uint32_t w_end2;
    uint32_t w_start;
    uint32_t w_end;
    uint32_t w_len_new;
    int32_t ret = 0;

    sec_len = mod_info->mod_sector_len;
    sec_start = (addr/sec_len)*sec_len;
    w_offs = addr - sec_start;
    w_len = len;
    tmp = mod_info->opera_info;
    while(tmp)
    {
        opera_node = tmp;
        tmp = opera_node->next;

        if(opera_node->addr == sec_start)
        {
            if(opera_node->type == NORFLASH_API_WRITTING)
            {
                if(!opera_node->lock)
                {
                    // select the first w_node in the list.
                    w_node = opera_node;
                    break;
                }
            }
            else
            {
                e_node = opera_node;
                break;
            }
        }
    }

    if(w_node)
    {
        memcpy(w_node->buff + w_offs,buff,w_len);
        w_start = w_node->w_offs <= w_offs ? w_node->w_offs:w_offs;
        w_end1 = w_node->w_offs + w_node->w_len;
        w_end2 = w_offs + w_len;
        w_end = w_end1 >= w_end2 ? w_end1 : w_end2;
        w_len_new = w_end - w_start;
        w_node->w_offs = w_start;
        w_node->w_len = w_len_new;
        opera_node = w_node;
        ret = 0;
    }
    else
    {
        opera_node = (OPRA_INFO*)_norflash_api_malloc(sizeof(OPRA_INFO));
        if(opera_node == NULL)
        {
            NORFLASH_API_TRACE(3,"%s:%d,_norflash_api_malloc failed! size = %d.",
                    __func__,__LINE__,sizeof(OPRA_INFO));
             ret = 1;
             goto _func_end;
        }
        opera_node->type = NORFLASH_API_WRITTING;
        opera_node->addr = sec_start;
        opera_node->len = sec_len;
        opera_node->w_offs = w_offs;
        opera_node->w_len = w_len;
        opera_node->buff = (uint8_t*)_norflash_api_malloc(opera_node->len);
        if(opera_node->buff == NULL)
        {
            _norflash_api_free(opera_node);
            NORFLASH_API_TRACE(3,"%s:%d,_norflash_api_malloc failed! size = %d.",
                    __func__,__LINE__,opera_node->len);
             ret = 1;
             goto _func_end;
        }
        if(e_node)
        {
            memset(opera_node->buff,0xff,opera_node->len);
        }
        else
        {
            memcpy(opera_node->buff,(uint8_t*)opera_node->addr,opera_node->len);
        }
        memcpy(opera_node->buff + w_offs,buff,w_len);
        opera_node->lock = false;
        opera_node->next = mod_info->opera_info;
        mod_info->opera_info = opera_node;
        ret = 0;
    }

_func_end:
    return ret;
}

bool _opera_flush(MODULE_INFO *mod_info,bool nosuspend)
{
    OPRA_INFO *cur_opera_info = NULL;
    enum HAL_NORFLASH_RET_T result;
    bool opera_is_completed = false;
    NORFLASH_API_OPERA_RESULT opera_result;
    bool ret = false;
    bool suspend;

#if defined(FLASH_SUSPEND)
    suspend = true;
#else
    suspend = false;
#endif
    suspend = nosuspend == true ? false: suspend;

    if(!mod_info->cur_opera_info)
    {
        mod_info->cur_opera_info = _get_tail(mod_info, false);
    }

    if(!mod_info->cur_opera_info)
    {
        return false;
    }

    ret = true;
    cur_opera_info = mod_info->cur_opera_info;
    if(cur_opera_info->type == NORFLASH_API_WRITTING)
    {
        if(mod_info->state == NORFLASH_API_STATE_IDLE)
        {
            suspend_number = 0;
            if(cur_opera_info->w_len > 0)
            {
                NORFLASH_API_TRACE(5,"%s: %d,hal_norflash_write_suspend,addr = 0x%x,len = 0x%x,suspend = %d.",
                                __func__,__LINE__,
                                cur_opera_info->addr + cur_opera_info->w_offs,
                                cur_opera_info->w_len,
                                suspend);

                FLASH_REMAP_START(mod_info->dev_id,cur_opera_info->addr + cur_opera_info->w_offs,cur_opera_info->w_len);
                pmu_flash_write_config();
                result = hal_norflash_write_suspend(mod_info->dev_id,
                             cur_opera_info->addr + cur_opera_info->w_offs,
                             cur_opera_info->buff + cur_opera_info->w_offs,
                             cur_opera_info->w_len,
                             suspend);
                pmu_flash_read_config();
                FLASH_REMAP_DONE(mod_info->dev_id,cur_opera_info->addr + cur_opera_info->w_offs,cur_opera_info->w_len);
            }
            else
            {
                result = HAL_NORFLASH_OK;
            }

            if(result == HAL_NORFLASH_OK)
            {
                opera_is_completed = true;
                goto __opera_is_completed;
            }
            else if(result == HAL_NORFLASH_SUSPENDED)
            {
                mod_info->state = NORFLASH_API_STATE_WRITTING_SUSPEND;
            }
            else
            {
                ASSERT(0, "%s: %d, hal_norflash_write_suspend failed,result = %d",__func__,__LINE__,result);
            }
        }
        else if(mod_info->state == NORFLASH_API_STATE_WRITTING_SUSPEND)
        {
            suspend_number ++;
            FLASH_REMAP_START(mod_info->dev_id,cur_opera_info->addr + cur_opera_info->w_offs,cur_opera_info->w_len);
            pmu_flash_write_config();
            result = hal_norflash_write_resume(mod_info->dev_id,
                         suspend);
            pmu_flash_read_config();
            FLASH_REMAP_DONE(mod_info->dev_id,cur_opera_info->addr + cur_opera_info->w_offs,cur_opera_info->w_len);
            if(result == HAL_NORFLASH_OK)
            {
                opera_is_completed = true;
                goto __opera_is_completed;
            }
            else if(result == HAL_NORFLASH_SUSPENDED)
            {
                mod_info->state = NORFLASH_API_STATE_WRITTING_SUSPEND;
            }
            else
            {
                ASSERT(0, "%s: %d, hal_norflash_write_resume failed,result = %d",__func__,__LINE__,result);
            }
        }
        else
        {
            ASSERT(0, "%s: %d, mod_info->state error,state = %d",__func__,__LINE__,mod_info->state);
        }
    }
    else
    {
        if(mod_info->state == NORFLASH_API_STATE_IDLE)
        {
            suspend_number = 0;
            NORFLASH_API_TRACE(5,"%s: %d,hal_norflash_erase_suspend,addr = 0x%x,len = 0x%x,suspend = %d.",
                                __func__,__LINE__,
                                cur_opera_info->addr,
                                cur_opera_info->len,
                                suspend);
            FLASH_REMAP_START(mod_info->dev_id, cur_opera_info->addr, cur_opera_info->len);
            pmu_flash_write_config();
            result = hal_norflash_erase_suspend(mod_info->dev_id,
                         cur_opera_info->addr,
                         cur_opera_info->len,
                         suspend);
            pmu_flash_read_config();
            FLASH_REMAP_DONE(mod_info->dev_id, cur_opera_info->addr, cur_opera_info->len);
            if(result == HAL_NORFLASH_OK)
            {
                opera_is_completed = true;
                goto __opera_is_completed;
            }
            else if(result == HAL_NORFLASH_SUSPENDED)
            {
                mod_info->state = NORFLASH_API_STATE_ERASE_SUSPEND;
            }
            else
            {
                ASSERT(0, "%s: %d, hal_norflash_erase_suspend failed,result = %d",__func__,__LINE__,result);
            }
        }
        else if(mod_info->state == NORFLASH_API_STATE_ERASE_SUSPEND)
        {
            suspend_number ++;
            FLASH_REMAP_START(mod_info->dev_id, cur_opera_info->addr, cur_opera_info->len);
            pmu_flash_write_config();
            result = hal_norflash_erase_resume(mod_info->dev_id,
                         suspend);
            pmu_flash_read_config();
            FLASH_REMAP_DONE(mod_info->dev_id, cur_opera_info->addr, cur_opera_info->len);
            if(result == HAL_NORFLASH_OK)
            {
                opera_is_completed = true;
                goto __opera_is_completed;
            }
            else if(result == HAL_NORFLASH_SUSPENDED)
            {
                mod_info->state = NORFLASH_API_STATE_ERASE_SUSPEND;
            }
            else
            {
                ASSERT(0, "%s: %d, hal_norflash_write_resume failed,result = %d",
                       __func__,__LINE__,result);
            }
        }
        else
        {
            ASSERT(0, "%s: %d, mod_info->state error,state = %d",
                __func__,__LINE__,mod_info->state);
        }
    }

__opera_is_completed:

    if(opera_is_completed)
    {
        mod_info->state = NORFLASH_API_STATE_IDLE;
        if(!nosuspend
           && mod_info->cb_func
           && ((cur_opera_info->w_len > 0 && cur_opera_info->type == NORFLASH_API_WRITTING)
               || (cur_opera_info->len > 0 && cur_opera_info->type == NORFLASH_API_ERASING))
           )
        {
            NORFLASH_API_TRACE(6,"%s: w/e done.type = %d,addr = 0x%x,w_len = 0x%x,len = 0x%x,suspend_num = %d.",
                __func__,
                cur_opera_info->type,
                cur_opera_info->addr + cur_opera_info->w_offs,
                cur_opera_info->w_len,
                cur_opera_info->len,
                suspend_number);
            if(cur_opera_info->type == NORFLASH_API_WRITTING)
            {
                opera_result.addr = cur_opera_info->addr + cur_opera_info->w_offs;
                opera_result.len = cur_opera_info->w_len;
            }
            else
            {
                opera_result.addr = cur_opera_info->addr;
                opera_result.len = cur_opera_info->len;
            }
            opera_result.type = cur_opera_info->type;
            opera_result.result = NORFLASH_API_OK;
            opera_result.remain_num = _get_ew_count(mod_info) - 1;
            mod_info->cb_func(&opera_result);
        }
        _opera_del(mod_info,cur_opera_info);
        mod_info->cur_opera_info = NULL;
    }

    return ret;
}

void _flush_disable(enum NORFLASH_API_USER user_id,uint32_t cb)
{
    norflash_api_info.allowed_cb[user_id] = (NOFLASH_API_FLUSH_ALLOWED_CB)cb;
}

void _flush_enable(enum NORFLASH_API_USER user_id)
{
    norflash_api_info.allowed_cb[user_id] = NULL;
}

bool _flush_is_allowed(void)
{
    bool ret = true;
    uint32_t user_id;

    for(user_id = NORFLASH_API_USER_CP; user_id < NORFLASH_API_USER_COUNTS; user_id ++)
    {
        if(norflash_api_info.allowed_cb[user_id])
        {
            if(!norflash_api_info.allowed_cb[user_id]())
            {
                ret = false;
            }
            else
            {
                norflash_api_info.allowed_cb[user_id] = NULL;
            }
        }
    }
    return ret;
}

//-------------------------------------------------------------------
// APIS Function.
//-------------------------------------------------------------------
enum NORFLASH_API_RET_T norflash_api_init(void)
{
    uint32_t i;

    memset((void*)&norflash_api_info,0,sizeof(NORFLASH_API_INFO));
    norflash_api_info.cur_mod_id = NORFLASH_API_MODULE_ID_COUNT;
    norflash_api_info.is_inited = true;
    norflash_api_info.cur_mod = NULL;
    for(i = 0; i < NORFLASH_API_MODULE_ID_COUNT; i++)
    {
        norflash_api_info.mod_info[i].state = NORFLASH_API_STATE_UNINITED;
    }

#if !defined(FLASH_API_SIMPLE)
#if defined(FLASH_SUSPEND)
    hal_sleep_set_sleep_hook(HAL_SLEEP_HOOK_NORFLASH_API, norflash_api_flush);
#else
    hal_sleep_set_deep_sleep_hook(HAL_DEEP_SLEEP_HOOK_NORFLASH_API, norflash_api_flush);
#endif
#endif

    return NORFLASH_API_OK;
}

enum NORFLASH_API_RET_T norflash_api_register(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                enum HAL_NORFLASH_ID_T dev_id,
                uint32_t mod_base_addr,
                uint32_t mod_len,
                uint32_t mod_block_len,
                uint32_t mod_sector_len,
                uint32_t mod_page_len,
                uint32_t buffer_len,
                NORFLASH_API_OPERA_CB cb_func
                )
{
    MODULE_INFO *mod_info = NULL;

    NORFLASH_API_TRACE(5,"%s: mod_id = %d,dev_id = %d,mod_base_addr = 0x%x,mod_len = 0x%x,",
            __func__,mod_id,dev_id,mod_base_addr,mod_len);
    NORFLASH_API_TRACE(4,"mod_block_len = 0x%x,mod_sector_len = 0x%x,mod_page_len = 0x%x,buffer_len = 0x%x.",
            mod_block_len,mod_sector_len,mod_page_len,buffer_len);
    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(2,"%s: %d, norflash_api uninit!",__func__,__LINE__);
        return NORFLASH_API_ERR_UNINIT;
    }

    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        NORFLASH_API_TRACE(2,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_BAD_MOD_ID;
    }

    if(dev_id >= HAL_NORFLASH_ID_NUM)
    {
        NORFLASH_API_TRACE(2,"%s : dev_id error! mod_id = %d.",__func__,dev_id);
        return NORFLASH_API_BAD_DEV_ID;
    }

    if(buffer_len < mod_sector_len || !API_IS_ALIGN(buffer_len,mod_sector_len))
    {
        NORFLASH_API_TRACE(2,"%s : buffer_len error buffer_len = %d.",__func__, buffer_len);
        return NORFLASH_API_BAD_BUFF_LEN;
    }
    mod_info = _get_module_info(mod_id);
    if(mod_info->is_inited)
    {
        // NORFLASH_API_TRACE(3,"%s: %d, norflash_async[%d] has registered!",__func__,__LINE__,mod_id);
        return NORFLASH_API_OK; //NORFLASH_API_ERR_HASINIT;
    }
    mod_info->dev_id = dev_id;
    mod_info->mod_id = mod_id;
    mod_info->mod_base_addr = mod_base_addr;
    mod_info->mod_len = mod_len;
    mod_info->mod_block_len = mod_block_len;
    mod_info->mod_sector_len = mod_sector_len;
    mod_info->mod_page_len = mod_page_len;
    mod_info->buff_len = buffer_len;
    mod_info->cb_func = cb_func;
    mod_info->opera_info = NULL;
    mod_info->cur_opera_info = NULL;
    mod_info->state = NORFLASH_API_STATE_IDLE;
    mod_info->is_inited = true;
    return NORFLASH_API_OK;
}

enum NORFLASH_API_RET_T norflash_api_read(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                uint32_t start_addr,
                uint8_t *buffer,
                uint32_t len
                )
{
    MODULE_INFO *mod_info = NULL;
    int32_t result;
    enum NORFLASH_API_RET_T ret;
    uint32_t lock;

    NORFLASH_API_TRACE(4,"%s:mod_id = %d,start_addr = 0x%x,len = 0x%x",
                __func__,mod_id, start_addr, len);
    ASSERT(buffer,"%s:buffer is null! ",
                __func__);
    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(1,"%s: norflash_api uninit!",__func__);
        return NORFLASH_API_ERR_UNINIT;
    }
    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        NORFLASH_API_TRACE(2,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_BAD_MOD_ID;
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        NORFLASH_API_TRACE(2,"%s : module unregistered! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_ERR_UNINIT;
    }

    if(start_addr < mod_info->mod_base_addr
       || start_addr + len > mod_info->mod_base_addr + mod_info->mod_len)
    {
        NORFLASH_API_TRACE(3,"%s : reading out of range! start_address = 0x%x,len = 0x%x.",
                    __func__, start_addr, len);
        return NORFLASH_API_BAD_ADDR;
    }

    if(len == 0)
    {
        NORFLASH_API_TRACE(2,"%s : len error! len = %d.",
                   __func__, len);
        return NORFLASH_API_BAD_LEN;
    }
    lock = int_lock_global();
    result = _opera_read(mod_info,start_addr,(uint8_t*)buffer,len);

    if(result)
    {
        ret =  NORFLASH_API_ERR;
    }
    else
    {
        ret = NORFLASH_API_OK;
    }
    int_unlock_global(lock);
    NORFLASH_API_TRACE(2,"%s: done. ret = %d.",__func__,ret);
    return ret;
}

enum NORFLASH_API_RET_T norflash_sync_read(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                uint32_t start_addr,
                uint8_t *buffer,
                uint32_t len
                )
{
    MODULE_INFO *mod_info = NULL;
    uint32_t lock;

    NORFLASH_API_TRACE(4,"%s:mod_id = %d,start_addr = 0x%x,len = 0x%x",
                __func__,mod_id,start_addr,len);
    ASSERT(buffer,"%s:%d,buffer is null! ",
                __func__,__LINE__);
    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(1,"%s: norflash_api uninit!", __func__);
        return NORFLASH_API_ERR_UNINIT;
    }
    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        NORFLASH_API_TRACE(2,"%s : mod_id error! mod_id = %d.", __func__, mod_id);
        return NORFLASH_API_BAD_MOD_ID;
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        NORFLASH_API_TRACE(2,"%s : module unregistered! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_ERR_UNINIT;
    }

    if(start_addr < mod_info->mod_base_addr
       || start_addr + len > mod_info->mod_base_addr + mod_info->mod_len)
    {
        NORFLASH_API_TRACE(3,"%s : reading out of range! start_address = 0x%x,len = 0x%x.",
                    __func__, start_addr, len);
        return NORFLASH_API_BAD_ADDR;
    }

    if(len == 0)
    {
        NORFLASH_API_TRACE(2,"%s : len error! len = %d.",
                   __func__, len);
        return NORFLASH_API_BAD_LEN;
    }

    lock = int_lock_global();
    FLASH_REMAP_START(mod_info->dev_id,start_addr, len);
    memcpy(buffer,(uint8_t*)start_addr,len);
    FLASH_REMAP_DONE(mod_info->dev_id,start_addr, len);
    NORFLASH_API_TRACE(1,"%s: done.",__func__);
    int_unlock_global(lock);

    return NORFLASH_API_OK;
}

enum NORFLASH_API_RET_T norflash_api_erase(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                uint32_t start_addr,
                uint32_t len,
                bool async
                )
{
    MODULE_INFO *mod_info = NULL;
    MODULE_INFO *cur_mod_info = NULL;
    uint32_t lock;
    int32_t result;
    bool bresult = false;
    enum NORFLASH_API_RET_T ret;

    NORFLASH_API_TRACE(5,"%s: mod_id = %d,start_addr = 0x%x,len = 0x%x,async = %d.",
                __func__,mod_id,start_addr,len,async);
    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(1,"%s: norflash_api uninit!",__func__);
        return NORFLASH_API_ERR_UNINIT;
    }
    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        NORFLASH_API_TRACE(2,"%s : invalid mod_id! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_BAD_MOD_ID;
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        NORFLASH_API_TRACE(2,"%s : module unregistered! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_ERR_UNINIT;
    }

    if(start_addr < mod_info->mod_base_addr
       || start_addr + len > mod_info->mod_base_addr + mod_info->mod_len)
    {
        NORFLASH_API_TRACE(3,"%s : erase out of range! start_address = 0x%x,len = 0x%x.",
                    __func__,start_addr,len);
        return NORFLASH_API_BAD_ADDR;
    }

    if(!API_IS_ALIGN(start_addr,mod_info->mod_sector_len))
    {
        NORFLASH_API_TRACE(2,"%s : start_address no alignment! start_address = %d.",
                   __func__, start_addr);
        return NORFLASH_API_BAD_ADDR;
    }

    if(
#ifdef PUYA_FLASH_ERASE_PAGE_ENABLE
       len != mod_info->mod_page_len &&
#endif
       len != mod_info->mod_sector_len &&
       len != mod_info->mod_block_len)
    {
        NORFLASH_API_TRACE(2,"%s : len error. len = %d!",
                   __func__, len);
        return NORFLASH_API_BAD_LEN;
    }

#if defined(FLASH_API_SIMPLE)
    async = false;
#endif
    if(async)
    {
        lock = int_lock_global();

        // add to opera_info chain header.
        result = _e_opera_add(mod_info,start_addr,len);
        if(result == 0)
        {
            ret = NORFLASH_API_OK;
        }
        else
        {
            ret = NORFLASH_API_BUFFER_FULL;
        }
        int_unlock_global(lock);
        NORFLASH_API_TRACE(4,"%s: _e_opera_add done. start_addr = 0x%x,len = 0x%x,ret = %d.",
                __func__,start_addr,len,ret);
    }
    else
    {
        lock = int_lock_global();
        // Handle the suspend operation.
        if(norflash_api_info.cur_mod != NULL
           && mod_info != norflash_api_info.cur_mod)
        {
            cur_mod_info = norflash_api_info.cur_mod;
            while(cur_mod_info->state != NORFLASH_API_STATE_IDLE)
            {
                if(!_flush_is_allowed())
                {
                    continue;
                }
                bresult = _opera_flush(cur_mod_info,true);
                if(!bresult)
                {
                    norflash_api_info.cur_mod = NULL;
                }
            }
        }

        // flush all of cur module opera.
        // norflash_api_info.cur_mod_id = mod_id;
        do{
            if(!_flush_is_allowed())
            {
                continue;
            }
            bresult = _opera_flush(mod_info,true);
        }while(bresult);
        FLASH_REMAP_START(mod_info->dev_id,start_addr,len);
        pmu_flash_write_config();
        result = hal_norflash_erase(mod_info->dev_id,start_addr, len);
        pmu_flash_read_config();
        FLASH_REMAP_DONE(mod_info->dev_id,start_addr,len);
        if(result == HAL_NORFLASH_OK)
        {
            ret = NORFLASH_API_OK;
        }
        else if(result == HAL_NORFLASH_BAD_ADDR)
        {
            ret = NORFLASH_API_BAD_ADDR;
        }
        else if(result == HAL_NORFLASH_BAD_LEN)
        {
            ret = NORFLASH_API_BAD_LEN;
        }
        else
        {
            ret = NORFLASH_API_ERR;
        }
        int_unlock_global(lock);
        NORFLASH_API_TRACE(4,"%s: hal_norflash_erase done. start_addr = 0x%x,len = 0x%x,ret = %d.",
                __func__,start_addr,len,ret);
    }
    //NORFLASH_API_TRACE(2,"%s: done.ret = %d.",__func__, ret);
    return ret;
}

enum NORFLASH_API_RET_T norflash_api_write(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                uint32_t start_addr,
                const uint8_t *buffer,
                uint32_t len,
                bool async
                )
{
    MODULE_INFO *mod_info = NULL;
    MODULE_INFO *cur_mod_info = NULL;
    uint32_t lock;
    int32_t result;
    bool bresult = false;
    enum NORFLASH_API_RET_T ret;

    NORFLASH_API_TRACE(4,"%s: mod_id = %d,start_addr = 0x%x,len = 0x%x.",
                __func__,mod_id,start_addr,len);

    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(1,"%s: norflash_api uninit!",__func__);
        return NORFLASH_API_ERR_UNINIT;
    }

    if(mod_id > NORFLASH_API_MODULE_ID_COUNT)
    {
        NORFLASH_API_TRACE(2,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_BAD_MOD_ID;
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        NORFLASH_API_TRACE(2,"%s :module unregistered! mod_id = %d.",__func__, mod_id);
        return NORFLASH_API_ERR_UNINIT;
    }


    if(start_addr < mod_info->mod_base_addr
       || start_addr + len > mod_info->mod_base_addr + mod_info->mod_len)
    {
        NORFLASH_API_TRACE(3,"%s : writting out of range! start_address = 0x%x,len = 0x%x.",
                    __func__,mod_info->mod_base_addr,mod_info->mod_len);
        return NORFLASH_API_BAD_ADDR;
    }

    if(len == 0)
    {
        NORFLASH_API_TRACE(2,"%s : len error! len = %d.",
                   __func__,len);
        return NORFLASH_API_BAD_LEN;
    }
#if defined(FLASH_API_SIMPLE)
    async = false;
#endif
    if(async)
    {
        // add to opera_info chain header.
        lock = int_lock_global();
        result = _w_opera_add(mod_info,start_addr,len,(uint8_t*)buffer);
        if(result == 0)
        {
            ret = NORFLASH_API_OK;
        }
        else
        {
            ret = NORFLASH_API_BUFFER_FULL;
        }
        int_unlock_global(lock);
        NORFLASH_API_TRACE(4,"%s: _w_opera_add done. start_addr = 0x%x,len = 0x%x,ret = %d.",
                __func__,start_addr,len,ret);
    }
    else
    {
        lock = int_lock_global();

        // flush the opera of currently being processed.
        if(norflash_api_info.cur_mod != NULL
            && mod_info != norflash_api_info.cur_mod)
        {
            cur_mod_info = norflash_api_info.cur_mod;
            while(cur_mod_info->state != NORFLASH_API_STATE_IDLE)
            {
                if(!_flush_is_allowed())
                {
                    continue;
                }
                bresult = _opera_flush(cur_mod_info,true);
                if(!bresult)
                {
                    norflash_api_info.cur_mod = NULL;
                }
            }
        }

        // flush all of cur module opera.
        do{
            if(!_flush_is_allowed())
            {
                continue;
            }
            bresult = _opera_flush(mod_info,true);
        }while(bresult);
        FLASH_REMAP_START(mod_info->dev_id,start_addr,len);
        pmu_flash_write_config();
        result = hal_norflash_write(mod_info->dev_id,start_addr, buffer, len);
        pmu_flash_read_config();
        FLASH_REMAP_DONE(mod_info->dev_id,start_addr,len);
        if(result == HAL_NORFLASH_OK)
        {
            ret = NORFLASH_API_OK;
        }
        else if(result == HAL_NORFLASH_BAD_ADDR)
        {
            ret = NORFLASH_API_BAD_ADDR;
        }
        else if(result == HAL_NORFLASH_BAD_LEN)
        {
            ret = NORFLASH_API_BAD_LEN;
        }
        else
        {
            ret = NORFLASH_API_ERR;
        }
        int_unlock_global(lock);
        NORFLASH_API_TRACE(4,"%s: hal_norflash_write done. start_addr = 0x%x,len = 0x%x,ret = %d.",
                __func__,start_addr,len,ret);
    }
    return ret;
}

// -1: error, 0:all pending flash op flushed, 1:still pending flash op to be flushed
int norflash_api_flush(void)
{
    enum NORFLASH_API_MODULE_ID_T mod_id = NORFLASH_API_MODULE_ID_COUNT;
    MODULE_INFO *mod_info = NULL;
    uint32_t lock;
    bool bret = false;

    lock = int_lock_global();
    if(!norflash_api_info.is_inited)
    {
        NORFLASH_API_TRACE(1,"%s: norflash_api uninit!",__func__);
        int_unlock_global(lock);
        return -1;
    }
#if defined(FLASH_API_SIMPLE)
    int_unlock_global(lock);
    return 0;
#endif

    if(!_flush_is_allowed())
    {
        int_unlock_global(lock);
        return 0;
    }

    mod_info = _get_cur_mod();
    if(!mod_info)
    {
        int_unlock_global(lock);
        return 0;
    }
    mod_id = _get_mod_id(mod_info);

    norflash_api_info.cur_mod_id = mod_id;
    norflash_api_info.cur_mod = mod_info;
    bret = _opera_flush(mod_info,false);
    if(!bret)
    {
        norflash_api_info.cur_mod = NULL;
    }
    int_unlock_global(lock);

    return 1;
}

bool norflash_api_buffer_is_free(
                enum NORFLASH_API_MODULE_ID_T mod_id)
{
    MODULE_INFO *mod_info = NULL;
    uint32_t count;

    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        ASSERT(0,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        ASSERT(0,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
    }

    count = _get_ew_count(mod_info);
    if(count > 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}

uint32_t norflash_api_get_used_buffer_count(
                enum NORFLASH_API_MODULE_ID_T mod_id,
                enum NORFLASH_API_OPRATION_TYPE type
                )
{
    MODULE_INFO *mod_info = NULL;
    uint32_t count = 0;

    if(mod_id >= NORFLASH_API_MODULE_ID_COUNT)
    {
        ASSERT(0,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
    }

    mod_info = _get_module_info(mod_id);
    if(!mod_info->is_inited)
    {
        ASSERT(0,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
    }
    if(type & NORFLASH_API_WRITTING)
    {
        count = _get_w_count(mod_info);
    }

    if(type & NORFLASH_API_ERASING)
    {
        count += _get_e_count(mod_info);
    }
    return count;
}

uint32_t norflash_api_get_free_buffer_count(
                enum NORFLASH_API_OPRATION_TYPE type
                )
{
    MODULE_INFO *mod_info = NULL;
    uint32_t i;
    uint32_t used_count = 0;
    uint32_t free_count = 0;

    if(type & NORFLASH_API_WRITTING)
    {
        for(i = NORFLASH_API_MODULE_ID_LOG_DUMP; i < NORFLASH_API_MODULE_ID_COUNT; i ++)
        {
            mod_info = _get_module_info((enum NORFLASH_API_MODULE_ID_T)i);
            if(mod_info->is_inited)
            {
                used_count += _get_w_count(mod_info);
            }
        }
        ASSERT(used_count <= NORFLASH_API_OPRA_LIST_LEN,"writting opra count error!");
        free_count += (NORFLASH_API_OPRA_LIST_LEN - used_count);
    }

    if(type & NORFLASH_API_ERASING)
    {
        for(i = NORFLASH_API_MODULE_ID_LOG_DUMP; i < NORFLASH_API_MODULE_ID_COUNT; i ++)
        {
            mod_info = _get_module_info((enum NORFLASH_API_MODULE_ID_T)i);
            if(mod_info->is_inited)
            {
                used_count += _get_e_count(mod_info);
            }
        }
        ASSERT(used_count <= NORFLASH_API_OPRA_LIST_LEN,"erase opra count error!");
        free_count += (NORFLASH_API_OPRA_LIST_LEN - used_count);
    }
    return free_count;
}


void norflash_api_flush_all(void)
{
    int ret;
    int cnt = 0;

    norflash_api_flush_enable_all();
    do
    {
        ret = norflash_api_flush();
        if(ret == 1)
        {
            cnt ++;
        }
    } while (1 == ret);

    NORFLASH_API_TRACE(2,"%s: done. cnt = %d.",__func__,cnt);
}

void norflash_api_flush_disable(enum NORFLASH_API_USER user_id,uint32_t cb)
{
    if(!norflash_api_info.is_inited)
    {
        return;
    }
    ASSERT(user_id < NORFLASH_API_USER_COUNTS, "%s: user_id(%d) error!", __func__, user_id);
    _flush_disable(user_id,cb);
}

void norflash_api_flush_enable(enum NORFLASH_API_USER user_id)
{
    if(!norflash_api_info.is_inited)
    {
        return;
    }
    ASSERT(user_id < NORFLASH_API_USER_COUNTS, "%s: user_id(%d) too large!", __func__, user_id);
    _flush_enable(user_id);
}

void norflash_api_flush_enable_all(void)
{
    uint32_t user_id;

    if(!norflash_api_info.is_inited)
    {
        return;
    }
    for(user_id = NORFLASH_API_USER_CP; user_id < NORFLASH_API_USER_COUNTS; user_id ++)
    {
        _flush_enable((enum NORFLASH_API_USER)user_id);
    }
}


enum NORFLASH_API_STATE norflash_api_get_state(enum NORFLASH_API_MODULE_ID_T mod_id)
{
    ASSERT(mod_id < NORFLASH_API_MODULE_ID_COUNT,"%s : mod_id error! mod_id = %d.",__func__, mod_id);
    return norflash_api_info.mod_info[mod_id].state;
}

void norflash_flush_all_pending_op(void)
{
    norflash_api_flush_all();
}

void app_flush_pending_flash_op(enum NORFLASH_API_MODULE_ID_T module,
    enum NORFLASH_API_OPRATION_TYPE type)
{
    hal_trace_pause();
    do
    {
        norflash_api_flush();
        if (NORFLASH_API_ALL != type)
        {
            if (0 == norflash_api_get_used_buffer_count(module, type))
            {
                break;
            }
        }
        else
        {
            if (norflash_api_buffer_is_free(module))
            {
                break;
            }
        }
        osDelay(10);
    } while (1);

    hal_trace_continue();
}

void app_flash_page_erase(enum NORFLASH_API_MODULE_ID_T module, uint32_t flashOffset)
{
    // check whether the flash has been erased
    bool isEmptyPage            = true;
    uint32_t *ptrStartFlashAddr = ( uint32_t * )(FLASH_NC_BASE + flashOffset);
    for (uint32_t index = 0; index < FLASH_SECTOR_SIZE / sizeof(uint32_t); index++)
    {
        if (0xFFFFFFFF != ptrStartFlashAddr[index])
        {
            isEmptyPage = false;
            break;
        }
    }

    if (isEmptyPage)
    {
        return;
    }

    uint32_t lock;
    enum NORFLASH_API_RET_T ret;

    flashOffset &= 0xFFFFFF;
    do
    {
        lock = int_lock_global();
        hal_trace_pause();
        ret = norflash_api_erase(module,
                                 (FLASH_NC_BASE + flashOffset),
                                 FLASH_SECTOR_SIZE,
                                 true);
        hal_trace_continue();
        int_unlock_global(lock);

        if (NORFLASH_API_OK == ret)
        {
            TRACE(1,"%s: norflash_api_erase ok!", __func__);
            break;
        }
        else if (NORFLASH_API_BUFFER_FULL == ret)
        {
            TRACE(0,"Flash async cache overflow! To flush it.");
            app_flush_pending_flash_op(module, NORFLASH_API_ERASING);
        }
        else
        {
            ASSERT(0, "%s: norflash_api_erase failed. ret = %d", __FUNCTION__, ret);
        }
    } while (1);
}

void app_flash_page_program(enum NORFLASH_API_MODULE_ID_T module,
                             uint32_t flashOffset,
                             uint8_t *ptr,
                             uint32_t len,
                             bool synWrite)
{
    uint32_t lock;
    enum NORFLASH_API_RET_T ret;
    bool is_async = false;

    flashOffset &= 0xFFFFFF;

    if (synWrite)
    {
        is_async = false;
    }
    else
    {
        is_async = true;
    }

    do
    {
        lock = int_lock_global();
        hal_trace_pause();

        ret = norflash_api_write(module,
                                 (FLASH_NC_BASE + flashOffset),
                                 ptr,
                                 len,
                                 is_async);

        hal_trace_continue();

        int_unlock_global(lock);

        if (NORFLASH_API_OK == ret)
        {
            TRACE(1,"%s: norflash_api_write ok!", __func__);
            break;
        }
        else if (NORFLASH_API_BUFFER_FULL == ret)
        {
            TRACE(0,"Flash async cache overflow! To flush it.");
            app_flush_pending_flash_op(module, NORFLASH_API_WRITTING);
        }
        else
        {
            ASSERT(0, "%s: norflash_api_write failed. ret = %d", __FUNCTION__, ret);
        }
    } while (1);
}


