#include "include.h"

#if FOT_EN

#define FOT_DEBUG_EN                    0

#if FOT_DEBUG_EN
#define FOT_DEBUG(...)                  printf(__VA_ARGS__)
#define FOT_DEBUG_R(...)                print_r(__VA_ARGS__)
#else
#define FOT_DEBUG(...)
#define FOT_DEBUG_R(...)
#endif

#define FOT_STA_INIT            BIT(0)
#define FOT_STA_START           BIT(1)
#define FOT_STA_PAUSE           BIT(2)

#define FOT_FLAG_UPDATE_OK      BIT(0)
#define FOT_FLAG_SYS_RESET      BIT(1)
#define FOT_FLAG_APP_CONNECT    BIT(2)


#define FOT_NOTIFY_STA          0x90
#define FOT_GET_INFO            0x91
#define FOT_GET_INFO_TLV        0x92
#define FOT_OUT_DATA_START      0xA0
#define FOT_OUT_DATA_CONTINUE   0x20


#define FOT_CMD_POS             0
#define FOT_SEQ_POS             1
#define FOT_ADDR_POS            2
#define FOT_DATA_LEN_POS        6
#define DATA_START_POS          10
#define DATA_CONTINUE_POS       2

#define FOT_BLOCK_LEN           512


extern bool ble_fot_send_packet(u8 *buf, u8 len);
extern u16 get_spp_mtu_size(void);

u16 dev_version = 0x0001; //Equipment version number

AT(.com_text.const)
static const u8 fot_auth_data[] = {0xCC, 0xAA, 0x55, 0xEE, 0x12, 0x19, 0xE4};

typedef enum{
    INFO_DEV_VER  = 1,
    INFO_UPDATE_REQ,
    INFO_DEV_FEATURE,
    INFO_DEV_CONNECT_STA,
    INFO_EDR_ADDR,
    INFO_DEV_CHANNEL,
}DEV_INFO_E;

//typedef struct __attribute__((packed)){
//    u16 remote_ver;
//    u32 total_len;
//    u32 remain_len;
//    u32 hash;
//    u16 data_pos;
//    u8 fot_recv_ok;
//    u32 addr;
//    u32 tick;
//}fot_s;
typedef struct __attribute__((packed)){
    u16 remote_ver;
    u32 total_len;//每此写的长度
    u32 sum_len;//升级文件的总长度
    u32 remain_len;\
    u32 rec_len;
    u32 hash;
    u16 data_pos;
    u8 fot_recv_ok;
    u8 fot_cmd_flag;
    u32 addr;
    u32 tick;
}fot_s;
fot_s fot_var;

u8 fot_data[FOT_BLOCK_LEN] AT(.fot_data.buf);


static u8 fot_seq = 0;
static u8 fot_remote_seq = 0;
static u8 fot_sta = 0;
static u8 fot_flag = 0;
static u16 fot_connect_sta = 0;

AT(.text.fot.cache)
static void fot_sent_proc(u8 *buf,u8 len)
{
    if((fot_flag & FOT_FLAG_APP_CONNECT) == 0){
        return;
    }

//    FOT_DEBUG("fot tx:");
//    FOT_DEBUG_R(buf,len);

#if LE_APP_FOT_EN
    if(ble_is_connect()){
        ble_fot_send_packet(buf,len);
    }else
#endif
    {
        bt_spp_tx(buf,len);
    }
}

