/*
 * lip_ll_lpc17xx.c
 *  Файл содержит реализацию порта TCP/IP стека lip под микроконтроллеры LPC17xx
 *  В качестве PHY-устройства поддерживаются LAN8720 и KS8721 (последнее давно не проверялось...)
 *
 *  Для передачи данных память не разбита на блоки по максимальному размеру пакета, а используется
 *       непрервыный массив данных заданного размера из которого используется только запрашиваемая часть
 *       для передачи (реально кол-во дескрипторов больше, чем максимальное количество максимального размера
 *       пакетов может вместится в циклический буфер), что позволяет передавать большее кол-во мелких пакетов
 *
 *  На прием используется сплошной буфер, разделенный на части меньшего размера, чем полный пакет.
 *      Количество дескрипторов совпадает с количеством буферов - для приема пакетов большего размера
 *      используется несколько дескрипторов
 *      (последний блок в буфере всегда размера макс. пакета, чтобы данные были последовательны)
 *
 *
 *  Есть поддержка передачи split-пакета - когда часть передается в выделенном буфере, а для часть в отдельном
 *     пользовательском буфере отельным дескриптором (может например использоваться TCP, чтобы не копировать
 *     данные из буфера)
 *
 *  todo:
 *      Multicast включается сразу, если есть поддержка IGMP - мб сделать через hash адресов
 *  Created on: 27.08.2010
 *      Author: borisov
 */
#include "lip/ll/lip_ll.h"
#include <string.h>
#include "LPC17xx.h"
#include "iolpc17XX.h"
#include "lpc17xx_pinfunc.h"
#include "lip/lip_log.h"
#include "lip/phy/lip_phy_mdio_regs_std.h"
#include "lpc17xx_eth_descr.h"
#include "lip/phy/lip_phy.h"
#include "lip/link/mac/lip_mac.h"
#include "lip/link/mac/rx/lip_mac_rx.h"
#include "lip/link/mac/tx/lip_mac_tx.h"



typedef struct {
    uint8_t* addr;
    uint32_t ctl;
} t_eth_tx_descr;

typedef struct {
    uint8_t* addr;
    uint32_t ctl;
} t_eth_rx_descr;

typedef struct {
    uint32_t ctl;
    uint32_t crc_hash;
} t_eth_rx_stat;

typedef struct {
    uint32_t ctl;
} t_eth_tx_stat;

#ifndef LIP_PORT_DESCR_MEM
    #define LIP_PORT_DESCR_MEM(var) var
#endif

#ifndef LIP_PORT_BUF_MEM
    #define LIP_PORT_BUF_MEM(var) var
#endif


/* делитель частоты для интерфейса управления phy (MIIM) */
#define PHY_MDIO_CLK_DIV  0xa

/* количество блоков на прием (оно же - кол-во дескрипторов) */
#define LIP_ETH_RX_BUF_CNT        26
/* размер одного блока */
#define LIP_ETH_RX_BUF_SIZE       256
/* размер последнего блока (=макс. размеру пакета) */
#define LIP_ETH_RX_LAST_BUF_SIZE  3*512

/* кол-во дескрипторов на передачу */
#define LIP_ETH_TX_BUF_CNT      16
/* макс. размер пакета на передачу */
#define LIP_ETH_TX_BUF_SIZE     LIP_MAC_TX_FRAME_MAX_SIZE//1536
/* размер циклического буфера на передачу */
#define LIP_ETH_TX_FULL_BUF_SIZE  (5*LIP_ETH_TX_BUF_SIZE + 256)


#define LSPEC_ALIGNMENT 8
/* массив дескрипторов DMA на прием */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(static t_eth_rx_descr  f_rx_descr[LIP_ETH_RX_BUF_CNT]);
/* массив статусов завершения дескрипторов на прием */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(static t_eth_rx_stat   f_rx_status[LIP_ETH_RX_BUF_CNT]);
/* массив дескрпиторов DMA на передачу */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(static t_eth_tx_descr  f_tx_descr[LIP_ETH_TX_BUF_CNT]);
/* массив статусов завершения передачи */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(static t_eth_tx_stat f_tx_status[LIP_ETH_TX_BUF_CNT]);

