/*
 * em_vango_v9260.h
 *
 *  Created on: 10/08/2021
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - 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.
   - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. *
  ------------------------------------------------------------------------
      VangoTech Energy-meter AFE V9260 chips UART API
 */

#include "em_vango_v9260.h"
#include "ptx.h"
#include "os_system.h"
#include <project-conf.h>
// provide : process_poll
#include "OsProcess.h"



/////////////////////////////////////////////////////////////////////////////////
#if 1
#include <assert.h>
#define ASSERT(...) assert(__VA_ARGS__)
#else
#define ASSERT(...)
#endif



//////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <trace_probes.h>

#ifndef trace_v9260_isr
trace_need(v9260_isr);
trace_need(v9260_wr);
trace_need(v9260_rd);
trace_need(v9260_to);
trace_need(v9260_wait);
trace_need(v9260_ver);
trace_need(v9260_retry);
#endif


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

/// @return mask of verifyable on write bits of register id
uint32_t    v9260_reg_mask_ver(enum V9260AddrID id);




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

#pragma pack(push, 1)

enum V9260Cmd{
    V9260CMD_RD        = 1,
    V9260CMD_WR        = 2,
};
typedef enum V9260Cmd   V9260Cmd;

enum {

    // RX reset need at least 64ms
    V9260_RX_RESET_MS       = 64,

    // header byte
    V9260HEAD_MARK          = 0xfe,

    V9260CTRL_CMD_Msk       = 3,    // @sa V9260Cmd

    V9260CTRL_ADR_Msk       = 0x0C,
    V9260CTRL_ADR_Pos       = 2,

    V9260CTRL_REG_Msk       = 0xF0,
    V9260CTRL_REG_Pos       = 4,
};

union V9260Msg{
    struct {
        uint8_t     mark;
        union {
            struct{
                uint8_t cmd :2; // 0 - write, 1 - read
                uint8_t adr :2; // chip A0A1 selector
                uint8_t regh:4; // register high adress
            };
            uint8_t     ctrl;
        };
        union {
            uint8_t     reg;        // WR cmd gives 1st reg adr here
            uint8_t     rd_cnt;    // RD ack provides amount of regs data
        };
        uint32_t    data;
        uint8_t     crc;        //< = 0x33 + ~(CMD + Data Byte 0 + Data Byte 1 + Data Byte 2 + DataByte 3 )
    };
    uint8_t     raw[8];
};
typedef union V9260Msg V9260Msg;

#pragma pack(pop)



//#define V9260_MSG_BYTES(regs)   ( sizeof(V9260Msg) + 4*(regs-1))

// write command bytes
#define V9260_WR_BYTES(regs)    V9260_MSG_BYTES( regs )
// write command acknowledge bytes
#define V9260_ACK_BYTES()       4

#define V9260_ASK_BYTES()       ( sizeof(V9260Msg) )
#define V9260_RD_BYTES(regs)    V9260_MSG_BYTES( regs )


static
V9260Adr v9260_msg_adr(V9260Msg* frame){
    return frame->reg | (frame->regh << 8);
}

static
uint8_t* v9260_msg_crc( const void* data, unsigned len){
    V9260Msg* frame = (V9260Msg*)data;
    return frame->raw + len-1;
}

static
uint8_t v9260_frame_crc( const void* data, unsigned len){
    V9260Msg* frame = (V9260Msg*)data;
    unsigned sum = 0;
    for (const uint8_t* px = frame->raw ; len > 1; --len )
        sum += *px++;
    return  0x33 + ~sum;
}

static
bool is_v9260_ack_ok( const void* msg ){
    V9260Msg* frame = (V9260Msg*)msg;
    uint8_t crc = v9260_msg_crc(frame, V9260_ACK_BYTES())[0];
    return v9260_frame_crc(msg, V9260_ACK_BYTES() ) == crc;
}

static
bool is_v9260_frame_ok( const void* msg, unsigned msg_limit ){
    V9260Msg* frame = (V9260Msg*)msg;

    unsigned len = V9260_WR_BYTES(frame->rd_cnt);
    if (len >= msg_limit)
        return false;

    uint8_t crc = v9260_msg_crc(frame, len )[0];
    return v9260_frame_crc(msg, len ) == crc;
}

static
uint8_t v9260_msg_ctrl( V9260Dev* self, V9260Cmd cmd){
    return (cmd & V9260CTRL_CMD_Msk)
            | ( (self->io.cs << V9260CTRL_ADR_Pos) & V9260CTRL_ADR_Msk )
            | ( ( (self->task.adr >> 8) << V9260CTRL_REG_Pos) & V9260CTRL_REG_Msk )
            ;
}