AT(.text.fot.update)
static void fot_reply_info_tlv(u8 *buf,u8 len)
{
    u8 read_offset = 0;
    u8 write_offset = 0;
    u8 rsp[32];
    u8 val_len = 0;

    if((buf == NULL) || (len == 0)){
        return;
    }

    rsp[write_offset++] = FOT_GET_INFO_TLV;
    rsp[write_offset++] = fot_seq++;

    while(read_offset < len){
        switch(buf[read_offset]){
            case INFO_DEV_VER:
                FOT_DEBUG("INFO_DEV_VER\n");
                val_len = buf[read_offset + 1];
                rsp[write_offset++] = INFO_DEV_VER;
                rsp[write_offset++] = 2;
                rsp[write_offset++] = dev_version & 0xff;
                rsp[write_offset++] = (dev_version >> 8) & 0xff;
            break;

            case INFO_DEV_FEATURE:
                FOT_DEBUG("INFO_DEV_FEATURE\n");
                {
                    u16 dev_ability = 0;
                    val_len = buf[read_offset + 1];
                    rsp[write_offset++] = INFO_DEV_FEATURE;
                    rsp[write_offset++] = 2;
                    rsp[write_offset++] = dev_ability & 0xff;
                    rsp[write_offset++] = (dev_ability >> 8) & 0xff;
                }
            break;

            case INFO_DEV_CONNECT_STA:
                FOT_DEBUG("INFO_DEV_CONNECT_STA\n");
                val_len = buf[read_offset + 1];
                rsp[write_offset++] = INFO_DEV_CONNECT_STA;
                rsp[write_offset++] = 2;
                rsp[write_offset++] = fot_connect_sta & 0xff;
                rsp[write_offset++] = (fot_connect_sta >> 8) & 0xff;
                break;

            case INFO_EDR_ADDR:
                FOT_DEBUG("INFO_EDR_ADDR\n");
                val_len = buf[read_offset + 1];
                rsp[write_offset++] = INFO_EDR_ADDR;
                rsp[write_offset++] = 6;
                memcpy(&rsp[write_offset],xcfg_cb.bt_addr,6);
                write_offset += 6;
                break;

            default:
                val_len = buf[read_offset + 1];
                break;
        }
        read_offset += (2 + val_len);
    }

    if(write_offset > sizeof(rsp)){
        printf("fot:rsp buf overflow!!!\n");
        while(1);
    }

    fot_sent_proc(rsp,write_offset);
}


AT(.text.fot.update)
static void fot_reply_dev_version(void)
{
    u8 data[5];

    data[0] = FOT_GET_INFO;
    data[1] = fot_seq++;
    data[2] = INFO_DEV_VER;
    memcpy(&data[3],&dev_version,2);
    fot_sent_proc(data,5);
}

AT(.text.fot.cache)
static void fot_dev_notify_sta(u8 sta)
{
    u8 buf[3];

    buf[0] = FOT_NOTIFY_STA;
    buf[1] = fot_seq++;
    buf[2] = sta;

    fot_sent_proc(buf,3);
}

AT(.text.fot.update)
u8 is_fot_start(void)
{
    return (fot_sta & FOT_STA_START) ? 1:0;
}

AT(.text.fot.update)
void fot_update_pause(void)
{
    if(fot_sta & FOT_STA_START){
        fot_sta = FOT_STA_PAUSE;
        fot_dev_notify_sta(FOT_UPDATE_PAUSE);
    }
}

AT(.text.fot.update)
void fot_update_continue(void)
{
    if(fot_sta & FOT_STA_PAUSE){
        bsp_fot_init();
        fot_dev_notify_sta(FOT_UPDATE_CONTINUE);
    }
}


