/*
 * sci_boot_protocol.h
 *
 *  Created on: 27/04/2022
 *      Author: alexrayne <alexraynepe196@gmail.com>
 * -----------------------------------------------------------------------
 *      протокол загрузки Renesas SCI boot mode
 *      @sa Renesas RA Family System Specifications for Standard Boot Firmware AppNote
 *          r01an5372eu0100-ra-sys-spec-std-boot-fw.pdf
 */

#ifndef FIRMWARE_SCI_BOOT_PROTOCOL_H_
#define FIRMWARE_SCI_BOOT_PROTOCOL_H_

#include <stdint.h>
#include "lib/portable_endian.h"



enum{
    RBOOTSCI_SOH    = 1,    // Start of command packet
    RBOOTSCI_SOD    = 0x81, // Start of data packet
    RBOOTSCI_ETX    = 3,    // End of packet

    RBOOTSCI_WR_LIMIT   = 256,  //maximum payload size of command
    RBOOTSCI_RD_LIMIT   = 1024, //maximum payload size of ack
};

typedef enum RBOOTSCI_CmdID{
    RBOOTSCI_INQUIRY    = 0,    // Inquiry command.     Return ACK (to determine the current phase)
    RBOOTSCI_ERASE      = 0x12, // Erase command        Erase data on target area
    RBOOTSCI_WR         = 0x13, // Write command        Write data on target area
    RBOOTSCI_RD         = 0x15, // Read command         Read data on target area
    RBOOTSCI_ID         = 0x30, // ID authentication command  Authenticate ID for connection with the device
    RBOOTSCI_BAUD       = 0x34, // Baud rate setting command. Set baud rate for UART
    RBOOTSCI_SIGN       = 0x3A, // Signature request command.  Get signature information
    RBOOTSCI_AREA       = 0x3B, // Area information request command.    Get area information
} RBOOTSCI_CmdID;

typedef enum RBOOTSCI_AckID{
    RBOOTSCI_ACK_OK     = 0,
    RBOOTSCI_ACK_ERR    = 0x80,                         //

    RBOOTSCI_ERR_UNSUPPORTED_CMD= 0x40 | RBOOTSCI_ACK_ERR,
    RBOOTSCI_ERR_PACKET         = 0x41 | RBOOTSCI_ACK_ERR,  //  Illegal length, Missing ETX, and so forth
    RBOOTSCI_ERR_CRC            = 0x42 | RBOOTSCI_ACK_ERR,
    RBOOTSCI_ERR_FLOW           = 0x43 | RBOOTSCI_ACK_ERR,
    RBOOTSCI_ERR_ADR            = 0x50 | RBOOTSCI_ACK_ERR,
    RBOOTSCI_ERR_BAUD           = 0x54 | RBOOTSCI_ACK_ERR,  // Baud rate margin error
    RBOOTSCI_ERR_PROTECTION     = 0x5A | RBOOTSCI_ACK_ERR,
    RBOOTSCI_ERR_ID             = 0x5B | RBOOTSCI_ACK_ERR,  // ID mismatch error
    RBOOTSCI_ERR_DISABLE        = 0x5C | RBOOTSCI_ACK_ERR,  // Serial programming disable error

    /* For Erase command or Write command, boot firmware checks the status registers of the flash sequencer
     * (FACI or FCB) and returns the following status.
     */
    RBOOTSCI_ERR_ERASE          = 0x61 | RBOOTSCI_ACK_ERR,  // If ERERR is detected
    RBOOTSCI_ERR_WR             = 0x62 | RBOOTSCI_ACK_ERR,  // If PRGERR or PRGERR01 are detected
    RBOOTSCI_ERR_SEQ            = 0x67 | RBOOTSCI_ACK_ERR,  // If ILGLERR or EILGLERR are detected
} RBOOTSCI_AckID;



_Pragma("pack(push,1)")

typedef struct RBOOT_SCIFrameHead {
    uint8_t             soh;
    uint8_t             lenh;
    uint8_t             lenl;
} RBOOT_SCIFrameHead;