/// @brief позиция фрейма в буфере обмена, подобрана так чтобы поле data фрейма
///         было выровнено на self->io.w[1] - где приложение может легко его посмотреть
static inline
V9260Msg* v9260_frame_buf(V9260Dev* self){
    return (V9260Msg*)(self->io.buf+1);
}

static inline
unsigned v9260_frame_limit(V9260Dev* self){
    return sizeof(self->io.buf)-1;
}

static
const void* v9260_msg_src(V9260Dev* self){
    if (self->task.src != NULL)
        return self->task.src;
    else
        return &self->task.data;
}



/////////////////////////////////////////////////////////////////////////////////
enum V9260STateId{
    V9260_STATE_FAILED  = -1,
    V9260_STATE_IDLE    = 0,

    V9260_STATE_RES,
    V9260_STATE_RES_WAIT,

    V9260_STATE_RD,
    V9260_STATE_WR,
    V9260_STATE_VER,
    V9260_STATE_SEND,
};

void v9260_init(V9260Dev* self){
    self->task.state = V9260_STATE_IDLE;
}

PTResult    v9260_wait(V9260Dev* self){
    if (self->task.state == V9260_STATE_IDLE)
        return ptOK;
    if (self->task.state < V9260_STATE_IDLE)
        return ptNOK;
    return ptWAITING;
}

static inline
bool v9260_in_progress(V9260Dev* self){
    return (self->task.state > V9260_STATE_IDLE);
}

static
void v9260_signal_to_me(V9260Dev* this){
    this->task.signal = PROCESS_CURRENT();
}

static
void v9260_signal_disable(V9260Dev* this){
    this->task.signal = NULL;
}

static
void v9260_finished_ok(V9260Dev* self){
    self->task.state    = V9260_STATE_IDLE;
    v9260_finished(self);
}

static
void v9260_finished_fail(V9260Dev* self){
    self->task.state    = V9260_STATE_FAILED;
    v9260_finished(self);
}





DevResult   v9260_reset(V9260Dev* self){
    if ( v9260_in_progress(self) )
        return DEV_BUSY;

    v9260_rx_reset(true);
    self->task.state = V9260_STATE_RES;

    os_timeout_t*   to = &(self->io.rd_to);
    ostimeout_init(to, CLOCKMS_MORE(V9260_RX_RESET_MS) );

    v9260_signal_to_me(self);

    return DEV_OK;
}


static
DevResult   v9260_send_msg(V9260Dev* self, unsigned len){
    V9260Msg* frame = v9260_frame_buf(self);

    int sent = self->io.write(self, frame, len );

    if (sent == (int)len ) {
        os_timeout_t*   to = &(self->io.rd_to);
        ostimeout_init(to, V9260IO_TIMEOUT(self, len) );
        return DEV_OK;
    }

    ++(self->stats.wrio_fail);

    if (sent < 0)
        return sent;
    return DEV_NOK;
};

static
DevResult v9260_msg_ask(V9260Dev* self){
    V9260Msg* frame = v9260_frame_buf(self);
    frame->mark = V9260HEAD_MARK;
    frame->ctrl = v9260_msg_ctrl(self, V9260CMD_RD);
    frame->reg  = (uint8_t)self->task.adr;

    unsigned cnt = self->task.cnt;
    if (cnt > V9260IO_BUF_REGS)
        cnt = V9260IO_BUF_REGS;
    frame->data = cnt;

    self->io.bufneed    = V9260_RD_BYTES(cnt);
    self->io.bufcnt     = 0;

    frame->crc  = v9260_frame_crc( frame, V9260_ASK_BYTES() );

    return v9260_send_msg(self, V9260_ASK_BYTES());
}

DevResult   v9260_ask(V9260Dev* self, V9260Adr adr, void* dst, unsigned cnt ){

    if (v9260_in_progress(self))
        return DEV_BUSY;

    self->task.adr = adr & V9260REG_Msk;
    self->task.cnt = cnt;
    self->task.dst = (uint32_t*)dst;
    self->task.state = V9260_STATE_RD;
    self->task.retry = V9260IO_RETRY;
    v9260_signal_to_me(self);

    return v9260_msg_ask(self);
}


