/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

#ifndef _UDS_H_
#define _UDS_H_

/*! \brief Contains public interface to various functions related
 *         to the Unified Diagnostic Services (UDS) following ISO 14229
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include "stimer.h"
#include "isotp.h"

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
 * the defines
 ******************************************************************************/

#define NRC_GENERAL_REJECT                          (0x10)
#define NRC_SERVICE_NOT_SUPPORTED                   (0x11)
#define NRC_SUBFUNCTION_NOT_SUPPORTED               (0x12)
#define NRC_INVALID_MESSAGE_LENGTH_OR_FORMAT        (0x13)
#define NRC_RESPONSE_TOO_LONG                       (0x14)
#define NRC_CONDITIONS_NOT_CORRECT                  (0x22)
#define NRC_REQUEST_SEQUENCE_ERROR                  (0x24)
#define NRC_REQUEST_OUT_OF_RANGE                    (0x31)
#define NRC_SECURITY_ACCESS_DENIED                  (0x33)
#define NRC_INVALID_KEY                             (0x35)
#define NRC_EXCEEDED_NUMBER_OF_ATTEMPTS             (0x36)
#define NRC_REQUIRED_TIME_DELAY_NOT_EXPIRED         (0x37)
#define NRC_UPLOAD_DOWNNLOAD_NOT_ACCEPTED           (0x70)
#define NRC_TRANSFER_DATA_SUSPENDED                 (0x71)
#define NRC_GENERAL_PROGRAMMING_FAILURE             (0x72)
#define NRC_WRONG_BLOCK_SEQUENCE_COUNTER            (0x73)
#define NRC_SERVICE_BUSY                            (0x78)
#define NRC_SUBFUNC_NOT_SUPPORTED_IN_ACTIVE_SESSION (0x7E)
#define NRC_SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION (0x7F)
#define NRC_PGM_VOLTAGE_OVER_HIGH                   (0x92)
#define NRC_PGM_VOLTAGE_UNDER_LOW                   (0x93)

#define POSITIVE_RSP                                (0x40)
#define NEGATIVE_RSP                                (0x7F)
#define UDS_GET_POSITIVE_RSP(sid)                   (POSITIVE_RSP + sid)

#define UDS_MAX_PAYLOAD_SIZE                        (1026)

#define UDS_SA_NONE                                 (0)
#define UDS_SA_LEVEL_1                              (0x01)
#define UDS_SA_LEVEL_3                              (0x03)
#define UDS_SA_LEVEL_09                             (0x09)
#define UDS_SA_LEVEL_63                             (0x63)

#define UDS_SESSION_DEFAULT                         (0x01)
#define UDS_SESSION_PROGRAMMING                     (0x02)
#define UDS_SESSION_EXTENDED                        (0x03)
#define UDS_SESSION_SAFTY                           (0x04)
#define UDS_SESSION_SUPPLIER                        (0x60)

#define UDS_SERVICE_NUM                             (12)
#define UDS_SA_LEVEL_NUM                            (3)

#define UDS_GET_SUB_FUNCTION(byte)                  ((byte)&0x7fu)

#define BYTES_OF(x)                                 (sizeof(x) / sizeof(uint8_t))

#define UDS_RSP_LEN_MAX                             (512)

/* DID type */
#define UDS_VAR_TYPE_NONVOL_STORAGE                 (1 << 0) /* set if non-volatile data */
#define UDS_VAR_TYPE_MAY_READ                       (1 << 1) /* set if readable */
#define UDS_VAR_TYPE_MAY_WRITE                      (1 << 2) /* set if writable */
#define UDS_DID_TYPE_RAM_RW                         (UDS_VAR_TYPE_MAY_READ | UDS_VAR_TYPE_MAY_WRITE)
#define UDS_DID_TYPE_NVM_RW                         (UDS_VAR_TYPE_NONVOL_STORAGE | UDS_VAR_TYPE_MAY_READ | UDS_VAR_TYPE_MAY_WRITE)
#define UDS_DID_TYPE_NVM_WO                         (UDS_VAR_TYPE_NONVOL_STORAGE | UDS_VAR_TYPE_MAY_WRITE)
#define UDS_DID_TYPE_NVM_RO                         (UDS_VAR_TYPE_NONVOL_STORAGE | UDS_VAR_TYPE_MAY_READ)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief Forward declaration of UDS object
 */
struct _UdsType_;
typedef struct _UdsType_ UdsType;


/*! \brief The Data-by-ID definition of UDS
 */