static inline
unsigned rboot_sci_frameload_len(const RBOOT_SCIFrameHead* x){
    return (x->lenh <<8) + x->lenl;
}

static inline
void rboot_sci_frameload_len_assign(RBOOT_SCIFrameHead* x, unsigned len){
    x->lenh = len >>8;
    x->lenl = len & 0xff;
}



typedef struct RBOOT_SCIFrameTail {
    uint8_t             cks;
    uint8_t             etx;
} RBOOT_SCIFrameTail;


typedef struct RBOOT_SCIFrame {
    RBOOT_SCIFrameHead      head;

    union {
        uint8_t                 data[0];

        RBOOT_SCIFrameTail     tail;
    };

} RBOOT_SCIFrame;


static inline
unsigned rboot_sci_frame_len(const RBOOT_SCIFrameHead* x){
    return rboot_sci_frameload_len(x) + sizeof(RBOOT_SCIFrame);
}

static inline
void rboot_sci_frame_len_assign(RBOOT_SCIFrameHead* x, unsigned len){
    rboot_sci_frameload_len_assign(x, len - sizeof(RBOOT_SCIFrame) );
}



typedef struct RBOOT_SCIFrameCmdHead {
    RBOOT_SCIFrameHead head;
    uint8_t             cmd;
} RBOOT_SCIFrameCmdHead;

typedef struct RBOOT_SCIFrameAckHead {
    RBOOT_SCIFrameHead head;
    uint8_t             res;    // return command code |  RBOOTSCI_OK/ERR
    uint8_t             sts;    // status code                  @sa RBOOTSCI_AckID
} RBOOT_SCIFrameAckHead;


static
unsigned rboot_sci_cmd_len(const RBOOT_SCIFrameCmdHead* cmd){
    const RBOOT_SCIFrameHead* x= &(cmd->head);
    return (x->lenh <<8) + x->lenl;
}

static
unsigned rboot_sci_ack_len(const RBOOT_SCIFrameAckHead* cmd){
    const RBOOT_SCIFrameHead* x= &(cmd->head);
    return (x->lenh <<8) + x->lenl;
}



typedef struct RBOOT_SCIFrameCmd {
    RBOOT_SCIFrameCmdHead  head;
    RBOOT_SCIFrameTail     tail;
} RBOOT_SCIFrameCmd;

typedef struct RBOOT_SCIFrameAck {
    RBOOT_SCIFrameAckHead  head;
    RBOOT_SCIFrameTail     tail;
} RBOOT_SCIFrameAck;