static
DevResult v9260_msg_post(V9260Dev* self){
    V9260Msg* frame = v9260_frame_buf(self);
    frame->mark = V9260HEAD_MARK;
    frame->ctrl = v9260_msg_ctrl(self, V9260CMD_WR);
    frame->reg  = (uint8_t)self->task.adr;

    const uint32_t* src = v9260_msg_src(self);
    unsigned msgregs;

    if (src){
        unsigned cnt = self->task.cnt;
        if (cnt > V9260IO_BUF_REGS)
            cnt = V9260IO_BUF_REGS;
        msgregs = cnt;

        for (uint32_t* dst = &frame->data ; cnt > 0; --cnt)
            *dst++ = *src++;
    }
    else {
        frame->data = self->task.data;
        msgregs = 1;
    }

    self->io.bufneed    = V9260_ACK_BYTES();
    self->io.bufcnt     = 0;

    int len = V9260_WR_BYTES(msgregs);
    uint8_t crc = v9260_frame_crc(frame, len );
    v9260_msg_crc(frame, len)[0] = crc;

    return v9260_send_msg(self, len);

}

DevResult   v9260_post(V9260Dev* self, V9260Adr adr, const uint32_t* src, unsigned cnt ){
    if (v9260_in_progress(self))
        return DEV_BUSY;

    self->task.adr = adr & V9260REG_Msk;
    self->task.cnt = cnt;
    self->task.src = src;

    if ( (adr & V9260REG_VERIFY) != 0 )
        self->task.state = V9260_STATE_WR;
    else
        self->task.state = V9260_STATE_SEND;

    self->task.retry = V9260IO_RETRY;
    self->task.retry_wr = V9260IO_RETRY;
    v9260_signal_to_me(self);

    return v9260_msg_post(self);
}


DevResult   v9260_post1(V9260Dev* self, V9260Adr adr, uint32_t data){
    if (v9260_in_progress(self))
        return DEV_BUSY;

    self->task.adr = adr & V9260REG_Msk;
    self->task.cnt = 1;
    self->task.src = NULL;
    self->task.data = data;

    if ( (adr & V9260REG_VERIFY) != 0 )
        self->task.state = V9260_STATE_WR;
    else
        self->task.state = V9260_STATE_SEND;

    self->task.retry = V9260IO_RETRY;
    self->task.retry_wr = V9260IO_RETRY;
    v9260_signal_to_me(self);

    return v9260_msg_post(self);
}

static
PTResult v9260_retry_ask(V9260Dev* self){
    if (self->task.cnt == 0) {
        v9260_finished_ok(self);
        return ptOK;
    }

    //
    if (self->task.retry <= 0){
        ++(self->stats.rd_fail);
        v9260_finished_fail(self);
        return ptNOK;
    }

    if (self->task.retry < V9260IO_RETRY){
        ++(self->stats.rd_retry);
    }

    DevResult ok = v9260_msg_ask(self);
    if (ok == DEV_OK)
        return ptWAITING;
    return ok;
}

static
PTResult v9260_on_rd(V9260Dev* self){
    unsigned have = self->io.bufcnt;
    unsigned need = self->io.bufneed;

    if (self->task.retry > 0)
        --(self->task.retry);

    if (need <= have) {
        // accept ack

        V9260Msg* frame = v9260_frame_buf(self);
        unsigned len = V9260_RD_BYTES(frame->rd_cnt);
        if (need >= len) {
            if ( is_v9260_frame_ok(frame, v9260_frame_limit(self) ) ) {
                // check received regs adr:
                if ( frame->regh == (self->task.adr >> 8) ){
                    // accept received registers
                    unsigned cnt = frame->rd_cnt;
                    if (cnt > self->task.cnt)
                        cnt = self->task.cnt;

                    if (self->task.dst)
                        memcpy(self->task.dst, &frame->data, 4*cnt);
                    self->task.dst += cnt;
                    self->task.adr += cnt;
                    self->task.cnt -= cnt;
                    self->task.retry = V9260IO_RETRY;

                    ++(self->stats.rd);
                }
                else {
                    ;// some other register, drop it and retry
                }
            }
            else {
                ;// crc fails
            }
        }
        else {
            ;// have ack len, more then requres, drop it and retry
        }
    }
    return v9260_retry_ask(self);
}


/// @brief verify writen contents, ignoring no-writable fields
static
bool v9260_regs_cmp(V9260Adr adr
                    , const uint32_t* a, const uint32_t* b, unsigned count)
{
    for (; count > 0; --count, ++adr, ++a, ++b){
        uint32_t rw = v9260_reg_mask_ver(adr);
        if ( ((*a ^ *b) & rw) != 0 )
            return false;
    }
    return true;
}

