/*****************************************************************************
 * file    lib/process/pd_upgrade.c  
 * author  WangBo
 * version 1.0.0
 * date    07-Feb-2023
 * brief   This file provides all the softwave upgrade related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2024 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. 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.
 *   3. Neither the name of LandPower 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.
 *
 ******************************************************************************/
 
/* Includes ------------------------------------------------------------------*/
// #ifdef HAVE_CONFIG_H
#include "config.h"
// #endif

#ifdef CFG_DEV_TYPE_LAND_PD
/* 标准 C 库头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <typedef.h>

/* 第三方头文件 */

/* 私有头文件 */
#include "main.h"
#include "pd_upgrade.h"
#include "pd_dau.h"
#include "pd_csg.h"
// #include "pd_dbg.h"

/* Private define ------------------------------------------------------------*/
#define PD_UPG_READ_SIZE (1024*1024)
#define PD_UPG_HARDWARE_VERSON "GIS10.0"

/* Private macro -------------------------------------------------------------*/

/* Private typedef -----------------------------------------------------------*/
typedef void (*pd_upg_sighandler)(int);

/* Private variables ---------------------------------------------------------*/
static pd_upg_ctrl_t pd_upg_ctrl;
static const char *pd_upg_type_str[PD_UPG_SEL_MAX] =
{
    "all",
    "cmu",
    "dau",
};
    
/* Private function prototypes -----------------------------------------------*/

/* Internal functions --------------------------------------------------------*/
/* description: 调用 system 下发 shell 命令
   param: cmd_line -- 需要执行的命令
   return:  */
int _pd_upg_system(const char *cmd_line)
{
   int ret = 0;
   pd_upg_sighandler old_handler;

   old_handler = signal(SIGCHLD, SIG_DFL);
   ret = system(cmd_line);
   signal(SIGCHLD, old_handler);

   return ret;
}

/* description: 提取升级数据写成文件
   param: addr -- 起始地址
          len  -- 大小
          name -- 文件名字
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_write_file(int32_t addr, int32_t size, const char *name)
{
    int fp_wr = -1;
    int fp_rd = -1;
    char *buf = NULL;
    int32_t len = 0;
    int32_t len_wr = 0;
    int32_t len_rd = 0;
    int32_t rv = E_NONE;

    /* 打开文件 */
    fp_wr = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0777);
    if (fp_wr <= 0)
    {
        DBG(DBG_M_PD_UPGRADE, "Open %s failed!\n", name);
        return E_SYS_CALL;
    }

    fp_rd = open(PD_UPG_SOFTWARE, O_RDONLY);
    if (fp_rd <= 0)
    {
        close(fp_wr);
        DBG(DBG_M_PD_UPGRADE, "Open " PD_UPG_SOFTWARE " failed!\n");
        return E_SYS_CALL;
    }

    /* 偏移到读取位置 */
    if (lseek(fp_rd, addr, SEEK_SET) < 0)
    {
        close(fp_wr);
        close(fp_rd);
        rv = E_SYS_CALL;
        DBG(DBG_M_PD_UPGRADE, "Fseek %d failed!\n", addr);
        return E_SYS_CALL;
    }

    /* 申请 buf */
    buf = (char*)XMALLOC_Q(MTYPE_UPGRADE, PD_UPG_READ_SIZE);
    if (!buf)
    {
        close(fp_wr);
        close(fp_rd);
        DBG(DBG_M_PD_UPGRADE, "Malloc failed!\n");
        return E_MEM;
    }

    while (len < size)
    {
        len_rd = read(fp_rd, buf, PD_UPG_READ_SIZE);
        if (len_rd > 0)
        {
            if(len_rd + len > size)
            {
                len_rd = size - len;
            }
            len_wr = write(fp_wr, buf, len_rd);
            if(len_wr != len_rd)
            {
                DBG(DBG_M_PD_UPGRADE, "Write %s error!\n", name);
                rv = E_SYS_CALL;
                break;
            }
        }
        else if(0 == len_rd)
        {
            break;
        }
        else
        {
            DBG(DBG_M_PD_UPGRADE, "Read " PD_UPG_SOFTWARE " error!\n");
            rv = E_SYS_CALL;
            break;
        }
            
        len += len_rd;
    }

    close(fp_rd);
    close(fp_wr);
    XFREE(MTYPE_UPGRADE, buf);
    _pd_upg_system("sync");
    sleep(1);

    return rv;
}

