/*
 * can_device.cpp
 *
 *  Created on: 8 нояб. 2018 г.
 *      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. *
 * ------------------------------------------------------
 *  реализауия  АПИ КАН контролере миландра
 */

#include <assert.h>
#include <can_device.hpp>
#include <string.h>
#include <mcu_nvic.h>
#include <mcu_gpio.h>
#include <mcu_rcc.h>
#include <mcu_can.h>
#include <system.h>
#include <hw.h>
#include <gpio.h>
#include <os_isr.h>
#include <OsTime.h>

const
NVIC_InitTypeDef    CAN1_NVIC = {
      struct_field(NVIC_IRQChannel)                     CAN1_IRQn
    , struct_field(NVIC_IRQChannelPreemptionPriority)   IRQPRIO(IRQP_CAN)
    , struct_field(NVIC_IRQChannelSubPriority)          IRQPRIO(IRQP_CAN)
    , struct_field(NVIC_IRQChannelCmd)                  ENABLE
};

const
NVIC_InitTypeDef    CAN2_NVIC = {
      struct_field(NVIC_IRQChannel)                     CAN2_IRQn
    , struct_field(NVIC_IRQChannelPreemptionPriority)   IRQPRIO(IRQP_CAN)
    , struct_field(NVIC_IRQChannelSubPriority)          IRQPRIO(IRQP_CAN)
    , struct_field(NVIC_IRQChannelCmd)                  ENABLE
};


const
MDR32_CANDevice::PORT_INIT CAN1C_init = {
          struct_field(CANx)        MDR_CAN1
        , struct_field(NVIC_Init)   &CAN1_NVIC

        , struct_field(RCC_Perifery) (RST_CLK_PCLK_CAN1 | RST_CLK_PCLK_PORTC)

        , struct_field(GPIOPINS) {
             struct_field(GPIO_PORT) MDR_PORTC
            , struct_field(GPIO_PINS) (PORT_Pin_8 | PORT_Pin_9)
            , struct_field(GPIO_FUNC) PORT_FUNC_MAIN
        }
        , struct_field(PINID_RX)    9
};

const
MDR32_CANDevice::PORT_INIT CAN2C_init = {
          struct_field(CANx)        MDR_CAN2
        , struct_field(NVIC_Init)   &CAN2_NVIC

        , struct_field(RCC_Perifery) (RST_CLK_PCLK_CAN2 | RST_CLK_PCLK_PORTC)

        , struct_field(GPIOPINS) {
              struct_field(GPIO_PORT) MDR_PORTC
            , struct_field(GPIO_PINS) (PORT_Pin_14 | PORT_Pin_15)
            , struct_field(GPIO_FUNC) PORT_FUNC_OVERRID
        }
        , struct_field(PINID_RX)    14
};

const
MDR32_CANDevice::PORT_INIT CAN1D_init = {
          struct_field(CANx)        MDR_CAN1
        , struct_field(NVIC_Init)   &CAN1_NVIC

        , struct_field(RCC_Perifery) (RST_CLK_PCLK_CAN1 | RST_CLK_PCLK_PORTD)

        , struct_field(GPIOPINS) {
              struct_field(GPIO_PORT) MDR_PORTD
            , struct_field(GPIO_PINS) (PORT_Pin_13 | PORT_Pin_14)
            , struct_field(GPIO_FUNC) PORT_FUNC_OVERRID
          }
          , struct_field(PINID_RX)    14
};

const
MDR32_CANDevice::PORT_INIT CAN2D_init = {
          struct_field(CANx)        MDR_CAN2
        , struct_field(NVIC_Init)   &CAN2_NVIC

        , struct_field(RCC_Perifery) (RST_CLK_PCLK_CAN2 | RST_CLK_PCLK_PORTD)

        , struct_field(GPIOPINS) {
              struct_field(GPIO_PORT) MDR_PORTD
            , struct_field(GPIO_PINS) (PORT_Pin_9 | PORT_Pin_15)
            , struct_field(GPIO_FUNC) PORT_FUNC_MAIN
        }
          , struct_field(PINID_RX)    15
};