AT(.text.fot.update)
static void fot_reply_update_request(void)
{
    u16 flash_remote_ver;
    u32 hash;
    u32 addr = 0;
    u32 block_len = FOT_BLOCK_LEN;
    u16 packet_len = 0;
    u8 data[14];
    u8 need_update = 1;

    if(bt_get_status() >= BT_STA_INCOMING){
        need_update = 0;
        goto fot_req_reply;
    }

#if LE_APP_FOT_EN
    if(ble_is_connect()){
        packet_len = ble_get_gatt_mtu();
        ble_update_conn_param(12,0,400);
    }else
#endif
    {
        packet_len = get_spp_mtu_size() - 6;
    }

    printf("fot_packet_len:%d\n",packet_len);

    fot_init();

    fot_sta |= FOT_STA_START;

    set_sys_clk(SYS_120M);

    FOT_DEBUG("hash_val:0x%x\n",fot_var.hash);

    param_fot_remote_ver_read((u8*)&flash_remote_ver);
    param_fot_hash_read((u8*)&hash);

    FOT_DEBUG("flash hash val:0x%x\n",hash);

    if((fot_var.hash != 0xFFFFFFFF) && (flash_remote_ver == fot_var.remote_ver) && (hash == fot_var.hash)){
        if(fot_breakpoint_info_read() == true){
            addr = fot_get_curaddr();
        }
    }else{
        param_fot_addr_write((u8*)&addr);
        param_fot_remote_ver_write((u8*)&fot_var.remote_ver);
        param_fot_hash_write((u8*)&fot_var.hash);
    }

fot_req_reply:
    data[0] = FOT_GET_INFO;
    data[1] = fot_seq++;
    data[2] = INFO_UPDATE_REQ;
    memcpy(&data[3],&addr,4);
    memcpy(&data[7],&block_len,4);
    memcpy(&data[11],&packet_len,2);
    data[13] = need_update;
    fot_sent_proc(data,14);
}

AT(.text.fot.update)
void bsp_fot_init(void)
{
    u8  dev_version_str[] = SW_VERSION;
    u16 version_temp = 0;

    memset(&fot_var,0,sizeof(fot_var));

    dev_version = 0x00;
    version_temp = dev_version_str[1]-'0';
    version_temp <<= 12;
    dev_version |= version_temp;

    version_temp = dev_version_str[3]-'0';
    version_temp <<= 8;
    dev_version |= version_temp;

    version_temp = dev_version_str[5]-'0';
    version_temp <<= 4;
    dev_version |= version_temp;

    fot_sta = FOT_STA_INIT;
}


#if LE_APP_FOT_EN
void fot_ble_disconnect_callback(void)
{
    printf("--->fot_ble_disconnect_callback\n");

    fot_sta = 0;
    fot_seq = 0;
    fot_remote_seq = 0;
    fot_flag &= ~FOT_FLAG_APP_CONNECT;
}

void fot_ble_connect_callback(void)
{
    printf("--->fot_ble_connect_callback\n");
}
#endif  //LE_APP_FOT_EN

#if BT_SPP_FOT_EN
void fot_spp_connect_callback(void)
{
    printf("--->fot_spp_connect_callback\n");
}

void fot_spp_disconnect_callback(void)
{
    printf("--->fot_spp_disconnect_callback\n");

    fot_sta = 0;
    fot_seq = 0;
    fot_remote_seq = 0;
    fot_flag &= ~FOT_FLAG_APP_CONNECT;
}
#endif // BT_SPP_FOT_EN

//------------------------------------------------------------------------------
AT(.text.fot.cache)
void fot_recv_proc(u8 *buf, u16 len)
{
    u32 addr;
    u32 recv_data_len;

    if(fot_remote_seq != buf[FOT_SEQ_POS]){
        if(memcmp(fot_auth_data, buf, 7)){      ////Access code passed first
            printf("remote seq err:%d,%d\n",fot_remote_seq,buf[FOT_SEQ_POS]);
            fot_dev_notify_sta(FOT_ERR_SEQ);
        }
        return;
    }

    printf("<-");
    print_r(buf, 4);

    fot_remote_seq++;

    if((fot_sta & FOT_STA_INIT) == 0){
        return;
    }

    if(buf[FOT_CMD_POS] == FOT_GET_INFO_TLV){
        fot_reply_info_tlv(&buf[2],len-2);
    }else if(buf[FOT_CMD_POS] == FOT_GET_INFO){
        if(buf[2] == INFO_DEV_VER){
            fot_reply_dev_version();
        }else if(buf[2] == INFO_UPDATE_REQ){
            memcpy(&fot_var.remote_ver,&buf[3],2);
            memcpy(&fot_var.hash,&buf[5],4);
            fot_reply_update_request();
        }
    }else if(buf[FOT_CMD_POS] == FOT_OUT_DATA_START){
        if(fot_var.remain_len){
            FOT_DEBUG("--->len err:%d\n",fot_var.remain_len);
            fot_dev_notify_sta(FOT_ERR_DATA_LEN);
            return;
        }
        recv_data_len = len-DATA_START_POS;
        memcpy(&fot_var.total_len,&buf[FOT_DATA_LEN_POS],4);
        fot_var.remain_len = fot_var.total_len - recv_data_len;
        memcpy(&addr,&buf[FOT_ADDR_POS],4);
        memcpy(&fot_data[fot_var.data_pos],&buf[DATA_START_POS],recv_data_len);
        fot_var.data_pos += recv_data_len;
        if(fot_var.remain_len == 0){
            fot_var.fot_recv_ok = 1;
            fot_var.data_pos = 0;
        }
    }else if(buf[FOT_CMD_POS] == FOT_OUT_DATA_CONTINUE){
        recv_data_len = len-DATA_CONTINUE_POS;
        if(fot_var.remain_len < recv_data_len){
           recv_data_len =  fot_var.remain_len;
        }
        fot_var.remain_len -= recv_data_len;
        memcpy(&fot_data[fot_var.data_pos],&buf[DATA_CONTINUE_POS],recv_data_len);
        fot_var.data_pos += recv_data_len;
        if(fot_var.remain_len == 0){
            fot_var.fot_recv_ok = 1;
            fot_var.data_pos = 0;
        }
    }
}