typedef struct {
    uint16_t tx_idx;
    t_lip_tx_frame *frame;
} t_tx_frame_ctx;
static t_tx_frame_ctx f_tx_frame_ctx[LIP_ETH_TX_BUF_CNT];





/* массив данных в который сохраняются входные пакеты */
#include "lcspec_align.h"
LIP_PORT_BUF_MEM(static uint8_t f_rx_bufs[(LIP_ETH_RX_BUF_CNT-1)*LIP_ETH_RX_BUF_SIZE + LIP_ETH_RX_LAST_BUF_SIZE]);
/* массив данных в который сохраняются данные для передачи */
#include "lcspec_align.h"
LIP_PORT_BUF_MEM(static uint8_t f_tx_bufs[LIP_ETH_TX_FULL_BUF_SIZE]);


#undef LSPEC_ALIGNMENT


/* позиция, начиная с которой будет положен следующий блок данных на передачу */
static uint16_t f_tx_buf_addr;
/* кол-во дескрипторов, которое использовалось, чтобы принять пакет
 * (используется, чтобы обновить кол-во дескр. в rx_drop */
static uint16_t f_rx_dscr_cnt;
static uint16_t f_tx_frame_finish_pos;


/*================================================================================================*/
/* Collision Window/Retry Register */
#define CLRT_DEF (0x0000370F)               // Default value
/* Non Back-to-Back Inter-Packet-Gap Register  Recommended Value*/
#define IPGR_DEF ((0x12 << LPC_EMAC_IPGR_NON_BACK_TO_BACKINTER_PACKET_GAP_PART2_Pos) | \
    (0x0C << LPC_EMAC_IPGR_NON_BACK_TO_BACKINTER_PACKET_GAP_PART1_Pos))
/* Back-to-Back Inter-Packet-Gap Register */
#define IPGT_FULL_DUP (0x00000015)          /* Recommended value for Full Duplex */
#define IPGT_HALF_DUP (0x00000012)          /* Recommended value for Half Duplex */
#define ETH_FRAG_SIZE       1536        /* Packet Fragment size 1536 Bytes   */
#define ETH_MAX_FLEN        1536        /* Max. Ethernet Frame Size          */