typedef struct RBOOT_SCIFrameAdr {
    RBOOT_SCIFrameCmdHead  head;

    uint32_net_t            start;
    uint32_net_t            end;

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCIFrameAdr;


typedef struct RBOOT_SCIAckData {
    RBOOT_SCIFrameCmdHead      head;

    union {
        uint8_t                 data[0];

        RBOOT_SCIFrameTail     tail;
    };

} RBOOT_SCIAckData;

_Pragma("pack(pop)")



//------------------------------------------------------------------------------------

typedef RBOOT_SCIFrameCmd   RBOOT_SCICmdInquiry;
typedef RBOOT_SCIFrameAdr   RBOOT_SCICmdErase;
typedef RBOOT_SCIFrameAdr   RBOOT_SCICmdRd;
typedef RBOOT_SCIFrameAdr   RBOOT_SCICmdWr;
typedef RBOOT_SCIAckData    RBOOT_SCICmdWrData;


//------------------------------------------------------------------------------------

_Pragma("pack(push,1)")
typedef struct RBOOT_SCICmdID {
    RBOOT_SCIFrameCmdHead  head;

    union {
        uint8_t             raw[16];
        uint32_net_t        w[4];
    }                       id;

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCICmdID;
_Pragma("pack(pop)")

/// this ID proposed to use for all chip erase
#define RBOOT_SCI_ID_ALLERASE   "ALeRASE"


//------------------------------------------------------------------------------------

_Pragma("pack(push,1)")
typedef struct RBOOT_SCICmdBaud {
    RBOOT_SCIFrameCmdHead  head;

    uint32_net_t           baud;   // net order (BE)

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCICmdBaud;
_Pragma("pack(pop)")



//------------------------------------------------------------------------------------
typedef RBOOT_SCIFrameCmd  RBOOT_SCICmdSign;

_Pragma("pack(push,1)")
typedef struct RBOOT_SCIAckSign {
    RBOOT_SCIFrameHead     head;
    uint8_t                 res;

    uint32_net_t            sci;    // SCI operating clock frequency [Hz]. net order (BE)
    uint32_net_t            rmb;    // Recommended maximum UART baud rate of the device [bps]

    /** Number of recordable areas
            Example: If device has following areas
            0. User area in Code flash (1st)
            1. User area in Code flash (2nd)
            2. User area in Data flash
            3. Config area
            -> 0x04
     * */
    uint8_t                 noa;

    /** type code
            0x02 (RA MCU + RA2/RA4 Series)
            0x03 (RA MCU + RA6 Series)     *
     * */
    uint8_t                 typ;

    uint16_net_t            bfv;    // Boot firmware version.  Example: Ver10.8 -> 0x0A, 0x08

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCIAckSign;
_Pragma("pack(pop)")



//------------------------------------------------------------------------------------


typedef enum RBOOT_SCIAreaID{
    RBOOTSCI_AREA_CODE  = 0,        // (User area in Code flash)
    RBOOTSCI_AREA_DATA  = 1,        // (User area in Data flash)
    RBOOTSCI_AREA_CONF  = 2,        // (Config area)
} RBOOT_SCIAreaID;


_Pragma("pack(push,1)")

typedef struct RBOOT_SCICmdArea {
    RBOOT_SCIFrameCmdHead  head;

    uint8_t                 narea;   // Area number [0–NOA-1]

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCICmdArea;


typedef struct RBOOT_SCIAckArea {
    RBOOT_SCIFrameHead     head;
    uint8_t                 res;

    /* Kind of area
        0x00 (User area in Code flash)
        0x01 (User area in Data flash)
        0x02 (Config area)
     * */
    uint8_t                 koa;        // @sa RBOOT_SCIAreaID
    uint32_net_t            start;
    uint32_net_t            end;
    uint32_net_t            sector_size;
    uint32_net_t            page_size;

    RBOOT_SCIFrameTail     tail;
} RBOOT_SCIAckArea;

_Pragma("pack(pop)")



//////////////////////////////////////////////////////////////////////////////////////////
///

typedef union RBOOT_SCIAnyFrame{
    RBOOT_SCIFrameHead  head;

    RBOOT_SCIFrame      data;
    RBOOT_SCIFrameCmd   cmd;
    RBOOT_SCIFrameAck   ack;
    RBOOT_SCIFrameAdr   adr;

    RBOOT_SCICmdInquiry echo;
    RBOOT_SCICmdErase   erase;
    RBOOT_SCICmdRd      rd;
    RBOOT_SCIAckData    ack_data;
    RBOOT_SCICmdWr      wr;
    RBOOT_SCICmdWrData  wr_data;
    RBOOT_SCICmdID      id;
    RBOOT_SCICmdBaud    baud;
    RBOOT_SCIAckSign    sign;
    RBOOT_SCICmdArea    area;
    RBOOT_SCIAckArea    ack_area;
} RBOOT_SCIAnyFrame;


/// @return >= 0 - valid frame length
/// @return < 0  - bad frame
int rboot_sci_frame_is_valid(const void* frame);
int rboot_sci_ack_is_valid(const void* ack, uint8_t cmd);

/// @brief подписывает sck фрефма, валидирует что он корректный
/// @return >= 0 - valid frame length
int rboot_sci_frame_build(void* frame);

/// @brief строит фрейм SOH
/// @return >= 0 - valid frame length
int rboot_sci_cmd_build(void* frame);

/// @brief строит фрейм SOD
/// @return >= 0 - valid frame length
int rboot_sci_data_build(void* frame);


#define RBOOT_FRAME_SIZE(data_len)  ( (data_len) + sizeof(RBOOT_SCIFrame) )



#endif /* FIRMWARE_SCI_BOOT_PROTOCOL_H_ */