const
CAN_InitTypeDef CAN80M_500K = {
        struct_field(CAN_ROP)   DISABLE
    ,   struct_field(CAN_SAP)   DISABLE
    ,   struct_field(CAN_STM)   DISABLE
    ,   struct_field(CAN_ROM)   DISABLE
    ,   struct_field(CAN_PSEG)  CAN_PSEG_Mul_1TQ
    ,   struct_field(CAN_SEG1)  CAN_SEG1_Mul_3TQ
    ,   struct_field(CAN_SEG2)  CAN_SEG2_Mul_3TQ
    ,   struct_field(CAN_SJW)   CAN_SJW_Mul_2TQ
    ,   struct_field(CAN_SB)    CAN_SB_3_SAMPLE
    ,   struct_field(CAN_BRP)   (80/4-1)
    ,   struct_field(CAN_OVER_ERROR_MAX)    15
};



#include <trace_probes.h>
#ifndef trace_isr_can
trace_need(isr_can)
trace_need(isr_can_err)
trace_need(isr_can_senterr)
trace_need(isr_can_sent)
trace_need(isr_can_recv)
trace_need(isr_can_abort)
#endif
//----------------------------------------------------------------------------
MDR32_CANDevice::MDR32_CANDevice()
:inherited()
, io(NULL)
, port_def(NULL)
, can_err_cnt(0)
{
    memset(on_slot, 0, sizeof(on_slot));
}

int MDR32_CANDevice::init(const DEV_INIT* x){
    port_def = x;
    if (port_def == NULL)
        return DEV_NOK;

    const PORT_INIT* init_state = port_def->port;
    io = init_state->CANx;
    RST_CLK_PCLKcmd(init_state->RCC_Perifery , ENABLE);

    if(init_state->GPIOPINS.port){
        // настраиваю RX для unhang_poll
        gpio_conf_pout_oc(init_state->GPIOPINS.port, (1<<init_state->PINID_RX));
        gpio_off(init_state->GPIOPINS.port, (1<<init_state->PINID_RX));

        // включаю функцию КАН порта на пинах
        gpio_conf_func(&init_state->GPIOPINS);
    }

    /* Enable Interrupt */
    if(init_state->NVIC_Init)
        NVIC_Init(init_state->NVIC_Init);

    can_init();

    int ok = init();
    return ok;
}

//virtual
DevResult MDR32_CANDevice::connect(addr_t address
        , recv_handle ev_recv, void* ev_self)
{
    // настрою 2 приемных буфера чтобы иметь возможность принимать
    //  пакеты без опасений их переписывания
    //allocate recv1 buffer;
    unsigned recv1_bufid = alloc_recvbuf(address, ev_recv, ev_self);
    if (!IS_CAN_BUFFER(recv1_bufid) )
        return DEV_NOK;
    unsigned recv2_bufid = alloc_recvbuf(address, ev_recv, ev_self);
    if (!IS_CAN_BUFFER(recv2_bufid) )
        return DEV_NOK;
    return DEV_OK;
}

unsigned MDR32_CANDevice::alloc_recvbuf(unsigned adress
        , recv_handle ev_recv, void* ev_self)
{
    unsigned bufid = CAN_GetDisabledBuffer(io);
    assert(IS_CAN_BUFFER(bufid));
    assign_filter(bufid, adress);

    slot_h& s = on_slot[bufid];
    s.adr       = adress;
    s.on_recv   = ev_recv;
    s.arg       = ev_self;

    /* Enable CAN1 interrupt from receive buffer */
    CAN_RxITConfig( io ,(1<<bufid), ENABLE);
    /* receive buffer enable */
    CAN_Receive(io, bufid, OVERRIDE_DISABLE);
  	return bufid;
}