t_lip_errs lip_ll_mdio_write_start(uint8_t phyaddr, uint8_t regaddr, uint16_t value) {
    LPC_EMAC->MADR = ((phyaddr << LPC_EMAC_MADR_PHY_ADDRESS_Pos) & LPC_EMAC_MADR_PHY_ADDRESS_Msk) |
        ((regaddr << LPC_EMAC_MADR_REGISTER_ADDRESS_Pos) & LPC_EMAC_MADR_REGISTER_ADDRESS_Msk);
    LPC_EMAC->MWTD = value;
    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_ll_mdio_read_start(uint8_t phyaddr, uint8_t regaddr) {
    LPC_EMAC->MADR = ((phyaddr << LPC_EMAC_MADR_PHY_ADDRESS_Pos) & LPC_EMAC_MADR_PHY_ADDRESS_Msk) |
        ((regaddr << LPC_EMAC_MADR_REGISTER_ADDRESS_Pos) & LPC_EMAC_MADR_REGISTER_ADDRESS_Msk);
    LPC_EMAC->MCMD = LPC_EMAC_MCMD_READ_Msk;
    return LIP_ERR_SUCCESS;
}

bool lip_ll_mdio_rdy(void) {
    return !(LPC_EMAC->MIND & LPC_EMAC_MIND_BUSY_Msk);
}

t_lip_errs lip_ll_mdio_data(uint16_t *data) {
    LPC_EMAC->MCMD = 0;
    *data = LPC_EMAC->MRDD & 0xFFFF;
    return LIP_ERR_SUCCESS;
}



void f_rx_descr_init(void) {
    int i;
    for (i = 0; i < LIP_ETH_RX_BUF_CNT; i++) {
        f_rx_descr[i].addr = &f_rx_bufs[i*LIP_ETH_RX_BUF_SIZE];
        f_rx_descr[i].ctl = (i==(LIP_ETH_RX_BUF_CNT-1)) ?
                LIP_ETH_RX_LAST_BUF_SIZE - 1 : LIP_ETH_RX_BUF_SIZE - 1;
        f_rx_status[i].ctl = 0;
        f_rx_status[i].crc_hash = 0;
    }

    /* Set EMAC Receive Descriptor Registers. */
    LPC_EMAC->RxDescriptor = (uint32_t)f_rx_descr;
    LPC_EMAC->RxStatus = (uint32_t)f_rx_status;
    LPC_EMAC->RxDescriptorNumber = LIP_ETH_RX_BUF_CNT - 1;

    /* Rx Descriptors Point to 0 */
    LPC_EMAC->RxConsumeIndex = 0;
}

void f_tx_descr_init(void) {
    for (int i = 0; (i < LIP_ETH_TX_BUF_CNT); i++) {
         f_tx_descr[i].addr = f_tx_bufs;
         f_tx_descr[i].ctl = LPC_ETH_DESCR_TXCTL_Last_Msk | (LIP_ETH_TX_BUF_SIZE - 1);
         f_tx_status[i].ctl = 0;
    }

    /* Set EMAC Transmit Descriptor Registers. */
    LPC_EMAC->TxDescriptor = (uint32_t)f_tx_descr;
    LPC_EMAC->TxStatus = (uint32_t)f_tx_status;
    LPC_EMAC->TxDescriptorNumber = (LIP_ETH_TX_BUF_CNT - 1);

    /* Tx Descriptors Point to 0 */
    LPC_EMAC->TxProduceIndex = 0;

}



void lip_ll_set_mac_addr(const uint8_t* mac_addr) {
    LPC_EMAC->SA0 = (mac_addr[5] << 8) | mac_addr[4];
    LPC_EMAC->SA1 = (mac_addr[3] << 8) | mac_addr[2];
    LPC_EMAC->SA2 = (mac_addr[1] << 8) | mac_addr[0];
}


/*************************************************************************************
 *  Запуск инициализации PHY и MAC................................
 *************************************************************************************/
t_lip_errs lip_ll_init(t_lip_init_stage stage, int flags) {
    switch (stage) {
        case LIP_INIT_STAGE_PHY_INTF_INIT:
#ifdef LPC_CLK_OUT_EN
            //CLK-Out - на P1.27 выставляем 25 МГц
            LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~LPC_PINCON_PINSEL_PX27_Msk) | LPC_PINFUNC_P1_27_CLKOUT;
#endif
#ifdef LIP_PHY_HARDWARE_RESET
            LPC_GPIO1->FIODIR |= 0x0004700; //P1.8, P1.9, P1.10, P1.14 - Out
            //выставляем уровни на выходах, определяющие режим работы phy
            LPC_GPIO1->FIOPIN1 = 0x47; //PHY MODE - 111 - все разрешено, auto-negotiation
#endif
            break;
        case LIP_INIT_STAGE_MAC_INIT:
            LPC_SC->PCONP |= LPC_SC_PCONP_PCENET_Msk;

            /* включение линий как MAC контроллера */
            LPC_PINCON->PINSEL2 = 0x50150105;
            LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~0x0000000F) | 0x00000005;

            /**************** инициализация регистров MAC-контроллера......  *************/
            /* сброс MAC */
            LPC_EMAC->MAC1 = LPC_EMAC_MAC1_RESET_TX_Msk | LPC_EMAC_MAC1_RESET_MCS_TX_Msk |
                LPC_EMAC_MAC1_RESET_RX_Msk | LPC_EMAC_MAC1_RESET_MCS_RX_Msk |
                LPC_EMAC_MAC1_SIMULATION_RESET_Msk | LPC_EMAC_MAC1_SOFT_RESET_Msk;
            LPC_EMAC->Command = LPC_EMAC_Command_RegReset_Msk | LPC_EMAC_Command_TxReset_Msk |
                LPC_EMAC_Command_RxReset_Msk;

            /* Инициализация регистров управления MAC */
            LPC_EMAC->MAC1 = LPC_EMAC_MAC1_PASS_ALL_RECEIVE_FRAMES_Msk | LPC_EMAC_MAC1_RX_FLOW_CONTROL_Msk
                                    | LPC_EMAC_MAC1_TX_FLOW_CONTROL_Msk;

            LPC_EMAC->MAC2 = LPC_EMAC_MAC2_CRC_ENABLE_Msk | LPC_EMAC_MAC2_PAD_CRC_ENABLE_Msk;

            LPC_EMAC->MAXF = ETH_MAX_FLEN;
            LPC_EMAC->CLRT = CLRT_DEF;
            LPC_EMAC->IPGR = IPGR_DEF;

            LPC_EMAC->MCFG = (0x0A << LPC_EMAC_MCFG_CLOCK_SELECT_Pos) | LPC_EMAC_MCFG_RESET_MII_MGMT_Msk;


            LPC_EMAC->MCFG = PHY_MDIO_CLK_DIV << LPC_EMAC_MCFG_CLOCK_SELECT_Pos;

            LPC_EMAC->Command = LPC_EMAC_Command_RMII_Msk | LPC_EMAC_Command_PassRuntFrame_Msk;

            if (!(flags & LIP_INIT_FLAGS_MAC_RX_FILTER_DIS))
                LPC_EMAC->Command |= LPC_EMAC_Command_PassRxFilter_Msk;
            break;
        case LIP_INIT_STAGE_MAC_START:
            const t_lip_phy_mode  *lp_mode = lip_mac_phy_mode();
            if (lp_mode->speed == LIP_PHY_SPEED_10) {
                LPC_EMAC->SUPP = 0;
            } else {
                LPC_EMAC->SUPP = LPC_EMAC_SUPP_SPEED_Msk;
            }

            if (lp_mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL) {
                LPC_EMAC->IPGT = IPGT_FULL_DUP;
                LPC_EMAC->MAC2 |= LPC_EMAC_MAC2_FULL_DUPLEX_Msk;
            } else {
                LPC_EMAC->IPGT = IPGT_HALF_DUP;
            }
            /* инициализация дескрипторов */
            f_rx_descr_init();
            f_tx_descr_init();

            /* разршение приема/передачи etc */
            LPC_EMAC->RxFilterCtrl = LPC_EMAC_RxFilterCtrl_AcceptBroadcastEn_Msk |
#ifdef LIP_USE_IGMP
                    LPC_EMAC_RxFilterCtrl_AcceptMulticastEn_Msk |
#endif
                    LPC_EMAC_RxFilterCtrl_AcceptPerfectEn_Msk;

            LPC_EMAC->Command |= LPC_EMAC_Command_RxEnable_Msk | LPC_EMAC_Command_TxEnable_Msk;
            LPC_EMAC->MAC1 |= LPC_EMAC_MAC1_RECEIVE_ENABLE_Msk;
            f_tx_frame_finish_pos = 0;
            break;
    }

    return LIP_ERR_SUCCESS;
}