static
PTResult v9260_on_wrver(V9260Dev* self){
    unsigned have = self->io.bufcnt;
    unsigned need = self->io.bufneed;

    if (self->task.retry > 0)
        --(self->task.retry);

    if (need <= have) {
        // accept ack

        V9260Msg* frame = v9260_frame_buf(self);
        unsigned len = V9260_ACK_BYTES();
        if (need >= len) {
            if ( is_v9260_frame_ok(frame, v9260_frame_limit(self)) ) {
                // check received regs adr:
                if ( frame->regh == (self->task.adr >> 8) ){
                    // accept received registers
                    unsigned cnt = frame->rd_cnt;
                    if (cnt > self->task.cnt)
                        cnt = self->task.cnt;

                    if ( v9260_regs_cmp(self->task.adr, v9260_msg_src(self), &frame->data, cnt) ){
                        // verifyed weel.
                        ++(self->stats.wr);

                        self->task.dst += cnt;
                        self->task.adr += cnt;
                        self->task.cnt -= cnt;
                        self->task.retry    = V9260IO_RETRY;
                        self->task.retry_wr = V9260IO_RETRY;
                        self->task.state    = V9260_STATE_WR;
                    }
                    else {
                        // verify fails, rewrite again
                        --(self->task.retry_wr);
                        if (self->task.retry_wr > 0) {
                            ++(self->stats.wr_retry);
                            self->task.state = V9260_STATE_WR;
                            self->task.retry = V9260IO_RETRY;
                        }
                        else {
                            ++(self->stats.wr_fail);
                            v9260_finished_fail(self);
                            return ptNOK;
                        }
                    }

                    if (self->task.cnt <= 0){
                        v9260_finished_ok(self);
                        return ptOK;
                    }

                    DevResult ok = v9260_msg_post(self);
                    if (ok == DEV_OK)
                        return ptWAITING;
                    return ok;
                }
                else {
                    ;// some other register, drop it and retry
                }
            }
            else {
                ;// crc fails
            }
        }
        else {
            ;// have ack len, more then requres, drop it and retry
        }
    }

    return v9260_retry_ask(self);
}


static
PTResult v9260_retry_post(V9260Dev* self){
    if (self->task.cnt == 0) {
        v9260_finished_ok(self);
        return ptOK;
    }

    //
    if (self->task.retry <= 0){
        ++(self->stats.wr_fail);
        v9260_finished_fail(self);
        return ptNOK;
    }

    if (self->task.retry_wr < V9260IO_RETRY){
        ++(self->stats.wr_retry);
    }

    DevResult ok = v9260_msg_post(self);
    if (ok == DEV_OK)
        return ptWAITING;
    return ok;
}

static
PTResult v9260_on_send(V9260Dev* self){
    unsigned have = self->io.bufcnt;
    unsigned need = self->io.bufneed;

    if (self->task.retry > 0)
        --(self->task.retry);

    if (need <= have) {
        // accept ack

        V9260Msg* frame = v9260_frame_buf(self);
        unsigned len = V9260_ACK_BYTES();
        if (need >= len) {
            if ( is_v9260_ack_ok(frame) ) {
                // check received regs adr:
                if (v9260_msg_adr(frame)  == self->task.adr)
                {
                    // accept writen registers
                    unsigned cnt = self->task.cnt;
                    if (cnt > V9260IO_BUF_REGS)
                        cnt = V9260IO_BUF_REGS;

                    self->task.src += cnt;
                    self->task.adr += cnt;
                    self->task.cnt -= cnt;
                    self->task.retry_wr = V9260IO_RETRY;

                    ++(self->stats.wr);
                }
                else {
                    ;// some other register, drop it and retry
                }
            }
            else {
                ;// crc fails
            }
        }
        else {
            ;// have ack len, more then requres, drop it and retry
        }
    }
    return v9260_retry_post(self);
}



static
PTResult v9260_on_wr(V9260Dev* self){
    unsigned have = self->io.bufcnt;
    unsigned need = self->io.bufneed;

    if (self->task.retry > 0)
        --(self->task.retry);

    if (need <= have) {
        // accept ack

        V9260Msg* frame = v9260_frame_buf(self);
        unsigned len = V9260_ACK_BYTES();
        if (need >= len) {
            if ( is_v9260_ack_ok(frame) ) {
                // check received regs adr:
                if (v9260_msg_adr(frame)  == self->task.adr)
                {
                    if ( v9260_reg_mask_ver(self->task.adr) != 0 ) {
                        // ask verify writen registers
                        self->task.state = V9260_STATE_VER;
                        self->task.retry = V9260IO_RETRY;

                        DevResult ok = v9260_msg_ask(self);
                        if (ok == DEV_OK)
                            return ptWAITING;
                        return ok;
                    }
                    else {
                        // register is ro/clear only, no need verify
                        return ptOK;
                    }
                }
                else {
                    ;// some other register, drop it and retry
                }
            }
            else {
                ;// crc fails
            }
        }
        else {
            ;// have ack len, more then requres, drop it and retry
        }
    }

    return v9260_retry_post(self);
}