/* description: 升级 CMU
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_upgrade_dau(void)
{
    uint8_t i = 0;
    int32_t offset = 0;
    int32_t lenth = 0;
    int32_t rv = E_ERROR;

    /* 查找升级文件 */
    for (i = 0; i < PD_UPG_AREA_MAX; i++)
    {
        if (0 == pd_upg_ctrl.head.arr_info[i].len)
        {
            continue;
        }

        if (PD_UPG_TYPE_DAU == pd_upg_ctrl.head.arr_info[i].type)
        {
            offset = pd_upg_ctrl.head.arr_info[i].start;
            lenth = pd_upg_ctrl.head.arr_info[i].len;
            break;
        }
    }

    /* 没有找到 */
    if (0 == lenth)
    {
        snprintf(pd_upg_ctrl.msg, 128, "DAU file is not found.");
        DBG(DBG_M_PD_UPGRADE, "DAU file is not found.\n");
        return E_NOT_FOUND;
    }

    /* 读取并写升级文件 */
    rv = _pd_upg_write_file(offset, lenth, PD_UPG_DAU_FILE_BAK);
    if (rv != E_NONE)
    {
        snprintf(pd_upg_ctrl.msg, 128, "DAU write file error.");
        return E_NOT_FOUND;
    }

    if (rename(PD_UPG_DAU_FILE_BAK, PD_UPG_DAU_FILE) != 0)
    {
        snprintf(pd_upg_ctrl.msg, 128, "DAU file rename error.");
        DBG(DBG_M_PD_UPGRADE, "Can't rename file %s!\n", PD_UPG_DAU_FILE_BAK);
        return E_SYS_CALL;
    }

    return E_NONE;
}

/* description: 升级 CMU
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_upgrade_cmu(void)
{
    char cmd[128] = {0};
    uint8_t i = 0;
    int32_t offset = 0;
    int32_t lenth = 0;
    int32_t rv = E_ERROR;

    /* 查找升级文件 */
    for (i = 0; i < PD_UPG_AREA_MAX; i++)
    {
        if (0 == pd_upg_ctrl.head.arr_info[i].len)
        {
            continue;
        }

        if (PD_UPG_TYPE_CMU == pd_upg_ctrl.head.arr_info[i].type)
        {
            offset = pd_upg_ctrl.head.arr_info[i].start;
            lenth = pd_upg_ctrl.head.arr_info[i].len;
            break;
        }
    }

    /* 没有找到 */
    if (0 == lenth)
    {
        snprintf(pd_upg_ctrl.msg, 128, "CMU file is not found.");
        DBG(DBG_M_PD_UPGRADE, "CMU file is not found.\n");
        return E_NOT_FOUND;
    }

    /* 先备份原始文件 */    
    snprintf(cmd, 128, "cp -rf %s bak", PD_UPG_CMU_FILE);
    _pd_upg_system(cmd);
    _pd_upg_system("sync");
    sleep(1);

    /* 读取并写升级文件 */
    rv = _pd_upg_write_file(offset, lenth, PD_UPG_CMU_FILE_BAK);
    if (rv != E_NONE)
    {
        snprintf(pd_upg_ctrl.msg, 128, "CMU write file error.");
        return E_NOT_FOUND;
    }
    
    if (rename(PD_UPG_CMU_FILE_BAK, PD_UPG_CMU_FILE) != 0)
    {
        snprintf(pd_upg_ctrl.msg, 128, "CMU file rename error.");
        DBG(DBG_M_PD_UPGRADE, "Can't rename file %s!\n", PD_UPG_CMU_FILE_BAK);
        return E_SYS_CALL;
    }
    DBG(DBG_M_PD_UPGRADE, "Upgrade CMU Ok.\n");

    return E_NONE;
}