AT(.text.fot.cache)
void bsp_fot_process(void)
{
    if(fot_sta & FOT_STA_START){
        if(fot_var.fot_recv_ok){
            //FOT_DEBUG("--->fot_recv_ok\n");
            fot_var.fot_recv_ok = 0;
            fot_write(fot_data, fot_get_curaddr(), fot_var.total_len);

            if(is_fot_update_success()){
                FOT_DEBUG("--->fot update success\n");
                fot_flag |= (FOT_FLAG_UPDATE_OK | FOT_FLAG_SYS_RESET);
                fot_dev_notify_sta(FOT_UPDATE_DONE);
                fot_var.tick = tick_get();
            }else{
                fot_dev_notify_sta(fot_get_err());
            }
        }
    }

    if(fot_flag & FOT_FLAG_SYS_RESET){
        if(tick_check_expire(fot_var.tick,3000)){
            fot_flag &= ~FOT_FLAG_SYS_RESET;
            FOT_DEBUG("-->fota update ok,sys reset\n");
            bt_nor_delete_link_info();
            WDT_RST();
        }
    }
}

AT(.text.fot.cache)
void bsp_usb_fot_process(void)
{
    if(fot_var.fot_recv_ok){
        fot_var.fot_recv_ok = 0;
        fot_write(fot_data, fot_get_curaddr(), fot_var.total_len);

        if(is_fot_update_success()){
            printk("--->fot update success\n");
            fot_flag |= (FOT_FLAG_UPDATE_OK | FOT_FLAG_SYS_RESET);
            fot_dev_notify_sta(FOT_UPDATE_DONE);
            fot_var.tick = tick_get();
        }else{
            printk("--->fot  ack:  %x \n",fot_get_err());
        }
    }

    if(fot_flag & FOT_FLAG_SYS_RESET){
        if(tick_check_expire(fot_var.tick,3000)){
            fot_flag &= ~FOT_FLAG_SYS_RESET;
            printk("-->fota update ok,sys reset\n");
            bt_nor_delete_link_info();
            WDT_RST();
        }
    }
}

AT(.com_text.fot)
u8 fot_app_connect_auth(uint8_t *packet, uint16_t size)
{
    if(!fot_sta){
        if(size == 7 && !memcmp(fot_auth_data, packet, 7)){     //sizeof(fot_auth_data)
            fot_flag |= FOT_FLAG_APP_CONNECT;
            bsp_fot_init();
        }
    }

    return fot_sta;
}

AT(.com_text.fot)
bool bsp_fot_is_connect(void)
{
    return (fot_sta>0);
}

#endif  //FOT_EN