#ifdef LIP_PORT_INTERMIDIATE_BUF_RX
    static uint8_t f_intm_rx_buf[LIP_ETH_RX_LAST_BUF_SIZE];
#endif

#ifdef LIP_PORT_INTERMIDIATE_BUF_TX
    static uint8_t f_intm_tx_buf[LIP_ETH_TX_BUF_SIZE];
#endif


/***************************************************************************
 *  Получение буфера с принятыми данными. Возвращает указатель на буфер и
 *  длину (через length). Если нет принятого пакета - возвращает 0
 ***************************************************************************/
static void f_check_rx_descr(void) {
    unsigned ind = LPC_EMAC->RxConsumeIndex;
    unsigned prod_ind = LPC_EMAC->RxProduceIndex;
    uint8_t* res = 0;

    if (ind!=prod_ind) {
        //todo - проверка ошибок

        int len = 0;
        f_rx_dscr_cnt = 1;

        //проходимся по цепочке дескрипторов, пока не наткнемся на конец пакета
        while (!(f_rx_status[ind].ctl & LPC_ETH_STATUS_RXCTL_LastFlag_Msk) && (ind!=prod_ind)) {
            len += (f_rx_status[ind++].ctl & LPC_ETH_DESCR_RXCTL_Size_Msk) + 1;
            f_rx_dscr_cnt++;
        }

        if (ind!=prod_ind) {
            t_lip_rx_ll_frame rx_frame;
            memset(&rx_frame, 0, sizeof(rx_frame));
            //добавляем длину последнего дескриптора из цепочки (если один, то только сдесь)
            len += (f_rx_status[ind].ctl & LPC_ETH_DESCR_RXCTL_Size_Msk) + 1;
            //возвращаем длину и адрес (адрес первого дескриптора)
            rx_frame.pl_main.size = len;
#ifdef LIP_PORT_INTERMIDIATE_BUF_RX
            memcpy(f_intm_rx_buf, f_rx_descr[LPC_EMAC->RxConsumeIndex].addr, len);
            rx_frame.pl_main.data = f_intm_rx_buf;
#else
            rx_frame.pl_main.data = f_rx_descr[LPC_EMAC->RxConsumeIndex].addr;
#endif
            lip_mac_rx_proc_frame(&rx_frame);

            ind = LPC_EMAC->RxConsumeIndex + f_rx_dscr_cnt;
            if (ind > LPC_EMAC->RxDescriptorNumber)
                ind = 0;
            LPC_EMAC->RxConsumeIndex = ind;
        }
    }
}