/* description: 升级系统
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_upgrade(void)
{
    if (PD_UPG_SEL_ALL == pd_upg_ctrl.upg_type || PD_UPG_SEL_CMU == pd_upg_ctrl.upg_type)
    {
        LD_E_RETURN(DBG_M_PD_UPGRADE, _pd_upg_upgrade_cmu());
    }

    if (PD_UPG_SEL_ALL == pd_upg_ctrl.upg_type || PD_UPG_SEL_DAU == pd_upg_ctrl.upg_type)
    {
        LD_E_RETURN(DBG_M_PD_UPGRADE, _pd_upg_upgrade_dau());
    }

    return E_NONE;
}

/* description: 检测硬件版本号是否匹配
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_hardware_check(void)
{
    if (strncmp(pd_upg_ctrl.head.hard_ver, PD_UPG_HARDWARE_VERSON, strlen(PD_UPG_HARDWARE_VERSON)))
    {
        DBG(DBG_M_PD_UPGRADE, "Hardware version(%s) not match\n", pd_upg_ctrl.head.hard_ver);
        return E_ERROR;
    }
    
    return E_NONE;
}


/* description: 验证升级文件, CRC 校验升级文件并对比每段分区确定是否需要升级
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_verify_file(void)
{
    int32_t rv = 0;
    int fd = 0;
    unsigned int crc = 0;
    char *buf = NULL;
    
    /*读取升级文件头信息*/
    fd = open(PD_UPG_SOFTWARE, O_RDONLY);
    if (fd < 0)
    {
        DBG(DBG_M_PD_UPGRADE, "Open file "PD_UPG_SOFTWARE" failed!\n");
        return E_SYS_CALL;
    }
    
    rv = read(fd, &pd_upg_ctrl.head, sizeof(pd_upg_head_t));
    if (rv != sizeof(pd_upg_head_t) || pd_upg_ctrl.head.magic != PD_UPG_MAGICNUM)
    {
        close(fd);
        DBG(DBG_M_PD_UPGRADE, "Read len %d(%d), magic %x(%x)\n",
            rv, sizeof(pd_upg_head_t), pd_upg_ctrl.head.magic, PD_UPG_MAGICNUM);
        return E_NOT_IDENTIFY;
    }
    
    /*crc校验*/
    buf = (char*)XMALLOC_Q(MTYPE_UPGRADE, PD_UPG_BLOCK_SIZE);
    if (!buf)
    {
        close(fd);
        DBG(DBG_M_PD_UPGRADE, "Malloc error!\n");
        return E_MEM;
    }
    
    while (1)
    {
        rv = read(fd, buf, PD_UPG_BLOCK_SIZE);
        if (rv < 0)
        {
            DBG(DBG_M_PD_UPGRADE, "Read failed!\n");
            rv = E_SYS_CALL;
            break;
        }
        else if(rv == 0)
        {
            rv = E_NONE;
            break;
        }
        
        crc = crc32(crc, buf, (unsigned long)rv);
    }

    if (E_NONE == rv)
    {
        if (crc != pd_upg_ctrl.head.crc)
        {
            DBG(DBG_M_PD_UPGRADE, "CRC error!\n");
            rv = E_ERROR;;
        }
    }

    /* 释放资源 */
    XFREE(MTYPE_UPGRADE, buf);
    close(fd);
    DBG(DBG_M_PD_UPGRADE, "Verfiy upgrade success.\n");
    return rv;
}

/* description: 检测是否存在满足升级的条件
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_file_check(void)
{
    int32_t rv = 0;
    
    /* 读取升级文件并校验 */
    rv = _pd_upg_verify_file();
    if (rv != E_NONE)
    {
        snprintf(pd_upg_ctrl.msg, 128, "App CRC verify error.");
        return rv;
    }

    /* 硬件版本号检查 */
    rv = _pd_upg_hardware_check();
    if (rv != E_NONE)
    {
        snprintf(pd_upg_ctrl.msg, 128, "Hardware is not match.");
        return rv;
    }
   
    return rv;
}

