#include "lip_ll_wch32v307.h"
#include "lip_log.h"
#include "lip/ll/lip_ll.h"
#include "phy/lip_phy.h"
#include "lip/link/lip_mac_tx.h"
#include "lip/link/lip_mac_rx.h"

//#define USE_FINE_MODE

#define TXDESCR_CTL_LS      ETH_DMATxDesc_LS
#define TXDESCR_CTL_TER        ETH_DMATxDesc_TER
#define TXDESCR_CTLST_OWN   ETH_DMATxDesc_OWN
#define TXDESCR_CTL_FS          ETH_DMATxDesc_FS
#define TXDESCR_ST_UF           ETH_DMATxDesc_UF
#define TXDESCR_ST_ES           ETH_DMATxDesc_ES

#define RXDESCR_ST_OWN      ETH_DMARxDesc_OWN
#define RXDESCR_ST_LS           ETH_DMARxDesc_LS
#define RXDESCR_ST_FS           ETH_DMARxDesc_FS
#define RXDESCR_ST_ES           ETH_DMARxDesc_ES
#define RXDESCR_CTL_RER       ETH_DMARxDesc_RER

#define RXDESCR_ST_GET_FRATE_LEN(val) ((val & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT)


#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_PORT_RX_DESCR_CNT - 1) ? 0 : (ind) + 1)
#define LIP_ETH_TX_NEXT_IND(ind) ((ind)==(LIP_PORT_TX_DESCR_CNT - 1) ? 0 : (ind) + 1)

#define ETH_BUF_ALIGN  4

#define LSPEC_ALIGNMENT ETH_BUF_ALIGN

typedef struct {
    uint32_t ctl_st;
    uint16_t size1;
    uint16_t size2;
    const uint8_t* addr1;
    const uint8_t* addr2;
} t_eth_tx_descr;

typedef struct {
    uint32_t status;
    uint16_t ctl_size1;
    uint16_t size2;
    uint8_t* addr1;
    uint8_t* addr2;
} t_eth_rx_descr;


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

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

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

/* номер дескриптора, соответствующий началу принимаемого пакета */
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;

#if LIP_PTP_ENABLE
static bool f_tstmp_en = false;
#else
#define f_tstmp_en (0)
#endif
static bool f_mac_init_finish = false;

static LINLINE void *f_rdes_buf_addr(int idx) {
    return  (void*)&f_rx_bufs[idx * LIP_PORT_RX_BUF_SIZE];
}