/****************************************************************************
 * Возвращает указатель на буфер, в котором можно формировать следующий
 * пакет (0, если нет свободного места для пакета)
 * В max_length возвращается максимальный размер пакета
 *   (в данной реализации - всегда LIP_ETH_TX_BUF_SIZE)
 ****************************************************************************/
uint8_t *lip_ll_tx_frame_buf_prepare(const t_lip_tx_frame_info *info, uint32_t size) {
    unsigned ind = LPC_EMAC->TxProduceIndex;
    unsigned ind_nxt = LPC_EMAC->TxProduceIndex + 1;
    uint8_t *buf = 0;
    const unsigned cons_ind = LPC_EMAC->TxConsumeIndex;
    const uint16_t cons_tx_idx = f_tx_frame_ctx[cons_ind].tx_idx;

    if (ind_nxt > LPC_EMAC->TxDescriptorNumber)
        ind_nxt = 0;

    if (ind_nxt != LPC_EMAC->TxConsumeIndex) {
        /* проверяем возможность выхода за границу циклического буфера */
        if ((f_tx_buf_addr + LIP_ETH_TX_BUF_SIZE) > LIP_ETH_TX_FULL_BUF_SIZE)
            f_tx_buf_addr = 0;

        /* ind == cons_ind соответствует случаю когда все данные передались =>
         * всегда весь буфер пуст. В остальных случаях проверяем, что начало использованного
         * буфера не попадает в резервируемую часть под новый пакет */
        if ((ind == cons_ind) ||
            (f_tx_buf_addr > cons_tx_idx) ||
            ((f_tx_buf_addr + LIP_ETH_TX_BUF_SIZE) <= cons_tx_idx)) {
            /* возвращаем адрес и макс. длину буфера */
            f_tx_descr[ind].addr = &f_tx_bufs[f_tx_buf_addr];
#ifdef LIP_PORT_INTERMIDIATE_BUF_TX
            buf = f_intm_tx_buf;
#else
            buf = f_tx_descr[ind].addr;
#endif
        }
    }
    return buf;
}