void MDR32_CANDevice::assign_filter(unsigned bufid, unsigned adress){
    CAN_FilterInitTypeDef filter;
    filter.Filter_ID = CAN_STDID_TO_EXTID(adress);
    filter.Mask_ID   = ~0u;
    CAN_FilterInit(io, bufid, &filter);
}

//virtual
DevResult MDR32_CANDevice::writeData(addr_t address, const void *data, size_t size){
    if (size > 8)
        return DEV_NOK;
    if (data == NULL){
        return writeAbort(address);
    }

    unsigned bufid = send_bufid(address);
    //if alredy have sending on io address, just terminate it and resend
    if (bufid >= slots_limit) {
        bufid = alloc_sendbuf();
        if (bufid >= slots_limit)
            return DEV_NOK;
    }

    DevResult ok = DEV_OK;
    if ((io->BUF_CON[bufid] & CAN_BUF_CON_TX_REQ) == 0)
        on_good(CAN_STATUS_TX_READY);
    else {
        bool may_drop = false;
        if ((on_slot[bufid].adr & adrMASK) == (address & adrMASK)) {
            may_drop = (address & adrMAY_ABSENT != 0);
        }
        if (!may_drop) {
        // if current BUF not emty = not finished send - there is a timeout. so rise an error
        on_error(CAN_STATUS_TX_READY);
        ok = DEV_BUSY;
        }
        else
            on_good(CAN_STATUS_TX_READY);
    }

    CAN_TxMsgTypeDef buf;
    buf.IDE      = CAN_ID_STD;
    buf.ID       = CAN_STDID_TO_EXTID(address);
    buf.DLC      = size;
    buf.PRIOR_0  = DISABLE;
    memcpy(buf.Data, data, size);

    CAN_Transmit(io, bufid, &buf);
    on_slot[bufid].adr = address;
    /* Enable CAN1 interrupt from transmit buffer */
    CAN_TxITConfig( io ,(1<<bufid), ENABLE);
    return ok;
}

unsigned MDR32_CANDevice::alloc_sendbuf(){
    // alloc new send buffer if need
    unsigned bufid = CAN_GetEmptyTransferBuffer(io);
    if (!IS_CAN_BUFFER(bufid)){
        bufid = CAN_GetDisabledBuffer(io);
        if (!IS_CAN_BUFFER(bufid)){
            return bufid;
        }
        io->BUF_CON[bufid] = CAN_BUF_CON_EN;
    }
    return bufid;
}

// \return  - sending slot for <adress>
unsigned MDR32_CANDevice::send_bufid(unsigned adress){
    unsigned id = CAN_STDID_TO_EXTID(adress);
    for (int i = 0; i < slots_limit; i++){
        if (on_slot[i].on_recv == NULL)
        if (io->CAN_BUF[i].ID == id)
        if (is_slot_active(i))
                return i;
    }
    return bufidNONE;
}

//virtual
DevResult MDR32_CANDevice::writeAbort(addr_t address){
    unsigned id = CAN_STDID_TO_EXTID(address);
    for (int i = 0; i < slots_limit; i++){
        slot_h& s = on_slot[i];
        if (s.on_recv != NULL)
            continue;
        if (s.adr == address){
            s.err_cnt = 0;
        }
        if (io->CAN_BUF[i].ID == id){
            can_abort(i);
            //CAN_BufferRelease(io, i);
        }
    }
    return bufidNONE;
}

//  abort all sends - terminate all transmitions. and cleanup error counts.
//virtual
DevResult MDR32_CANDevice::writeAbort(void){
    for (int i = 0; i < slots_limit; i++){
        slot_h& s = on_slot[i];
        if (s.on_recv != NULL)
            continue;
        if (!is_sending(i))
            continue;
        can_abort(i);
    }
    return bufidNONE;
}