uint32_t lip_ll_wch32descr_rx_init(void) {
    unsigned i;
    memset((void*)&f_rx_descr[0], 0, sizeof(f_rx_descr));
    for (i=0; i < LIP_PORT_RX_DESCR_CNT; i++) {
        f_rx_descr[i].addr1 = (uint8_t*)f_rdes_buf_addr(i);

        if (i == (LIP_PORT_RX_DESCR_CNT - 1)) {
            f_rx_descr[i].ctl_size1 = RXDESCR_CTL_RER | LIP_PORT_RX_LAST_BUF_SIZE;
        } else {
            f_rx_descr[i].ctl_size1 = LIP_PORT_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_wch32descr_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_descr_progr_cnt = 0;

    return (uint32_t)&f_tx_descr[0];
}

static t_lip_rx_frame rx_frame;

#ifdef USE_FINE_MODE
volatile uint32_t sec_H = 0;
uint32_t acc_val;
uint8_t sir_val;
#endif

#define CONVERT_TO_TIME

#if LIP_PTP_ENABLE
int convert_ssec_to_tstmp(uint32_t ssec_h, int32_t ssec_l, t_lip_ptp_tstmp *tstmp) {
#ifdef USE_FINE_MODE
    union t_systick_cnt cnt;
    static union t_systick_cnt prev_cnt;

    if(ssec_l < 0) {
        lprintf("ssec_l<0 = %d\n", ssec_l);
        while(1);
    }
    cnt.CNTL = ssec_l;
    //ssec_h /= 3;
    cnt.CNTL |= (ssec_h << 29) & 0x80000000;
    cnt.CNTH = (ssec_h >> 3) | (sec_H >> 1);

    //cnt.CNT /= 3;

    if (cnt.CNT % sir_val) {
        //lprintf("(cnt sir_val) = %d\n", (uint32_t)(cnt.CNT % acc_val));
    }
    //cnt.CNTL = ssec_l;
    //cnt.CNTL |= (ssec_h << 29) & 0x80000000;
    //cnt.CNTH = (ssec_h >> 3) | (sec_H >> 1);
#if 0
    if (cnt.CNT % 60) {
        return 0;
        lprintf("cnt unexpected!\n");
        //while(1);
        union t_systick_cnt cnt;

        cnt.CNTL = ssec_l;
        cnt.CNTL |= (ssec_h << 29) & 0x80000000;
        cnt.CNTH = (ssec_h >> 3) | (sec_H >> 1);

        if (cnt.CNT % 60) {
            ssec_h += 4;
            cnt.CNTL = ssec_l;
            cnt.CNTL |= (ssec_h << 29);
            cnt.CNTH = (ssec_h >> 3) | (sec_H >> 1);
            if (cnt.CNT % 60) {
                //lprintf("fail2 0x%x%x\n", cnt.CNTH, cnt.CNTL);
                ssec_h -= 8;
                cnt.CNTL = ssec_l;
                cnt.CNTL |= (ssec_h << 29);
                cnt.CNTH = (ssec_h >> 3) | (sec_H >> 1);
                if (cnt.CNT % 60) {
                    lprintf("prev_cnt 0x%x%x\n", prev_cnt.CNTH, prev_cnt.CNTL);
                    lprintf("fail3 0x%x%x\n", cnt.CNTH, cnt.CNTL);
                }
            }
        }
    }
#endif
    prev_cnt = cnt;
    tstmp->sec = cnt.CNT / (1U << 31);
    tstmp->ns = (((double)cnt.CNT / (1U << 31)) - tstmp->sec) * 1000000000;


/*
    tstmp->sec = ssec_h >> 1;
    if (ssec_l < 0) {
        lprintf("ssec_l = %d unexpected\n", ssec_l);
        while(1);
    }
    tstmp->ns = ((double)ssec_l / (1U << 31)) * 1000000000;
    if (tstmp->ns < 0) {
        lprintf("tstmp->ns = %d unexpected\n", tstmp->ns);
        while(1);
    }
    */
#else
#ifdef CONVERT_TO_TIME
    union t_systick_cnt cnt;
    cnt.CNTL = ssec_l;
    cnt.CNTL |= ssec_h << 31;
    cnt.CNTH = ssec_h >> 1;

    //cnt->CNT *= time_k;
    /*tstmp->ns = cnt->CNT % SYSCLK_FREQ_144MHz;
    uint8_t log10val = IntegerLogBase10(tstmp->ns);
    lprintf("tstmp->ns / SYSCLK = %d\n", tstmp->ns, log10val, pow10(log10val));
    tstmp->ns *= pow10(log10val);
    lprintf("tstmp->ns=%d log10=%d pow10=%d\n", tstmp->ns, log10val, pow10(log10val));
    */

    tstmp->sec = cnt.CNT / SYSCLK_FREQ_144MHz;
    tstmp->ns = (((double)cnt.CNT / SYSCLK_FREQ_144MHz) - tstmp->sec) * 1000000000;
    //lprintf("tstmp->ns=%d in double\n", tstmp->ns);
    if(tstmp->ns < 0) {
        lprintf("ns=%d unexpected! ssec_h=%d ssec_l=%d\n", tstmp->ns, ssec_h, ssec_l);
        while(1);
    }
#else
    tstmp->sec = ssec_h;
    tstmp->ns = ssec_l;
#endif
#endif
    tstmp->fract_ns = 0;
    return 1;
}
#endif  //LIP_PTP_ENABLE


//#define PTP_REQ_FREQ (50000000)
//#define PTP_REQ_FREQ (25000000)
#define PTP_REQ_FREQ (36000000)
#define CHIP_CLK_PTP_FREQ (144000000)
#define AFIO_PCFR1_PPS_PB5_OUT (1 << 30)

#if LIP_PTP_ENABLE
void f_tstm_stop(void) {
    ETH->PTPTSCR |=  ~ETH_PTPTSCR_TSE;
}

void f_tstm_start(void) {
    ETH->PTPTSCR =  ETH_PTPTSCR_TSE;

#ifdef USE_FINE_MODE
    // Fine adjustment mode
    ETH->PTPTSCR |= ETH_PTPTSCR_TSFCU;
    //ETH->PTPTSAR = 0x40000000; // деление на (1LL<<32)/(1U<<31) = 2
    acc_val = ((double) PTP_REQ_FREQ / CHIP_CLK_PTP_FREQ) * 2 * (1UL << 31);
    ETH->PTPTSAR = acc_val;
    //ETH->PTPSSIR = 60;
    sir_val = (1UL << 31)/ PTP_REQ_FREQ;
    sir_val += 1;
    ETH->PTPTSCR |= ETH_PTPTSCR_TSARU;
    while (ETH->PTPTSCR & ETH_PTPTSCR_TSARU);

    ETH->PTPSSIR = sir_val;
    lprintf("PTP freq %d fine acc=0x%x sir=%d\n", PTP_REQ_FREQ, acc_val, sir_val);

    // включаем PB5 на вывод PPS
    AFIO->PCFR1 |= AFIO_PCFR1_PPS_PB5_OUT;
#else
    ETH->PTPSSIR = 1;
#endif

    ETH->PTPTSHUR = 0;
    ETH->PTPTSLUR = 0;

    ETH->PTPTSCR |= ETH_PTPTSCR_TSSTI;
    while (ETH->PTPTSCR & ETH_PTPTSCR_TSSTI);

    ETH->PTPTSCR |= ETH_PTPTSCR_TSSTU;
    while (ETH->PTPTSCR & ETH_PTPTSCR_TSSTU);
}
#else
    #define f_tstm_start()
    #define f_tstm_stop()
#endif

static inline void lip_ll_tstmp_read_raw(uint32_t *ssec_h, uint32_t *ssec_l) {
    do {
        *ssec_h = ETH->PTPTSHR;
        *ssec_l = ETH->PTPTSLR;
    } while (*ssec_h != ETH->PTPTSHR);
}

#if 0
void tstmp_to_uint64(t_lip_ptp_tstmp *tstmp, uint64_t *pval) {
    *((uint32_t*)pval + 1) = tstmp->sec;
    *(uint32_t*)pval |= tstmp->ns;
}

void f_tstm_test(void) {
    //ETH->PTPTSCR |= ETH_PTPTSCR_TSSTU;
    t_lip_ptp_tstmp tstmp;
    uint32_t secs;



    t_ltimer tmr;
#define TIME_REG    (ETH->PTPTSHR)
    uint32_t sub_secs = TIME_REG;
    //uint32_t secs = ETH->PTPTSHR;
    uint32_t st_tick = lclock_get_ticks();
    volatile uint32_t *ptr = (uint32_t*)&ETH->PTPTSHR;
    uint64_t val64;

    ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(1000));

    while(1) {
        uint32_t ssec_h;
        uint32_t ssec_l;

        lip_ll_tstmp_read_raw(&ssec_h, &ssec_l);
        convert_ssec_to_tstmp(ssec_h, ssec_l, &tstmp);
        lprintf("%d: %x %x, %d s %d ns\n", lclock_get_ticks() - st_tick, ssec_h, ssec_l, tstmp.sec, tstmp.ns);
        //lprintf("%d: %x %x\n", lclock_get_ticks() - st_tick, ssec_h, ssec_l);
        //lip_ll_tstmp_read(&tstmp);
        //lprintf("%d: %d %d\n", lclock_get_ticks() - st_tick, tstmp.sec, tstmp.ns);
    }


    /*while(secs == ETH->PTPTSHR);
    lprintf("%d %d elapsed %d\n", secs, ETH->PTPTSHR, lclock_get_ticks() - st_tick);
    return;*/
    lip_ll_tstmp_read(&tstmp);
    lprintf("sec=%d ns=%d\n", tstmp.sec, tstmp.ns);
    tstmp_to_uint64(&tstmp, &val64);
    val64 |= 3;
    lprintf("val64=%d val64=%d\n", *(uint32_t*)&val64, *((uint32_t*)&val64 + 1));
    uint32_t ssec = ETH->PTPTSLR;
    uint32_t sec = ETH->PTPTSHR;

    for (int i = 0; i < 40; ) {
        /*
        while (sub_secs == TIME_REG);
        lprintf("%d ticks: %d %d sec %d\n", lclock_get_ticks() - st_tick, sub_secs, TIME_REG, ETH->PTPTSHR);
        sub_secs = TIME_REG;
        st_tick = lclock_get_ticks();
        i++;
        continue;
        */
        if (!ltimer_expired(&tmr)) {
            continue;
        }
        i++;
        uint32_t tmp, tmp2;
        uint64_t tmp64;
        //continue;
        lip_ll_tstmp_read(&tstmp);
        tstmp_to_uint64(&tstmp, &tmp64);
        val64 = tmp64 - val64;
        //lprintf("val %x %x\n", *((uint32_t*)&tmp64 + 1), *(uint32_t*)&tmp64);
        //lprintf("sec elapsed %x %x\n", *((uint32_t*)&val64 + 1), *(uint32_t*)&val64);
        //lprintf("%d %d %d %d\n", tmp64, tmp64>>32, val64, val64>>32);
        tmp = ETH->PTPTSLR;
        tmp2 = ETH->PTPTSHR;
        lprintf("sec %d, delta %d, ssec %d, ssec delta = %d\n", tmp2, tmp2 - sec, tmp, tmp - ssec);
        sec = tmp2;
        ssec = tmp;
        val64 = tmp64;

        //lprintf("PTPTSHR=%x\n", *ptr);
        tmp = ETH->PTPTSLR;

        //lprintf("PTPTSLR elapsed = %d\n", tmp - sub_secs);
        sub_secs = tmp;
		ltimer_reset(&tmr);
    }
}
#endif

#if LIP_PTP_ENABLE

void lip_ll_tstmp_enable(void) {
    if (!f_tstmp_en) {
        f_tstmp_en = true;
        if (f_mac_init_finish) {
            f_tstm_start();
        }
    }
}

bool lip_ll_tstmp_is_enabled(void) {
    return f_tstmp_en;
}

t_lip_errs lip_ll_tstmp_read(t_lip_ptp_tstmp *tstmp) {
    tstmp->fract_ns = 0;
    tstmp->hsec = 0;
    uint32_t ssec_h;
    uint32_t ssec_l;

    lip_ll_tstmp_read_raw(&ssec_h, &ssec_l);

    convert_ssec_to_tstmp(ssec_h, ssec_l, tstmp);
    //lprintf("tstmp_read: %d %d\n", tstmp->sec, tstmp->ns);

#if 0
    uint32_t s1 = ETH->PTPTSHR;
    // TODO: negative/positive??
    tstmp->ns = ETH->PTPTSLR;
    uint32_t s2 = ETH->PTPTSHR;
    /* для избежания изменения значения секунд между двумя чтениями выполняем
     * три чтения. Если нс меньше половины предела. Считаем, что если количество
     * нс ближе к началу, то переполнение не возможно после, если ближе к концу,
     * то перед */
    tstmp->sec = tstmp->ns < 500000000 ? s2 : s1;
    //tstmp->sec >>= 2;
#endif
    return LIP_ERR_SUCCESS;
}
#endif

#ifdef DEBUG
typedef struct {
    uint8_t *rx_frame_data;
    uint16_t rx_frame_size;
    uint32_t rx_data_wrd;
    uint16_t window;
    uint32_t last_ack_sent;
    uint32_t rcv_nxt;
} dbg_t;

dbg_t dbg[10];
int dbg_num = 0;

uint8_t *cur_rx_frame_data;
uint16_t cur_rx_frame_size;
uint32_t cur_rx_data_wrd;

#include "lcspec_pack_start.h"
/* заголовок tcp пакета */
struct st_lip_tcp_hdr {
    uint16_t src_port;
    uint16_t dest_port;
    uint32_t seq_number;
    uint32_t ack_number;
    uint8_t offset;
    uint8_t flags;
    uint16_t window;
    uint16_t checksum;
    uint16_t urgent_ptr;
    uint8_t opt[0];
} LATTRIBUTE_PACKED;
typedef struct st_lip_tcp_hdr t_lip_tcp_hdr;
#endif

#ifdef DEBUG
__attribute__((optimize ("-O0")))
#endif
t_lip_rx_frame *lip_mac_rx_get_frame(void) {
    t_lip_rx_frame *frame = NULL;
    uint8_t* buf_start = 0;
    int cur_descr_ind = f_rx_cur_descr;
    /* если пришел новый дескриптор */
    while (!(f_rx_descr[cur_descr_ind].status & RXDESCR_ST_OWN) && !frame) {
        volatile t_eth_rx_descr *cur_descr = &f_rx_descr[cur_descr_ind];
        int cur_err = 0;
        if ((cur_descr->status & ETH_DMARxDesc_ES) ||
                (!(cur_descr->status & RXDESCR_ST_FS) && (cur_descr_ind==f_rx_cur_descr))) {
            /* при ошибке выкидываем все пакеты до этой ошибки */
			//ETH_DMARxDesc_RE
            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 = f_rdes_buf_addr(cur_descr_ind);
            }
            if (cur_descr->status & RXDESCR_ST_LS) {
                /* последний дескриптор => принят весь кадр - возвращаем размер и адрес с начала */
                rx_frame.info.flags = 0;
                rx_frame.pl.data = buf_start;
                /* В дескрипторе сохраняется размер с CRC - вычитаем его */
                rx_frame.pl.size = RXDESCR_ST_GET_FRATE_LEN(cur_descr->status) - 4;
                rx_frame.fifo_num = 0;
                // TODO: сделать размеры буферов одинаковые и проверять перехлест
                //rx_frame.pl_tail.data = NULL;
                //rx_frame.pl_tail.size = 0;
                frame = &rx_frame;
#if LIP_PTP_ENABLE
                if (f_tstmp_en) {
                    //union t_systick_cnt cnt;
                    rx_frame.info.tstmp.hsec = 0;
                    rx_frame.info.tstmp.fract_ns = 0;
                    //rx_frame.info.tstmp.ns = cur_descr->addr1;
                    //rx_frame.info.tstmp.sec = cur_descr->addr2;
                    //cnt.CNTL = cur_descr->addr1;
                    //cnt.CNTH = cur_descr->addr2;
                    if (convert_ssec_to_tstmp((uint32_t)cur_descr->addr2, (int32_t)cur_descr->addr1, &rx_frame.info.tstmp)) {
                        //lprintf("rx timestamp: %d %d\n", rx_frame.info.tstmp.sec, rx_frame.info.tstmp.ns);
                        rx_frame.info.flags |= LIP_FRAME_RXINFO_FLAG_TSTAMP_VALID;
                    }
                }
#endif

                /* сохраняем номер последнего дескриптора, чтобы потом быстрей
                 * освободить все дескрипторы кадра в lip_ll_rx_drop() */
                f_rx_last_descr = cur_descr_ind;
            } else {
                /* последний дескриптор размером с макс. пакет (чтобы данные всегда были подряд),
                 * поэтому в нем всегда должен быть полный сегмент, если не так -
                 * то это ошибка ! */
                if (cur_descr_ind == (LIP_PORT_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++) {
#if LIP_PTP_ENABLE
                // адрес затирается таймштампом - восстанавливаем
                f_rx_descr[i].addr1 = f_rdes_buf_addr(i);
#endif
                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);
        }
    }

#ifdef DEBUG
    cur_rx_frame_data = rx_frame.pl.data;
    cur_rx_frame_size = rx_frame.pl.size;
    memcpy(&cur_rx_data_wrd, (cur_rx_frame_data + 0x40 + 8), sizeof(cur_rx_data_wrd));
#endif

    return frame;

}

// функции заглушки т.к. не используется rx queue из lip:
void lip_max_rx_init(void) {
}
void lip_max_rx_close(void) {
}

void lip_mac_rx_get_frame_finish(t_lip_rx_frame *frame) {
    int i;
    for (i=f_rx_cur_descr; i < (f_rx_last_descr+1); i++) {
#if LIP_PTP_ENABLE
        // адрес затирается таймштампом - восстанавливаем
        f_rx_descr[i].addr1 = f_rdes_buf_addr(i);
#endif
        f_rx_descr[i].status = RXDESCR_ST_OWN;
    }
    f_rx_cur_descr = LIP_ETH_RX_NEXT_IND(f_rx_last_descr);
}

static void f_tx_check_descr(void) {
    /* проверяем, какие дескрипторы завершились */
    /* сперва проверяем, какие дескрипторы завершились */
    while ((f_tx_descr_progr_cnt != 0) &&
            !(f_tx_descr[f_tx_done_descr].ctl_st & TXDESCR_CTLST_OWN)) {
        int tx_descr_num = f_tx_done_descr;
        volatile t_eth_tx_descr *tx_descr = &f_tx_descr[tx_descr_num];
        t_lip_tx_frame  *frame = f_tx_frames[tx_descr_num];

        if (tx_descr->ctl_st & TXDESCR_ST_ES) {
            frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_ERROR;
            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[tx_descr_num].ctl_st);
            //lprintf("addr=%x size=%d\n", tx_descr->addr1, tx_descr->size1);

            if (tx_descr->ctl_st  & TXDESCR_ST_UF) {
                /* сбрасываем статус ошибки */
               lip_ll_wch32descr_clear_stat();
               /* указываем DMA, что нужно проверить дескриптор */
               lip_ll_wch32descr_tx_poll_req();
            }
        }

#if LIP_PTP_ENABLE
        if (tx_descr->ctl_st & ETH_DMATxDesc_TTSS) {
            //union t_systick_cnt cnt;
            //cnt.CNTH = tx_descr->addr1;
            //cnt.CNTL = tx_descr->addr2;
            if (convert_ssec_to_tstmp((uint32_t)tx_descr->addr1, (int32_t)tx_descr->addr2, &frame->status.tstmp)) {
                //lprintf("tx timestamp: %d %d\n", frame->status.tstmp.sec, frame->status.tstmp.ns);
                //frame->status.tstmp.ns = tx_descr->addr2;
                //frame->status.tstmp.sec = tx_descr->addr1;

                frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_TSTMP_VALID;
            }
        }
#endif

        f_tx_done_descr = LIP_ETH_TX_NEXT_IND(f_tx_done_descr);
        f_tx_descr_progr_cnt--;
        frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_FINISH;
        lip_mac_tx_frame_done_cb(frame);
    }
}

#ifdef TX_TEST_CNTR
uint32_t tx_test_cntr = 1;
#endif

#ifdef DEBUG
__attribute__((optimize ("-O0")))
#endif
t_lip_errs lip_ll_tx_frame(t_lip_tx_frame *frame) {
    /* ind == cons_ind соответствует случаю когда все данные передались =>
     * всегда весь буфер пуст. В остальных случаях проверяем, что начало использованного
     * буфера не попадает в резервируемую часть под новый пакет */
    uint32_t hdr_size = frame->buf.cur_ptr - frame->buf.start_ptr;
    const uint32_t data_size = frame->exdata_size;
    const uint8_t *exdata_ptr = frame->exdata_ptr;

    /* проверяем наличие свободного места */
    if (f_tx_descr_progr_cnt >= LIP_PORT_TX_DESCR_CNT) {
        return LIP_ERR_BUF_NOT_RDY;
    }

    f_tx_frames[f_tx_put_descr] = frame;

    if (data_size) {
        if ((hdr_size & 3) == 0) {
            lprintf("hdr_size=%x unexpected\n", hdr_size);
            while(1);
        }
        #ifdef TX_TEST_CNTR
        {
            uint32_t cntr;
            if (hdr_size == 66)
            {
                memcpy(&cntr, (uint16_t*)exdata_ptr + 1, sizeof(cntr));
            } else {
                memcpy(&cntr, (uint8_t*)frame->buf.start_ptr + 66, sizeof(cntr));
            }
            if (cntr != tx_test_cntr) {
                lprintf("[%x]%x != %x expected\n", (uint16_t*)exdata_ptr + 1, cntr, tx_test_cntr);
                //while(1);
            }
            tx_test_cntr += (data_size + hdr_size - 66)/ 4;
        }
        #endif
        // выравниваем данные
        {
            uint8_t *start_wrd = (uint8_t*)((uint8_t*)frame->buf.start_ptr + hdr_size);
            start_wrd[0] = *((uint8_t*)exdata_ptr + 2);
            start_wrd[1] = *((uint8_t*)exdata_ptr + 3);
            exdata_ptr += 4;
        }
    }

    f_tx_descr[f_tx_put_descr].addr1 = frame->buf.start_ptr;
    f_tx_descr[f_tx_put_descr].size1 = hdr_size;
    f_tx_descr[f_tx_put_descr].addr2 = exdata_ptr;
    f_tx_descr[f_tx_put_descr].size2 = data_size;

    /*if (frame->exdata_ptr) {
        //lprintf("tx: %x %x %x\n", ((uint32_t*)frame->exdata_ptr)[0], ((uint32_t*)frame->exdata_ptr)[1], ((uint32_t*)frame->exdata_ptr)[2]);
        if (((uint32_t*)frame->exdata_ptr)[0] == 0x314c5443) {
            lprintf("bytes %x %x\n", *((uint8_t*)frame->buf.start_ptr + hdr_size), *((uint8_t*)frame->buf.start_ptr + hdr_size + 1));
            make_loop = true;
        }
    }*/

#ifdef DEBUG
    t_lip_tcp_hdr *pkt_hdr = (t_lip_tcp_hdr*)(frame->buf.start_ptr + 0x22);

    // src_port: 11115
    if (hdr_size == 66 && data_size == 0 && pkt_hdr->src_port == 0x6b2b) {

        dbg[dbg_num].rx_frame_data = cur_rx_frame_data;
        dbg[dbg_num].rx_frame_size = cur_rx_frame_size;
        dbg[dbg_num].rx_data_wrd = cur_rx_data_wrd;
        dbg[dbg_num].window = pkt_hdr->window;

        void f_sock_get_dbg(uint32_t *last_ack_sent, uint32_t *rcv_nxt);
        f_sock_get_dbg(&dbg[dbg_num].last_ack_sent, &dbg[dbg_num].rcv_nxt);

        dbg_num++;
        if (dbg_num == (sizeof(dbg)/sizeof(dbg[0]))) {
            dbg_num = 0;
        }

        static int brk_cntr = 0;
        if(brk_cntr == 5) {
            asm volatile ("" : : "r" (*(volatile uint32_t*)0x40013008));
        }

        brk_cntr++;
    }
#endif

    asm volatile("": : :"memory");
    /* конец цикла */
    f_tx_descr[f_tx_put_descr].ctl_st =
        ((frame->info.flags & LIP_FRAME_TXINFO_FLAG_RAW) ? ETH_DMATxDesc_CIC_ByPass | ETH_DMATxDesc_DP: ETH_DMATxDesc_CIC_TCPUDPICMP_Full ) |
        TXDESCR_CTL_FS | TXDESCR_CTL_LS | ETH_DMATxDesc_IC |
        ((f_tstmp_en && (frame->info.flags & LIP_FRAME_TXINFO_FLAG_TSTAMP_REQ)) ? ETH_DMATxDesc_TTSE : 0) |
        TXDESCR_CTLST_OWN |
        (f_tx_put_descr == (LIP_PORT_TX_DESCR_CNT - 1) ? TXDESCR_CTL_TER : 0);

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

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

    return LIP_ERR_SUCCESS;
}

void lip_port_pull(void) {
    const uint32_t ch_st = ETH->DMASR;
    ETH->DMASR = ch_st;
#if 0
    if (ch_st & ETH_DMASR_RS) {
        f_rx_check_descr();
    }
#endif
    if (ch_st & (ETH_DMASR_AIS | ETH_DMASR_NIS))
    {
        f_tx_check_descr();
    }
}

void lip_mac_user_pull(void) {
    lip_port_pull();
}

#ifdef LIP_CFG_USE_IRQ
void __attribute__((interrupt())) ETH_IRQHandler(void) {
    lip_port_pull();
}
#endif

void lip_ll_wch32descr_tx_poll_req(void) {
        /* Resume DMA transmission*/
        ETH->DMATPDR = 0;
}

void lip_ll_wch32descr_clear_stat(void) {
        /* Clear TBUS ETHERNET DMA flag */
        ETH->DMASR = ETH_DMASR_TBUS | ETH_DMASR_TUS;
}

#define define_O(a,b) \
  GPIO_InitStructure.GPIO_Pin = b;\
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;\
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;\
  GPIO_Init(a, &GPIO_InitStructure)

#define define_I(a,b) \
  GPIO_InitStructure.GPIO_Pin = b;\
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;\
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;\
  GPIO_Init(a, &GPIO_InitStructure)

  //GPIO_Mode_IN_FLOATING

void LipStagePhyInit(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
		RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);

	RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, ENABLE);
	RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, DISABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC_Tx, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);

    //define_O(GPIOA, GPIO_Pin_8);/* MCO */

    define_O(GPIOA, GPIO_Pin_2);/* MDC */  /* <<-- TODO: MDIO*/
    define_O(GPIOC, GPIO_Pin_1);/* MDIO */

    // TODO: проверить с GPIO_Mode_AF_OD
    define_O(GPIOB, GPIO_Pin_11);//txen
    define_O(GPIOB, GPIO_Pin_12);//txd0
    define_O(GPIOB, GPIO_Pin_13);//txd1

    /* RX */
    define_I(GPIOA, GPIO_Pin_1);/* PA1 RXC */
    define_I(GPIOA, GPIO_Pin_7);/* PA7 RXDV */
    define_I(GPIOC, GPIO_Pin_4);/* RXD0 */
    define_I(GPIOC, GPIO_Pin_5);/* RXD1 */
}

