/* Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
 * All rights reserved.
 *
 * This software is supplied "AS IS" without any warranties.
 * RDA assumes no responsibility or liability for the use of the software,
 * conveys no license or title under any patent, copyright, or mask work
 * right to the product. RDA reserves the right to make changes in the
 * software without notification.  RDA also make no representation or
 * warranty that such application will be suitable for the specified use
 * without further testing or modification.
 */

#ifndef _FUPDATE_H_
#define _FUPDATE_H_

#include "osi_compiler.h"
#include "fupdate_config.h"

OSI_EXTERN_C_BEGIN

// The following APIs are called in application

/**
 * \brief firmware update pack file name
 *
 * The default value is \p FUPDATE_PACK_FILE_NAME. And it can be defined
 * as other value to override the default value. When overrided, it
 * should be the same as bootloader.
 */
extern const char *gFupdatePackFileName;

/**
 * \brief firmware update stage file name
 *
 * The default value is \p FUPDATE_STAGE_FILE_NAME. And it can be defined
 * as other value to override the default value. When overrided, it
 * should be the same as bootloader.
 */
extern const char *gFupdateStageFileName;

/**
 * \brief firmware update temporal file name
 *
 * The default value is \p FUPDATE_TEMP_FILE_NAME. And it can be defined
 * as other value to override the default value. When overrided, it
 * should be the same as bootloader.
 */
extern const char *gFupdateTempFileName;

/**
 * \brief firmware update signature file name
 *
 * The default value is \p FUPDATE_SIGN_FILE_NAME. And it can be defined
 * as other value to override the default value. When overrided, it
 * should be the same as bootloader.
 */
extern const char *gFupdateSignFileName;

/**
 * \brief firmware update status
 */
typedef enum fupdateStatus
{
    FUPDATE_STATUS_NOT_READY, ///< not ready
    FUPDATE_STATUS_READY,     ///< ready
    FUPDATE_STATUS_FINISHED,  ///< update finished
} fupdateStatus_t;

/**
 * \brief firmware update content type
 */
typedef enum fupdateContentType
{
    FUPDATE_CONTENT_INVALID = -1, ///< no content updating
    FUPDATE_CONTENT_OTHERS,       ///< in updating, but not one of the followings
    FUPDATE_CONTENT_FLASH,        ///< flash updating
} fupdateContentType_t;

/**
 * \brief flash updating content
 */
typedef struct
{
    uint32_t device_name; ///< flash device name
    unsigned offset;      ///< flash offset
} fupdateFlashContent_t;

/**
 * \brief progress callback function type
 *
 * During firmware update in bootloader, this callback will be called.
 * For pre-callback, it will be called before the block will be processed.
 * For post-callback, it will be called after the block is processed.
 *
 * \param block_count   total block count
 * \param block         current block
 */
typedef void (*fupdateProgressCallback_t)(int block_count, int block);

/**
 * \brief get current update content type
 *
 * It can be called in pre-callback progress function.
 *
 * \return
 *      - current updating content type
 */
fupdateContentType_t fupdateGetUpdatingType(void);

/**
 * \brief get current flash updating content
 *
 * It can be called in pre-callback progress function.
 *
 * \return
 *      - true on success
 *      - false on error, invalid parameter or not in flash updating
 */
bool fupdateGetFlashUpdatingContent(fupdateFlashContent_t *content);

/**
 * \brief get firmware update status
 *
 * The status is just get from update status file. And it won't perform
 * thorough update pack file content check.
 *
 * \return      update status
 */
fupdateStatus_t fupdateGetStatus(void);

/**
 * \brief get firmware update version information
 *
 * The old version and new version are embedded inside update pack.
 *
 * The version strings are specified at update pack creation. It is
 * suggestion just use them as trace or report information.
 *
 * \p old_version and \p new_version are allocated inside. Caller should
 * free them after used. When there are no version string inside update
 * pack, the returned pointer will be NULL.
 *
 * \param old_version   return pointer for old version
 * \param new_version   return pointer for new version
 * \return
 *      - true on success
 *      - false on error
 *          - out of memory, or invalid parameter
 *          - status is \p FUPDATE_STATUS_NONE
 */
bool fupdateGetVersion(char **old_version, char **new_version);

/**
 * \brief invalidate firmware update
 *
 * Invalidate firmware update status. \p gFupdateStageFileName will be
 * removed, \p gFupdateTempFileName will be removed, and
 * \p gFupdatePackFileName can be removed optionally.
 *
 * After previous update is finished, it is recommended to call this,
 * and remove update pack file, to release spaces in file system.
 *
 * It will be ensured that \p fupdateGetStatus will return
 * \p FUPDATE_STATUS_NONE after this is called.
 *
 * \param removePack    true to remove update pack file
 */
