#include "lip_ll_lpc43xx_descr.h"
#include "lip_log.h"

#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


#define LIP_ETH_RX_NEXT_IND(ind) ((ind)==(LIP_ETH_RX_DESCR_CNT-1) ? 0 : (ind)+1)
#define LIP_ETH_TX_NEXT_IND(ind) ((ind)==(LIP_ETH_TX_DESCR_CNT-1) ? 0 : (ind)+1)


#define ETH_TX_FLAGS (TXDESCR_CTL_FS | TXDESCR_CTL_LS)


#define ETH_BUF_ALIGN  4

#define LSPEC_ALIGNMENT ETH_BUF_ALIGN

/* Массив дескрипторов DMA на прием */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(volatile t_eth_rx_descr  f_rx_descr[LIP_ETH_RX_DESCR_CNT]);

/* Массив дескрпиторов DMA на передачу */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(volatile t_eth_tx_descr  f_tx_descr[LIP_ETH_TX_DESCR_CNT]);

/* Массив данных в который сохраняются входные пакеты */
#include "lcspec_align.h"
LIP_PORT_BUF_MEM(volatile uint8_t f_rx_bufs[LIP_ETH_RX_FULL_BUF_SIZE]);

/* Массив данных на передачу */
#include "lcspec_align.h"
LIP_PORT_BUF_MEM(volatile uint8_t f_tx_bufs[LIP_ETH_TX_FULL_BUF_SIZE]);
/* Позиции в буфере на передачу, которые использовлись для в качестве начала
 * кадра для следующего дескрптора. Используются для определения свободного
 * места в буфере */
static uint16_t f_tx_indx[LIP_ETH_TX_DESCR_CNT];

/* номер дескриптора, соответствующий началу принимаемого пакета */
static int f_rx_cur_descr;
/* номер дескриптора, соответствующий последнему сегменту принимаемого пакета */
static int f_rx_last_descr;

/* номер дескриптора, который будет использован для передачи след. пакета */
static int f_tx_put_descr;
/* номер дескриптора, следующий за последним, по которому завершена передача */
static int f_tx_done_descr;
/* кол-во дескрипторов, по которым начата, но не завершена передача */
static int f_tx_descr_progr_cnt;
/* позиция в буфере на передачу, которая будет использоваться для следующего кадра */
static uint16_t f_tx_buf_put_pos;
/* позиция в буфере на передачу, соответствующая началу кадра для дескриптора,
   идущего сразу после дескриптора, по которому последним закончена передача */
static uint16_t f_tx_buf_done_pos;


uint32_t lip_ll_lpc43descr_rx_init(void) {
    unsigned i;
    memset((void*)&f_rx_descr[0], 0, sizeof(f_rx_descr));
    for (i=0; i < LIP_ETH_RX_DESCR_CNT; i++) {
        f_rx_descr[i].addr1 = (uint8_t*)&f_rx_bufs[i*LIP_ETH_RX_BUF_SIZE];

        if (i==(LIP_ETH_RX_DESCR_CNT-1)) {
            f_rx_descr[i].ctl_size1 = RXDESCR_CTL_RER | LIP_ETH_RX_LAST_BUF_SIZE ;
        } else {
            f_rx_descr[i].ctl_size1 = LIP_ETH_RX_BUF_SIZE;
        }
        f_rx_descr[i].status = RXDESCR_ST_OWN;
    }

    f_rx_cur_descr = 0;
    return  (uint32_t)&f_rx_descr[0];

}

uint32_t lip_ll_lpc43descr_tx_init(void) {
    memset((void*)&f_tx_descr[0], 0, sizeof(f_tx_descr));
    /* сам массив дескрипторов реально инициализируется при начале передачи,
       поэтому здесь доп. инициализация не требуется */

    f_tx_put_descr = f_tx_done_descr = 0;
    f_tx_buf_put_pos = f_tx_buf_done_pos = 0;
    f_tx_descr_progr_cnt = 0;

    return (uint32_t)&f_tx_descr[0];    
}