enum {
    CAN_STATUS_ANY_ERR =  CAN_STATUS_BIT_ERR
                        //эта ошибка при обрыве линии теряет смысл
                        //| CAN_STATUS_ERROR_OVER
                        | CAN_STATUS_BIT_STUFF_ERR
                        //| CAN_STATUS_CRC_ERR
                        //| CAN_STATUS_FRAME_ERR
                        //| CAN_STATUS_ACK_ERR
};

void MDR32_CANDevice::IRQ(){
    trace_isr_can_on();

    uint32_t status = CAN_GetStatus(io);

    for (int i = 0; i < slots_limit; i++){
        if (on_slot[i].on_recv != NULL){
            if ((status&CAN_STATUS_RX_READY) != 0) {
                if (CAN_GetRxITStatus(io, i) == SET){
                    IRQ_buf_recv(i);
                }
            }
        }
        else if ((status&CAN_STATUS_TX_READY) != 0) {
            if (CAN_GetTxITStatus(io, i) == SET) {
                if (is_slot_send(i)){
                    if ((status & CAN_STATUS_ACK_ERR) != 0){
                        if (io->TXID == io->CAN_BUF[i].ID){
                            can_send_err(i);
                        }
                    }
                    //if (CAN_GetBufferStatus())
                    if (!is_sending(i)){
                        CAN_TxITConfig( io ,(1<<i), DISABLE);
                        trace_isr_can_sent_twist();
                    }
                }
                else //if (is_slot_send(i))
                    CAN_TxITConfig( io ,(1<<i), DISABLE);
            }//if (CAN_GetTxITStatus(io, i) == SET)
        }
    }//for (int i = 0; i < slots_limit

    if ((status & CAN_STATUS_ANY_ERR) != 0){
        on_error(status);
    }
    else if ((status & CAN_STATUS_ERROR_OVER) != 0){

    }
    else {
        /*
        if (err_cnt > 2)
            err_cnt -= 2;
        */
        can_err_cnt = 0;
    }

    CAN_ITClearErrorPendingBit(io, status);

    trace_isr_can_off();
};

// реакция на ошибку отсылки слота
void MDR32_CANDevice::can_send_err(unsigned bufid){
    if (!IS_CAN_BUFFER(bufid))
        return;
    slot_h& s = on_slot[bufid];
    if (io->TXID != io->CAN_BUF[bufid].ID)
        return;
    trace_isr_can_senterr_on();
    s.err_cnt++;
    //err_cnt--;
    //if (0)
    if (s.err_cnt > err_abort_limit){
        // stop transmit
        trace_isr_can_abort_twist();
        can_abort(bufid);
        if ((s.adr & adrMAY_ABSENT) == 0)
            on_error(CAN_STATUS_TX_READY);
    }
    trace_isr_can_senterr_off();
}

void MDR32_CANDevice::can_abort(unsigned bufid){
    // stop transmit
    io->BUF_CON[bufid] &= ~CAN_BUF_CON_TX_REQ;

    // FIX: при отсутствии связи отправляемое сообщение может висеть в передатчике
    //      постоянно, и отсулаться пока его ктото не примет.
    //  чтобы остановить передачу, переведу текущий буфер на прием своего же сообщения

    //slot_h& s = on_slot[bufid];
    //assign_filter(bufid, s.adr);
    io->CAN_BUF_FILTER[bufid].FILTER  = io->CAN_BUF[bufid].ID;
    io->CAN_BUF_FILTER[bufid].MASK    = ~0u;

    CAN_Receive(io, bufid, OVERRIDE_DISABLE);
}

void MDR32_CANDevice::IRQ_buf_recv(unsigned bufid){
    CAN_RxMsgTypeDef RxMessage;
    trace_isr_can_recv_on();
    CAN_GetRawReceivedData(io, bufid, &RxMessage);
    slot_h& s = on_slot[bufid];
#if 0
    if (RxMessage.Rx_Header.ID == CAN_STDID_TO_EXTID(bup_addr()) )
#endif
    {
        s.on_recv(s.arg, s.adr, (u8*)RxMessage.Data, RxMessage.Rx_Header.DLC );
        on_good(CAN_STATUS_RX_READY);
    }
    CAN_ITClearRxTxPendingBit(io, bufid, CAN_STATUS_RX_READY);
    //CAN_Receive(io, bufid, OVERRIDE_DISABLE);
    trace_isr_can_recv_off();
}