void fupdateInvalidate(bool removePack);

/**
 * \brief check whether firmware update file is valid
 *
 * It will perform thorough update pack file content. So, it will take
 * times.
 *
 * When \p curr_version is not NULL, it will try to check current version
 * with old version string embedded in update pack. When old version
 * string in update pack is not NULL also, and \p curr_version doesn't
 * match old version in update pack, the update pack will be regarded as
 * invalid.
 *
 * It is optional to embed version string inside update pack.
 *
 * \param curr_version  current version string, NULL for not to check
 * \return
 *      - true if the update pack file content is valid
 *      - false if not
 */
bool fupdateIsPackValid(const char *curr_version);

/**
 * \brief set firmware update to ready status
 *
 * Inside this function, the update pack file will be thoroughly checked.
 *
 * When the update pack file is valid:
 * - \p gFupdateStageFileName will be updated to indicate ready status;
 * - \p gFupdateTempFileName will be removed;
 *
 * When the update pack file is invalid:
 * - \p gFupdateStageFileName will be removed;
 * - \p gFupdateTempFileName will be removed;
 *
 * It must be called after the update pack file is written and closed. If
 * the pack file is modified after \p fupdateSetReady is called,
 * bootloader will still try to update. Though bootloader can detected
 * update pack invalid and not start real update, it will waste boot
 * time.
 *
 * No matter whether \p fupdateIsPackValid is called, the update pack file
 * will be verified inside. So, it will take even longer.
 *
 * When \p curr_version is not NULL, it will try to check current version
 * with old version string embedded in update pack. When old version
 * string in update pack is not NULL also, and \p curr_version doesn't
 * match old version in update pack, the update pack will be regarded as
 * invalid.
 *
 * It is optional to embed version string inside update pack.
 *
 * \param curr_version  current version string, NULL for not to check
 * \return
 *      - true if update pack file is valid
 *      - false if not
 */
bool fupdateSetReady(const char *curr_version);

/**
 * \brief get the required temp file system space of firmware update pack file if it 
 * can be successfully updated
 *
 * It will perform thorough update pack file content. So, it will take some time.
 *
 * \return
 *      - >0 if the update pack file content is valid and the space size is calcated
 *      - -1 if check failed
 */
int fupdateGetRequiredSpace(void);

/**
 * \brief firmware update result
 */
typedef enum fupdateResult
{
    FUPDATE_RESULT_NOT_READY,   ///< not ready
    FUPDATE_RESULT_CANNT_START, ///< failed to start
    FUPDATE_RESULT_FAILED,      ///< started and failed
    FUPDATE_RESULT_FINISHED,    ///< finished
} fupdateResult_t;

/**
 * \brief perform firmware update
 *
 * It must be called in bootloader. Before \p fupdateRun is called, file
 * system should be initializaed.
 * - file system for \p CONFIG_FS_FOTA_DATA_DIR
 * - file system to be patched
 *
 * File system layout and mount points must be the same as application.
 *
 * \p FUPDATE_RESULT_NOT_READY means that \p gFupdateStageFileName doesn't
 * exists, or not indicates \p FUPDATE_STATUS_READY. Bootloader shall boot
 * application normally.
 *
 * \p FUPDATE_RESULT_CANNT_START means that \p gFupdateStageFileName
 * indicates \p FUPDATE_STATUS_READY, however the update pack is not
 * valid or not enough memory can be allocated for upgraded. It shouldn't
 * happen, but the current firmware isn't changed. Bootloader can boot
 * application normally. And it is suggested to notify application to
 * invalidate update status.
 *
 * \p FUPDATE_RESULT_FAILED means that current firmware is changed, and
 * encounter unrecoverable error. Typical case is that power supply is
 * unstable. Due to current firmware is changed, bootloader shall **NEVER**
 * boot application normally. It is suggested that bootloader shall reboot
 * and try again.
 *
 * \p FUPDATE_RESULT_FINISHED means that current firmware is successfully
 * updated. Bootloader can boot application normally, or even better reboot.
 * At reboot, \p fupdateRun will return \p FUPDATE_RESULT_NOT_READY.
 *
 * When \p progress callback is not NULL, it will be called after each block
 * is finished.
 *
 * \param progress      progress callback after block is processed
 * \return firmware update result
 */
fupdateResult_t fupdateRun(fupdateProgressCallback_t progress);

/**
 * \brief perform firmware update
 *
 * It is similar to \p fupdateRun, just \p pre_callback can be specified.
 *
 * \param pre_callback      progress callback before block will be processed
 * \param post_callback     progress callback after block is processed
 * \return firmware update result
 */
fupdateResult_t fupdateRunV2(fupdateProgressCallback_t pre_callback, fupdateProgressCallback_t post_callback);


OSI_EXTERN_C_END
#endif