#ifdef LIP_USE_IGMP
    t_lip_errs lip_ll_join(const uint8_t* mac) {

        return LIP_ERR_SUCCESS;
    }

    t_lip_errs lip_ll_leave(const uint8_t* mac) {

        return LIP_ERR_SUCCESS;
    }
#endif

t_lip_errs lip_ll_init(t_lip_init_stage stage, int flags) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    switch (stage) {
        case LIP_INIT_STAGE_PHY_INTF_INIT:
            LipStagePhyInit();
            break;
        case LIP_INIT_STAGE_MAC_INIT:
            {
                t_ltimer tmr;
                /* select RMII */
                AFIO->PCFR1 |= 1 << 23;

                /* Software reset */
                ETH->DMABMR |= ETH_DMABMR_SR;

                #define MAC_RESET_TOUT 10
                /* ожидание завершения сброса MAC */
                ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(MAC_RESET_TOUT));
                while ((ETH->DMABMR & ETH_DMABMR_SR) && !ltimer_expired(&tmr)) {
                    continue;
                }

                if (ETH->DMABMR & ETH_DMABMR_SR) {
                    err = LIP_ERR_INIT_MAC_RESET;
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_ERROR_FATAL, err,
                               "lip port: mac reset error. possibly phy clock not present!\n");
                    break;
                }

                //ETH_AutomaticPadCRCStrip_Enable
                uint32_t MACCR_val = ETH_Speed_100M | ETH_Mode_FullDuplex | ETH_ChecksumOffload_Enable | ETH_InterFrameGap_64Bit;
                //ETH_ForwardUndersizedGoodFrames_Enable
                //| ETH_SecondFrameOperate_Disable
                // |
                uint32_t DMAOMR_val = ETH_DropTCPIPChecksumErrorFrame_Enable | ETH_TransmitStoreForward_Enable;
                //ETH_ForwardErrorFrames_Enable
                ETH->DMAOMR = DMAOMR_val;
                //Clears the ETHERNET transmit FIFO
                ETH->DMAOMR |= ETH_DMAOMR_FTF;
                while(ETH->DMAOMR & ETH_DMAOMR_FTF); // wait flush finish

                ETH->MACCR = MACCR_val;
                //ETH_PromiscuousMode_Enable
                ETH->MACFFR = ETH_ReceiveAll_Enable | ETH_BroadcastFramesReception_Enable | ETH_MulticastFramesFilter_Perfect | ETH_UnicastFramesFilter_Perfect;

                f_tstm_start();
            }

            break;
        case LIP_INIT_STAGE_MAC_START:
            {
                const t_lip_phy_mode *mode = lip_mac_phy_mode();

                ETH->MACCR = (ETH->MACCR & ~(ETH_Speed_100M | ETH_Mode_FullDuplex))
                    | (mode->speed == LIP_PHY_SPEED_100 ? ETH_Speed_100M : 0)
                    | ((mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL) ? ETH_Mode_FullDuplex : 0);

                ETH->DMAIER = ETH_DMAIER_TIE /*| ETH_DMAIER_RIE*/ | ETH_DMAIER_NISE | ETH_DMAIER_AISE;

                //uint32_t DMABMR_val = ETH_FixedBurst_Enable | /*ETH_AddressAlignedBeats_Enable |*/ ETH_RxDMABurstLength_1Beat | ETH_TxDMABurstLength_1Beat | ETH_DMAArbitration_RoundRobin_RxTx_2_1;
                //ETH->DMABMR = DMABMR_val;
                ETH->DMARDLAR  = lip_ll_wch32descr_rx_init();
                ETH->DMATDLAR = lip_ll_wch32descr_tx_init();
                // DMA Transmission Enable
                // DMA Reception Enable
                ETH->DMAOMR |= ETH_DMAOMR_SR | ETH_DMAOMR_ST | ETH_DMAOMR_TSF;
                // Reception Enable
                // Transmission Enable
                ETH->MACCR |= ETH_MACCR_RE | ETH_MACCR_TE;
#ifdef SHOW_LIP_PHY_REGS
                {
                    extern uint8_t f_phy_addr;
                    uint16_t val;
                    lip_phy_reg_read(f_phy_addr, 1, &val);
                    lprintf("MACCR=%x phy(1)=%x\n", ETH->MACCR, val);
                    lprintf("CFGR0=%x\n", RCC->CFGR0);
                }
#endif
                f_mac_init_finish = true;
#ifdef LIP_CFG_USE_IRQ
                //NVIC_SetPriority(ETH_IRQn,1<<4);
                NVIC_EnableIRQ(ETH_IRQn);
#endif
                void fpga_test_clk(void);
                //fpga_test_clk();
                //f_tstm_test();
			}

            break;
    }
    return err;
}