/* description: 软件升级处理线程
   param: parm -- 控制结构体
   return: */
void *_pd_upg_handle(void *parm)
{
    char cmd[128] = {0};
    int32_t rv = E_ERROR;

    while(1)
    {
        /* 文件检查 */
        rv = _pd_upg_file_check();
        if (rv != E_NONE)
        {
            break;
        }

        /* 升级系统 */
        rv = _pd_upg_upgrade();
        if (rv != E_NONE)
        {
            break;
        }

        break;
    }

    /* 保存升级原始文件 */
    snprintf(cmd, 128, "mv " PD_UPG_SOFTWARE " bak");
    _pd_upg_system(cmd);
    _pd_upg_system("sync");
    sleep(1);

    /* 处理升级结果 */
    if (E_NONE == rv)
    {
        DBG(DBG_M_PD_UPGRADE, "Upgrade ok.\n");
        if (pd_upg_ctrl.upgrade_result)
        {
            pd_upg_ctrl.upgrade_result(1, "update successful.");
        }

        /* 升级成功, 主动重启 */
        log_notice(LOG_UPGRADE, "Upgrade %s ok.", pd_upg_type_str[pd_upg_ctrl.upg_type]);
        system("sync");
        reboot_system(LOG_UPGRADE, REBOOT_UPGRADE_ALL);
    }
    else
    {
        DBG(DBG_M_PD_UPGRADE, "Upgrade failed.\n");
        if (pd_upg_ctrl.upgrade_result)
        {
            pd_upg_ctrl.upgrade_result(0, pd_upg_ctrl.msg);
        }
    }

    pd_upg_ctrl.is_start = FALSE;
    pthread_exit(NULL);
    return NULL;
}

/* description: 启动升级线程
   param:
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t _pd_upg_start(void)
{
    struct sched_param param;
    pthread_attr_t attr;
    pthread_t pid;
    int32_t rv = 0;
    
    /* 创建线程 配置线程 RR 调度, 优先级 50 */
    pthread_attr_init(&attr);
    param.sched_priority = 50;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    rv = pthread_create(&pid, &attr, _pd_upg_handle, NULL);
    if (rv != 0)
    {
        log_err(LOG_UPGRADE, "PD can't create pthread %d!", rv);
        return E_SYS_CALL;
    }
    else
    {
        thread_m_add("UPGRADE", pid);
    }
    pthread_attr_destroy(&attr);

    return E_NONE;    
}

/* Interface functions -------------------------------------------------------*/
/* description: 软件升级开始
   param: from -- 来自谁的升级 PD_UPG_FROM_xxx
          type -- 升级类型 PD_UPG_SEL_xxx
   return: E_NONE - 成功, E_XXXX - 失败 */
int32_t pd_upg_start(uint8_t from, uint8_t type)
{
    int32_t rv = E_NONE;

    if (pd_upg_ctrl.is_start == FALSE)
    {
        DBG(DBG_M_PD_UPGRADE, "Start upgrade system.\n");
    
        pd_upg_ctrl.is_start = TRUE;
        pd_upg_ctrl.upg_from = from;
        pd_upg_ctrl.upg_type = type;
        if (PD_UPG_FROM_CSG == from)
        {
            pd_upg_ctrl.upgrade_result = csg_upgrade_result_send;
        }
        else
        {
            pd_upg_ctrl.upgrade_result = NULL;
        }
        
        /* 初始化模块. */
        LD_E_RETURN(DBG_M_PD_UPGRADE, _pd_upg_start());
    }
    else
    {
        DBG(DBG_M_PD_UPGRADE, "Upgrade is busy!\n");
        rv = E_TIMEOUT;
    }
    
    return rv;
}
#endif
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/