typedef struct _Uds_DidType_
{
    uint16_t did;
    uint8_t  type;
    uint8_t  sessionLevel;
    uint8_t  securityLevel;
    uint8_t *pBytes;
    uint16_t length;
    void (*function)(UdsType *obj,uint8_t dataBuf[], uint8_t dataLen);
} Uds_DidType;

/*! \brief The definition of UDS DID table
 */
typedef Uds_DidType *Uds_DidTable;


/*! \brief UDS address type supported
 */
typedef struct _Uds_AddressSupportType
{
    bool funcAddrSupported;
    bool phyAddrSupported;
} Uds_AddressSupportType;

/*! \brief UDS service definition
 */
typedef struct _Uds_ServiceType_
{
    uint8_t                sid;
    uint8_t                minLen;
    bool                   subFuncOwner;
    Uds_AddressSupportType defaultSessionAddrInfo;
    Uds_AddressSupportType extendSessionAddrInfo;
    Uds_AddressSupportType programSessionAddrInfo;

    void (*service)(UdsType *obj, const uint8_t msgBuf[], uint16_t msgLen);
} Uds_ServiceType;

/*! \brief The definition of UDS service table
 */
typedef const Uds_ServiceType *Uds_ServiceTable;

/*! \brief The definition of UDS security table
 * \note The table should be like table[SID number][security level number]
 */
typedef const bool *Uds_SecurityTableType;

/*! \brief The definition of UDS initialization parameters
 */
typedef struct _Uds_ParamsType_
{
    IsoTp_Params isotpParams;
    uint16_t     p2Server_ms;
    uint16_t     p2xServer_10ms;
    uint16_t     s3Server_ms;
} Uds_ParamsType;

/*! \brief The UDS prototype definition
 */
struct _UdsType_
{
    bool                  active;
    uint8_t               session;
    uint8_t               securityLevel;
    uint32_t              serviceNum;
    Uds_ServiceTable      seviceTable;
    Uds_SecurityTableType securityTable;
    Uds_DidTable          didTable;
    uint16_t              didNum;

    IsoTpType isotp;
    bool      suppressPosRsp;
    bool      isFuncAddr;
    uint8_t   curServiceIdx;
    uint8_t   payload[UDS_MAX_PAYLOAD_SIZE];

    int64_t timeStamp_ms;

    uint16_t p2Server_ms;
    uint16_t p2xServer_10ms;
    uint16_t s3Server_ms;

    StimerType p2ServerTimer;
    StimerType p2xServerTimer;
    StimerType s3ServerTimer;
};

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the function prototypes
 ******************************************************************************/

/*! \brief  Initializes the UDS object.
 */
extern void Uds_Init(UdsType *obj, const Uds_ParamsType *pParams);

/*! \brief The main service of UDS object.
 * \note Suggest run every 1ms
 */
extern void Uds_Run(UdsType *obj);

/*! \brief This function gives a positive response
 */
static inline void Uds_PositiveResponse(UdsType *obj, const uint8_t data[], uint16_t len)
{
    Stimer_Start(&obj->s3ServerTimer, obj->s3Server_ms);
    if(obj->suppressPosRsp == true)
    {
        return;
    }
    IsoTp_Send(&obj->isotp, data, len);
    return;
}

/*! \brief This function gives a nagative response
 */
static inline void Uds_NegativeResponse(UdsType *obj, uint8_t sid, uint8_t rsp_nrc)
{
    uint8_t temp_buf[8] = {0};

    if(rsp_nrc != NRC_SERVICE_BUSY)
    {
        Stimer_Start(&obj->s3ServerTimer, obj->s3Server_ms);
    }

    if(obj->isFuncAddr && (rsp_nrc == NRC_SERVICE_NOT_SUPPORTED || rsp_nrc == NRC_SUBFUNCTION_NOT_SUPPORTED || rsp_nrc == NRC_REQUEST_OUT_OF_RANGE || rsp_nrc == NRC_SUBFUNC_NOT_SUPPORTED_IN_ACTIVE_SESSION || rsp_nrc == NRC_SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION))
    {
        return;
    }

    temp_buf[0] = NEGATIVE_RSP;
    temp_buf[1] = sid;
    temp_buf[2] = rsp_nrc;
    IsoTp_Send(&obj->isotp, temp_buf, 3);
}

/*! \brief This function provide the time base for the UDS process
 */
static inline void Uds_Tick(UdsType *obj)
{
    obj->timeStamp_ms++;

    Stimer_Tick(&obj->p2ServerTimer);
    Stimer_Tick(&obj->p2xServerTimer);
    Stimer_Tick(&obj->s3ServerTimer);
}

#ifdef __cplusplus
}
#endif /* extern "C" */

#endif /* _UDS_H_ */