PTResult    v9260_receive(V9260Dev* self){

    // IO operation is online?
    if (self->task.state < V9260_STATE_RD)
        return ptNOK;

    if (self->io.bufneed <= 0)
        return ptBUSY; // ??? ptNOK

    unsigned have = self->io.bufcnt;
    unsigned need = self->io.bufneed;
    if (need > have) {
        uint8_t* buf = (uint8_t*)v9260_frame_buf(self);
        // await ack from device
        int recv = self->io.read(self, buf + have, (need - have) );
        if (recv > 0){
            have += recv;
            self->io.bufcnt = have;

            if (buf[0] != V9260HEAD_MARK){
                //cleanup from trash
                uint8_t* mark = memchr(buf, V9260HEAD_MARK, have);
                if (mark != NULL){
                    have -= (mark - buf);
                    memmove(buf, mark, have);
                }
                else {
                    have = 0;
                    self->io.bufcnt = 0;
                }
            }
        }
        else if (recv < 0){
            // error here!
            ++(self->stats.rdio_fail);

            // finish expected frame on error, and make received frame corrupted
            self->io.bufcnt = self->io.bufneed;
            V9260Msg* frame = v9260_frame_buf(self);
            frame->ctrl = 0xff;

            return ptOK;
        }
    }

    os_timeout_t*   to = &(self->io.rd_to);
    if (need > have) {
        if ( ostimeout_waiting(to) )
            return ptWAITING;

        //ostimeout_stop(to);

        // operation timed out. retry?
        if (self->task.retry <= 0){
            v9260_finished_fail(self);
            return ptNOK;
        }

    }
    else {
        ostimeout_stop(to);
    }

    return ptOK;
}

PTResult    v9260_process(V9260Dev* self){
    if (self->task.state == V9260_STATE_IDLE)
        return ptOK;
    if (self->task.state < V9260_STATE_IDLE)
        return ptNOK;

    os_timeout_t*   to = &(self->io.rd_to);
    if (self->task.state == V9260_STATE_RES){
        if ( ostimeout_waiting(to) )
            return ptWAITING;

        v9260_rx_reset(false);

        // timeout after reset, for chip startup
        ostimeout_init(to, CLOCKMS(2200) );
        self->task.state = V9260_STATE_RES_WAIT;

        return ptWAITING;
    }
    else if (self->task.state == V9260_STATE_RES_WAIT){
        if ( ostimeout_waiting(to) )
            return ptWAITING;

        v9260_finished_ok(self);
        return ptOK;
    }

    PTResult ok = v9260_receive(self);
    if (ok != ptOK)
        return ok;

    switch ( (int) self->task.state) {
        case V9260_STATE_IDLE:  return ptOK;

        case V9260_STATE_RD:    return v9260_on_rd(self);

        case V9260_STATE_WR:    return v9260_on_wr(self);

        case V9260_STATE_VER:   return v9260_on_wrver(self);

        case V9260_STATE_SEND:  return v9260_on_send(self);

        default:
            self->task.state    = V9260_STATE_IDLE;
            return ptNOK;
    }
    return ptOK;
}

void v9260_abort(V9260Dev* self){
    self->task.retry    = 1;
    self->task.retry_wr = 1;
    self->task.cnt      = 0;
}





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

struct V9260RegMask {
    V9260Adr        adr;
    uint32_t        nwbits;
};

#define V9260_REG_MASKS(a, nowrite) { .adr = (a), .nwbits = (nowrite) }

const struct V9260RegMask v9260_reg_masks[] = {
    V9260_REG_MASKS(V9260_SYSCTRL,  ~V9260_SYSCTRL_VER_Msk   ),
    V9260_REG_MASKS(V9260_ANACTRL2, V9260_ANACTRL2_RESERVE_Msk),
    V9260_REG_MASKS(V9260_MTPARA0,  V9260_MTPARA0_RESERVE_Msk),
    V9260_REG_MASKS(V9260_MTPARA1,  V9260_MTPARA1_RESERVE_Msk),
};

/// @return mask of verifyable on write bits of register id
///         it lookups id in v9260_reg_masks.
///         by default all register is writable
uint32_t    v9260_reg_mask_ver(enum V9260AddrID id){
    const struct V9260RegMask* msk = v9260_reg_masks;
    for (unsigned cnt = ARRAY_COUNT(v9260_reg_masks) ;  cnt > 0; ++msk, --cnt) {
        if (msk->adr == id){
            return ~msk->nwbits;
        }
    }
    return ~0ul;
}