/***************************************************************************
 *  Получение буфера с принятыми данными. Возвращает указатель на буфер и
 *  длину (через length). Если нет принятого пакета - возвращает 0
 ***************************************************************************/
uint8_t* lip_ll_lpc43descr_rx_get_buf(size_t *length) {
    uint8_t* res = 0;
    uint8_t* buf_start = 0;
    int cur_descr_ind = f_rx_cur_descr;
    /* если пришел новый дескриптор */
    while (!(f_rx_descr[cur_descr_ind].status & RXDESCR_ST_OWN) && !res) {
        volatile t_eth_rx_descr *cur_descr = &f_rx_descr[cur_descr_ind];
        int cur_err = 0;
        if ((cur_descr->status & RXDESCR_ST_ES) ||
                (!(cur_descr->status & RXDESCR_ST_FS) && (cur_descr_ind==f_rx_cur_descr))) {
            /* при ошибке выкидываем все пакеты до этой ошибки */
            lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                       "lip port: rx error. status = 0x%08x. drop descr from = %d to = %d\n",
                        cur_descr->status, f_rx_cur_descr, cur_descr_ind);
            cur_err = 1;
        } else {
            /* для первого дескриптора сохраняем адрес начала принятого кадра */
            if (cur_descr_ind == f_rx_cur_descr) {
                buf_start = cur_descr->addr1;
            }
            if (cur_descr->status & RXDESCR_ST_LS) {
                /* последний дескриптор => принят весь кадр - возвращаем размер и адрес с начала */
                res = buf_start;
                if (length!=NULL) {
                    /* В дескрипторе сохраняется размер с CRC - вычитаем его */
                    *length = RXDESCR_ST_GET_FRATE_LEN(cur_descr->status)-4;
                }
                /* сохраняем номер последнего дескриптора, чтобы потом быстрей
                 * освободить все дескрипторы кадра в lip_ll_rx_drop() */
                f_rx_last_descr = cur_descr_ind;
            } else {
                /* последний дескриптор размером с макс. пакет (чтобы данные всегда были подряд),
                 * поэтому в нем всегда должен быть полный сегмент, если не так -
                 * то это ошибка ! */
                if (cur_descr_ind == (LIP_ETH_RX_DESCR_CNT-1)) {
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                               "lip port: rx last packet err. status = 0x%08x\n",
                                cur_descr->status);
                    cur_err = 1;
                }
            }
        }


        if (cur_err) {
            int i;
            buf_start=0;
            /* при ошибке выкидываем все части этого пакета,
             * и начинаем искать действительный пакет дальше */
            for (i=f_rx_cur_descr; i < (cur_descr_ind+1); i++) {
                f_rx_descr[i].status = RXDESCR_ST_OWN;
            }
            f_rx_cur_descr = f_rx_last_descr = cur_descr_ind = LIP_ETH_RX_NEXT_IND(cur_descr_ind);;
        } else {
            cur_descr_ind = LIP_ETH_RX_NEXT_IND(cur_descr_ind);
        }
    }

    return res;
}

/*****************************************************************************
 * указывает, что принятый буфер больше обработан, и связанные дескрипторы
 * можно пометить как свободные (вызывается после lip_ll_rx_get_buf())
 ****************************************************************************/
t_lip_errs lip_ll_lpc43descr_rx_drop(void) {
    int i;
    for (i=f_rx_cur_descr; i < (f_rx_last_descr+1); i++) {
        f_rx_descr[i].status = RXDESCR_ST_OWN;
    }
    f_rx_cur_descr = LIP_ETH_RX_NEXT_IND(f_rx_last_descr);
    return LIP_ERR_SUCCESS;
}


/****************************************************************************
 * Возвращает указатель на буфер, в котором можно формировать следующий
 * пакет (0, если нет свободного места для пакета)
 * В max_length возвращается максимальный размер пакета
 *   (в данной реализации - всегда LIP_ETH_TX_BUF_SIZE)
 ****************************************************************************/
