/* 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 _DIAG_H_
#define _DIAG_H_

#include "diag_config.h"
#include "drv_debug_port.h"
#include "osi_compiler.h"

OSI_EXTERN_C_BEGIN

#define LOG_TAG_DIAG OSI_MAKE_LOG_TAG('D', 'I', 'A', 'G')

#define DIAG_MCU_READ_MAX_SIZE 2048
#define DIAG_ENC_DATA_STATIC_SIZE (DIAG_MCU_READ_MAX_SIZE << 1)

/**
 * @brief diag command header
 *
 * The diag command header is common. The data after the heade is
 * called diag command data. The diag command header and the followed
 * command data is called command packet. \p len is the total length
 * of diag packet.
 *
 * When pointer of \p diagMsgHead_t is used ad parameter, it can be safely
 * assumed that the pointer is 4 bytes aligned. The memory from the
 * beginning of command header with size of \p diagMsgHead_t.len is
 * accessible.
 */
typedef struct diagMsgHead
{
    uint32_t seq_num; ///< Message sequence number, used for flow control
    uint16_t len;     ///< The totoal size of the packet
    uint8_t type;     ///< Main command type
    uint8_t subtype;  ///< Sub command type
} diagMsgHead_t;

/**
 * @brief diag command handle function type
 *
 * @param cmd       the diag command packet
 * @param ctx       caller context
 * @return
 *      - true if the command is handled
 *      - false if the command isn't handled
 */
typedef bool (*diagCmdHandle_t)(const diagMsgHead_t *cmd, void *ctx);

/**
 * \brief diag device type
 */
typedef enum
{
    DIAG_DEVICE_UNKNOWN = 0, ///< unknown
    DIAG_DEVICE_UART,        ///< uart
    DIAG_DEVICE_USERIAL,     ///< usb serial
} diagDevType_t;

/**
 * @brief get diag command data from command header
 *
 * @param cmd       the diag command packet
 * @return      the diag command data
 */
static inline const void *diagCmdData(const diagMsgHead_t *cmd) { return cmd + 1; }

/**
 * @brief get diag command data size from command header
 *
 * @param cmd       the diag command packet
 * @return      the diag command data size
 */
static inline unsigned diagCmdDataSize(const diagMsgHead_t *cmd) { return cmd->len - sizeof(diagMsgHead_t); }

/**
 * \brief diag device name
 *
 * It may be affected by configuration and sysnv.
 *
 * \return diag device name
 */
unsigned diagDeviceName(void);

/**
 * @brief initialize diag module
 */
void diagInit(drvDebugPort_t *port);

/**
 * @brief register diag command handler by type
 *
 * @param type      main command type
 * @param handler   diag command handler
 * @param ctx       caller context
 * @return
 *      - true on success
 *      - false if \p type is invalid
 */
bool diagRegisterCmdHandle(uint8_t type, diagCmdHandle_t handler, void *ctx);

/**
 * @brief unregister diag command type handler
 *
 * @param type      main command type
 * @return
 *      - true on success
 *      - false if \p type is invalid
 */
bool diagUnregisterCmdHandle(uint8_t type);

/**
 * @brief output diag packet
 *
 * \p data should be a complete diag packet.
 *
 * @param data      diag packet pointer
 * @param size      diag packet size
 * @return
 *      - true on success
 *      - false if \p data is NULL or size is zero
 */
bool diagOutputPacket(const void *data, unsigned size);

/**
 * @brief output diag packet, header followed by data
 *
 * \p size is permitted to be zero. That means there are no data.
 *
 * This just to make diag output easier. When there are data following
 * command header, it is not needed to allocate memory for the whole
 * packet, and concate the header and data together.
 *
 * \p len in the header will be changed to \code sizeof(diagMsgHead_t) + size \endcode.
 *
 * @param cmd       diag command header, must be valid
 * @param data      diag data following header, must be valid when \p size if not zero
 * @param size      diag data size
 * @return
 *      - true on success
 *      - false if \p head is NULL
 */
bool diagOutputPacket2(const diagMsgHead_t *cmd, const void *data, unsigned size);

/**
 * @brief output diag packet, header followed by sub-header and data
 *
 * \p sub_header_size is permitted to be zero. That means there are no sub-header.
 *
 * \p size is permitted to be zero. That means there are no data.
 *
 * This just to make diag output easier. When there are sub-header and data
 * following command header, it is not needed to concate them together.
 *
 * \p len in the header will be changed to
 * \code sizeof(diagMsgHead_t) + sub_header_size + size \endcode.
 *
 * @param cmd       diag command header, must be valid
 * @param sub_header        sub-header, must be valid when \p sub_header_size if not zero
 * @param sub_header_size   sub-header size
 * @param size      diag data size
 * @param data      diag data following sub-header, must be valid when \p size if not zero
 * @param size      diag data size
 * @return
 *      - true on success
 *      - false if \p head is NULL
 */
bool diagOutputPacket3(const diagMsgHead_t *cmd, const void *sub_header, unsigned sub_header_size, const void *data, unsigned size);

/**
 * @brief output response for bad command
 *
 * @param cmd       diag command header, must be valid
 */
void diagBadCommand(const diagMsgHead_t *cmd);

OSI_EXTERN_C_END
#endif