t_lip_errs lip_ll_tx_frame(t_lip_tx_frame *frame) {
    unsigned ind = LPC_EMAC->TxProduceIndex;
    unsigned ind_nxt = ind + 1;

    t_lip_errs res = LIP_ERR_SUCCESS;

    if (ind_nxt > LPC_EMAC->TxDescriptorNumber)
        ind_nxt = 0;

    if (ind_nxt != LPC_EMAC->TxConsumeIndex) {
        uint32_t hdr_size = frame->buf.cur_ptr - frame->buf.start_ptr;
        const uint32_t data_size = frame->exdata_size;
        if (data_size == 0) {
            if (hdr_size > LIP_ETH_TX_BUF_SIZE)
                hdr_size = LIP_ETH_TX_BUF_SIZE;
            //устанавливаем размер буфера в дескрипторе
            f_tx_descr[ind].ctl = LPC_ETH_DESCR_TXCTL_Last_Msk | ((hdr_size-1) & LPC_ETH_DESCR_TXCTL_Size_Msk);
#ifdef LIP_PORT_INTERMIDIATE_BUF_TX
            memcpy(f_tx_descr[ind].addr, f_intm_tx_buf, hdr_size);
#endif
    //сохраняем используемый адрес
            f_tx_frame_ctx[ind].tx_idx = f_tx_buf_addr;
            //переходим к адресу за подготовленным буфером
            f_tx_buf_addr += hdr_size;

            f_tx_frame_ctx[ind].frame = frame;

            //указываем контроллеру DMA что подготовили буфер и он может передавать
            LPC_EMAC->TxProduceIndex = ind_nxt;

        } else {
            unsigned ind_nxt2;
            ind_nxt2 = ind_nxt + 1;
            if (ind_nxt2 > LPC_EMAC->TxDescriptorNumber)
                ind_nxt2 = 0;

            if (ind_nxt2 != LPC_EMAC->TxConsumeIndex) {
                f_tx_descr[ind].ctl = ((hdr_size-1) & LPC_ETH_DESCR_TXCTL_Size_Msk);
#ifdef LIP_PORT_INTERMIDIATE_BUF_TX
                memcpy(f_tx_descr[ind].addr, f_intm_tx_buf, hdr_size);
#endif
                LPC_EMAC->TxProduceIndex = ind_nxt;

                f_tx_descr[ind_nxt].addr = (uint8_t*)frame->exdata_ptr;
                f_tx_descr[ind_nxt].ctl = LPC_ETH_DESCR_TXCTL_Last_Msk | ((data_size-1) & LPC_ETH_DESCR_TXCTL_Size_Msk);


                f_tx_frame_ctx[ind].tx_idx = f_tx_buf_addr;
                f_tx_frame_ctx[ind_nxt].tx_idx = f_tx_buf_addr;
                f_tx_buf_addr += hdr_size;

                f_tx_frame_ctx[ind].frame = NULL;
                f_tx_frame_ctx[ind_nxt].frame = frame;

                LPC_EMAC->TxProduceIndex = ind_nxt2;


            }
        }

    }
    return res;

}

static void f_check_tx_descr(void) {
    uint32_t tx_cons_idx = LPC_EMAC->TxConsumeIndex;
    uint32_t tx_proc_pos = f_tx_frame_finish_pos;
    while (tx_cons_idx != tx_proc_pos) {
        t_lip_tx_frame *frame = f_tx_frame_ctx[tx_proc_pos].frame;
        if (frame) {
            f_tx_frame_ctx[tx_proc_pos].frame = 0;
            frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_FINISH;
            lip_mac_tx_frame_done_cb(frame);
        }
        if (++tx_proc_pos > LPC_EMAC->TxDescriptorNumber) {
            tx_proc_pos = 0;
        }
        f_tx_frame_finish_pos = tx_proc_pos;
    }
}


#ifdef LIP_USE_IGMP
/************************************************************************
 * Вызывается при подключение к mcast группе с заданным mac-адресом =>
 *   после вызова должны принимать пакеты по заданному адресу
 ************************************************************************/
t_lip_errs lip_ll_join(const uint8_t* mac) {
    //LPC_EMAC->RxFilterCtrl |= LPC_EMAC_RxFilterCtrl_AcceptMulticastEn_Msk;
    return LIP_ERR_SUCCESS;
}

/***************************************************************************
 * Вызывается при отключение от mcast гурппы. После вызова порт может не
 * принимать больше пакеты по заданному mac-адресу
 ***************************************************************************/
t_lip_errs lip_ll_leave(const uint8_t* mac) {
    return LIP_ERR_SUCCESS;
}
#endif



/***********************************************************************
 *  Отключение интерфейса. После данного вызова порт может
 *  отключить аппаратные средства под ethernet
 ***********************************************************************/
void lip_ll_close(void) {
    LIP_PHY_RESET_CLR();
    LPC_SC->PCONP &= ~LPC_SC_PCONP_PCENET_Msk;
}


void lip_ll_stop(void) {

}


void lip_ll_pull(void) {
    f_check_rx_descr();
    f_check_tx_descr();
}