uint8_t *lip_ll_lpc43descr_tx_get_buf(int* max_length) {
    uint8_t* res = 0;
    /* сперва проверяем, какие дескрипторы завершились */
    while ((f_tx_descr_progr_cnt !=0) &&
            !(f_tx_descr[f_tx_done_descr].ctl_st & TXDESCR_CTLST_OWN)) {

        if (f_tx_descr[f_tx_done_descr].ctl_st & TXDESCR_ST_ES) {
            lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                       "lip port: tx buf done with error! status = 0x%08X\n",
                       f_tx_descr[f_tx_done_descr].ctl_st);

            if (f_tx_descr[f_tx_done_descr].ctl_st  & TXDESCR_ST_UF) {
                /* сбрасываем статус ошибки */
               lip_ll_lpc43descr_clear_stat();
               /* указываем DMA, что нужно проверить дескриптор */
               lip_ll_lpc43descr_tx_poll_req();
            }
        }


        f_tx_buf_done_pos = f_tx_indx[f_tx_done_descr];
        f_tx_done_descr = LIP_ETH_TX_NEXT_IND(f_tx_done_descr);
        f_tx_descr_progr_cnt--;
    }

    /* проверяем наличие свободного места */
    if (f_tx_descr_progr_cnt < LIP_ETH_TX_DESCR_CNT) {
        /* ind == cons_ind соответствует случаю когда все данные передались =>
         * всегда весь буфер пуст. В остальных случаях проверяем, что начало использованного
         * буфера не попадает в резервируемую часть под новый пакет */
        if ((f_tx_descr_progr_cnt == 0) ||
               (f_tx_buf_put_pos > f_tx_buf_done_pos) ||
               ((f_tx_buf_put_pos + LIP_ETH_TX_BUF_SIZE) <= f_tx_buf_done_pos)) {
            /* возвращаем адрес и макс. длину буфера */
            f_tx_descr[f_tx_put_descr].addr1 = res = (uint8_t* )&f_tx_bufs[f_tx_buf_put_pos];
            if (max_length!=0)
                *max_length = LIP_ETH_TX_BUF_SIZE;
        }

    }

    return res;
}


static LINLINE void f_start_tx_descr(size_t size) {
    f_tx_descr[f_tx_put_descr].size1 = size;
    /* конец цикла */
    if (f_tx_put_descr == (LIP_ETH_TX_DESCR_CNT-1)) {
        f_tx_descr[f_tx_put_descr].ctl_st = ETH_TX_FLAGS | TXDESCR_CTL_TER |
                TXDESCR_CTLST_OWN;
    } else {
        f_tx_descr[f_tx_put_descr].ctl_st = ETH_TX_FLAGS | TXDESCR_CTLST_OWN;
    }

    lip_ll_lpc43descr_dmb();
    /* указываем DMA, что нужно проверить дескриптор */
    lip_ll_lpc43descr_tx_poll_req();

    /* рассчет позиции в буфере для использования в след. кадре */

    /* выравниваем используемый размер на заданную границу */
    if (size % ETH_BUF_ALIGN)
        size += (ETH_BUF_ALIGN-size%ETH_BUF_ALIGN);
    /* при этом не должны превысить макс. размер пакета */
    if (size > LIP_ETH_TX_BUF_SIZE)
        size = LIP_ETH_TX_BUF_SIZE;
    f_tx_buf_put_pos += size;

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

    /* сохраняем позицию для дескриптора, чтобы рассчитать потом свободное место */
    f_tx_indx[f_tx_put_descr] = f_tx_buf_put_pos;


    /* переходим к следующему дескриптору */
    f_tx_put_descr = LIP_ETH_TX_NEXT_IND(f_tx_put_descr);
    f_tx_descr_progr_cnt++;
}


t_lip_errs lip_ll_lpc43descr_tx_flush_split(size_t size, const uint8_t* data, int data_size) {
    t_lip_errs res = LIP_ERR_SUCCESS;
    f_tx_descr[f_tx_put_descr].addr2 = data;
    f_tx_descr[f_tx_put_descr].size2 = data_size;
    f_start_tx_descr(size);
    return res;
}