#ifdef LIP_CFG_USE_IRQ
void lip_ll_irq_enable(void) {
    //NVIC_EnableIRQ(ETH_IRQn);
    __enable_irq();
}

void lip_ll_irq_disable(void) {
    //NVIC_DisableIRQ(ETH_IRQn);
    __disable_irq();
}
#endif

t_lip_errs lip_ll_mdio_read_start(uint8_t phyaddr, uint8_t regaddr) {
    /* Set the SMI interface clock, set as the main frequency divided by 42  */
    ETH->MACMIIAR = (uint32_t)ETH_MACMIIAR_CR_Div42
        | (((uint32_t)phyaddr << 11) & ETH_MACMIIAR_PA)
        | (((uint32_t)regaddr << 6) & ETH_MACMIIAR_MR);
    ETH->MACMIIAR |= ETH_MACMIIAR_MB;
    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_ll_mdio_write_start(uint8_t phyaddr, uint8_t regaddr, uint16_t value) {
    ETH->MACMIIDR = value;
    ETH->MACMIIAR = (uint32_t)ETH_MACMIIAR_CR_Div42
        | ETH_MACMIIAR_MW
        | (((uint32_t)phyaddr << 11) & ETH_MACMIIAR_PA)
        | (((uint32_t)regaddr << 6) & ETH_MACMIIAR_MR);
    ETH->MACMIIAR |= ETH_MACMIIAR_MB;

    return LIP_ERR_SUCCESS;
}

void lip_ll_ip_addr_changed(void) {
}

void phy_reset(void);

void lip_ll_stop(void) {
    NVIC_DisableIRQ(ETH_IRQn);
    ETH->DMAIER = 0;
    f_tstm_stop();

    /* Disable the MAC receiver */
    ETH->MACCR &= ~ETH_MACCR_RE;

    /*Disable the receive DMA */
    ETH->DMAOMR &= ~ETH_DMAOMR_SR;

	ETH->DMAOMR |= ETH_DMAOMR_FTF; //flush transmission fifo
    while(ETH->DMAOMR & ETH_DMAOMR_FTF); // wait flush finish

    /* Disable the Transmit DMA */
    ETH->DMAOMR &= ~ETH_DMAOMR_ST;

    /* Disable the MAC transmitter*/
    ETH->MACCR &= ~ETH_MACCR_TE;

    f_mac_init_finish = false;
}

void lip_ll_close(void) {
#if 0
	ETH->DMAOMR &= ~ETH_DMAOMR_SR; //disable dma reception
	ETH->MACCR &= ~ETH_MACCR_RE; // disable mac reception
	ETH->DMAOMR |= ETH_DMAOMR_FTF; //flush transmission fifo
    while(ETH->DMAOMR & ETH_DMAOMR_FTF); // wait flush finish
	ETH->DMAOMR &= ~ETH_DMAOMR_ST; //disable dma transmission
	ETH->MACCR &= ~ETH_MACCR_TE; //disable mac transmission
    // Phy reset
    //phy_reset();
	RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, ENABLE);
#endif

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC_Rx, DISABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC_Tx, DISABLE);
    // нельзя выключать ETHMAC, т.к. изменяется mapping SDIO
	//RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, ENABLE);
}

void lip_ll_set_mac_addr(const uint8_t *mac_addr) {
    ETH->MACA0LR =   ((uint32_t) mac_addr[3] << 24) |
                                    ((uint32_t) mac_addr[2] << 16) |
                                    ((uint32_t) mac_addr[1] << 8) |
                                    ((uint32_t) mac_addr[0]);
    ETH->MACA0HR = ((uint32_t) mac_addr[5] << 8) |
                                    ((uint32_t) mac_addr[4]);
}

bool lip_ll_mdio_rdy(void) {
    return !(ETH->MACMIIAR & ETH_MACMIIAR_MB);
}

t_lip_errs lip_ll_mdio_data(uint16_t *data) {
    *data = (unsigned)(ETH->MACMIIDR);
    return 0;
}