//virtual
void MDR32_CANDevice::on_error(unsigned err){
    can_err_cnt++;
    if (can_err_cnt > err_switch_limit){
        can_error_state = errCAN;
        can_err_cnt = err_good_level;
    }
    else
      if (can_err_cnt < err_good_level)
          can_error_state &= ~errCAN;
}

// успешная активность КАН, должна както влиять на счетчики ошибок
void MDR32_CANDevice::on_good(unsigned err){
    trace_isr_can_err_on();
    can_err_cnt -= err_good_price;
    if (can_err_cnt <= 0){
        can_err_cnt = 0;
    }
    if (can_err_cnt < err_good_level)
        can_error_state &= ~errCAN;
    trace_isr_can_err_off();
}


//virtual
int MDR32_CANDevice::init(){
    return DEV_OK;
}

int MDR32_CANDevice::deinit(){
    return can_down();
}

int MDR32_CANDevice::can_init(){
    if (port_def == NULL)
        return DEV_NOK;

    /* CANx configuration*/
    CAN_BRGInit(io, CAN_HCLKdiv1);
    CAN_Init(io, port_def->CAN_Init);
    can_err_cnt = 0;
    // FIX: can_abort использует технику борьбы с железом, требующую ROP
    io->CONTROL |= CAN_CONTROL_ROP;

    // adjust baudrate
    CAN_SetBaud(io, port_def->baud);
    bit1_us    = 1000000ul/port_def->baud;
    CAN_Cmd(io, ENABLE);

    /* Enable CAN1 GLB_INT and RX_INT interrupts */
    CAN_ITConfig( io, CAN_IT_GLBINTEN
                    | CAN_IT_RXINTEN
                    | CAN_IT_TXINTEN
                    //| CAN_IT_ERROVERINTEN
                , ENABLE);
    return DEV_OK;
}

int MDR32_CANDevice::can_down()
{
    CAN_ITConfig( io, CAN_IT_GLBINTEN
                        | CAN_IT_RXINTEN
                        | CAN_IT_TXINTEN
                        | CAN_IT_ERROVERINTEN
                 , DISABLE);
    CAN_Cmd(io, DISABLE);

    for (int i = 0; i < slots_limit; i++){
        slot_h& s = on_slot[i];
        if (s.on_recv != NULL){
            s.on_recv(s.arg, s.adr, NULL, 0);
        }
        else if (is_slot_active(i)){
            CAN_BufferRelease(io, i);
        }
    }//for (int i = 0; i < slots_limit

    CAN_DeInit(io);

    uint32_t  tmpreg = MDR_RST_CLK->CAN_CLOCK;

    if (io == MDR_CAN1)
        tmpreg &= ~RST_CLK_CAN_CLOCK_CAN1_CLK_EN;
    else if (io == MDR_CAN2)
        tmpreg &= ~RST_CLK_CAN_CLOCK_CAN2_CLK_EN;
    MDR_RST_CLK->CAN_CLOCK = tmpreg;

    return DEV_OK;
}

void MDR32_CANDevice::unhang_poll(){
    const GPIOFUNC_INIT* pins = &port_def->port->GPIOPINS;
    // пин RX настроен на доминантный бит.
    // чтобы выставить доминант на линию, включу функцию порта.
    gpio_set_func(pins->port, (1<<port_def->port->PINID_RX), PORT_FUNC_PORT);
    os_delay_usec(bit1_us);
    gpio_set_func(pins->port, (1<<port_def->port->PINID_RX), pins->func);
}
