/*
 * lip_eth.c
 *
 *  Created on: 31.08.2010
 *      Author: borisov
 *
 *      Файл содержит функции промежуточного уровня - между портом для mac-контроллера
 *        и протоколами сетевого уровня (IP, ARP)
 */

#include "lip/link/lip_eth.h"
#include "lip/misc/lip_rand_cfg_defs.h"
#if LIP_RAND_ENABLE
#include "lip/misc/lip_rand.h"
#endif
#include "lip/app/ptp/lip_ptp_cfg_defs.h"
#if LIP_PTP_ENABLE
#include "lip/app/ptp/lip_ptp_private.h"
#endif
#include "lip/ll/lip_ll.h"
#include "lip/lip_private.h"
#include "lip_eth_private.h"
#include <string.h>


static t_lip_tx_frame *f_tx_frame;


const uint8_t g_lip_broadcast_mac[LIP_MAC_ADDR_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

static uint8_t f_mac_addr[LIP_MAC_ADDR_SIZE];

void lip_eth_set_mac_addr(const uint8_t *mac) {
    memcpy(f_mac_addr, mac, LIP_MAC_ADDR_SIZE);
#if LIP_RAND_ENABLE
    /* генератор случайных числе инициализируется с использованием mac-адреса */
    lip_rand_init();
#endif
#if LIP_PTP_ENABLE
    lip_ptp_mac_addr_update(mac);
#endif
}

/******************************************************************************************************
 *   Передача пакета данных через ehternet. Функция получает буфер для передачи,
 *      подготавливает в нем LLC заголовок, копирует туда переданные данные и посылает пакет
 *      Функция приводит к копированию данных, в отличии от lip_eth_prepare/lip_eth_flush
 *      Используется когда данные уже сформированы в другом буфере и копировать все равно придется
 *      в частности ARP - который использует входной буфер с принятыми данными для формирования пакета)
 *   Параметры:
 *      buf  (in) - указатель на массив с данными для передачи
 *      size (in) - размер данных для передачи (без LLC заголовка)
 *      type (in) - тип/размер из LLC заголовка (определяет сетевой протокол или длину...)
 *      dest_addr - mac-адрес назначения при передаче пакета
 *   Возвращаемое значение:
 *      код ошибки
 ******************************************************************************************************/
t_lip_errs lip_eth_send(const uint8_t *buf, size_t size, t_lip_ethtype type, const uint8_t *dest_addr) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    //получаем буфер для передачи
    int max_size;
    uint8_t *tx_buf = lip_eth_prepare_def(type, dest_addr, &max_size);
    if (tx_buf != NULL)  {
        if ((int)size > max_size) {
            err = LIP_ERR_TXBUF_TOO_SMALL;
        } else {
            memcpy(tx_buf, buf, size); //копируем данные
            lip_eth_flush(size); //передаем кадр
        }
    } else {
        err = LIP_ERR_TXBUF_NOT_FOUND;
    }
    return err;
}



/***********************************************************************************************************
 *  Функция получает буфер для передачи пакета и подготавливает в нем заголовок LLC.
 *        Возвращает указатель на место в буфере сразу за заголовком
 *        - куда сетевой протокол может сохранять данные
 *      Используется совместно с lip_eth_flush для передачи пакета без лишнего копирования
 *  Параметры
 *       type      (in)  - тип/размер из LLC заголовка (определяет сетевой протокол или длину...)
 *      dest_addr (in)  - mac-адрес назначения при передаче пакета
 *      max_size  (out) - возвращает размер выделенного буфера (>0) или код ошибки (<0)
 *  Возвращаемое значение
 *      Указатель на буфер для данных протокола сетевого уровня при успехе или 0 при ошибке
 ***********************************************************************************************************/
t_lip_tx_frame *lip_eth_frame_prepare(const t_lip_tx_frame_info *finfo, t_lip_ethtype type, const uint8_t *dest_addr) {
    /* проверяем, возможна ли передача пакета в принципе */
    t_lip_errs err = lip_check_tx_enabled();
    t_lip_tx_frame *tx_frame = NULL;
    if (err == LIP_ERR_SUCCESS) {
        /* получаем буфер для передачи */
        tx_frame = lip_tx_frame_prepare(finfo);
        if (tx_frame != NULL) {
            /* заполняем отдельно поля, а не через структуру, поскольку порядок
             * и налчие полей может быть разный, в зависимости от поддержки
             * аппаратной вставки адреса источника, добавления vlan и т.д. */

            uint8_t *hdr_ptr = tx_frame->buf.cur_ptr;
            memcpy(hdr_ptr, dest_addr, LIP_MAC_ADDR_SIZE);
            hdr_ptr += LIP_MAC_ADDR_SIZE;

            /* если в качестве источника используется bcast, то вставляем вручную,
             * иначе вставляем свой mac, но только если это уже не делается
             * аппаратно на уровне порта */
            if (finfo->flags & LIP_FRAME_TXINFO_FLAG_SRC_MAC_BCAST) {
                memset(hdr_ptr, 0xFF, LIP_MAC_ADDR_SIZE);
                hdr_ptr += LIP_MAC_ADDR_SIZE;
#if !LIP_LL_PORT_FEATURE_HW_TX_ETH_SA_INS
            } else {
                memcpy(hdr_ptr, f_mac_addr, LIP_MAC_ADDR_SIZE);
                hdr_ptr += LIP_MAC_ADDR_SIZE;
#endif
            }

#if LIP_VLAN_ENABLE
            /* в случае необходимости передать vlan-tag добавляем его
             * после адресов и до типа */
            if (tx_frame->info.flags & LIP_FRAME_TXINFO_FLAG_VLAN) {
                t_lip_vlan_hdr *vlan_hdr = (t_lip_vlan_hdr *)hdr_ptr;
                vlan_hdr->type = HTON16(LIP_ETHTYPE_CVLAN);
                vlan_hdr->tci  = HTON16(tx_frame->info.vlan);
                hdr_ptr += sizeof(t_lip_vlan_hdr);
            }
#endif
            *hdr_ptr++ = (type >> 8) & 0xFF;
            *hdr_ptr++ = type & 0xFF;
            tx_frame->buf.cur_ptr = hdr_ptr;
        }
    }

    return tx_frame;
}
void *lip_eth_prepare_def(t_lip_ethtype type, const uint8_t *dest_addr, int *max_size) {
    void *ret = NULL;
    static t_lip_tx_frame_info finfo_def;
    f_tx_frame = lip_eth_frame_prepare(&finfo_def, type, dest_addr);
    if (f_tx_frame) {
        ret = f_tx_frame->buf.cur_ptr;
        if (max_size) {
            *max_size = f_tx_frame->buf.end_ptr - f_tx_frame->buf.cur_ptr;
        }
    }
    return ret;
}



/***********************************************************************************************************
 *  Функция приводит к передаче пакета данных, который должен быть заранее подготовлен!
 *  Параметры
 *      size (in)- размер данных для передачи (без размера LLC заголовка)
 *  Возвращаемое значение:
 *     Код ошибки
 * ********************************************************************************************************/
t_lip_errs lip_eth_flush_split(size_t size, const uint8_t *data, int data_size) {
    f_tx_frame->exdata_ptr = data;
    f_tx_frame->exdata_size = data_size;
    f_tx_frame->buf.cur_ptr += size;
    return lip_eth_frame_flush(f_tx_frame);
}


const uint8_t *lip_eth_cur_mac() {
    return f_mac_addr;
}


t_lip_errs lip_eth_process_packet(t_lip_rx_frame *rx_frame, t_lip_eth_rx_packet *eth_packet) {
    const t_lip_payload *eth_pl = &rx_frame->pl;
    t_lip_errs err = eth_pl->size < LIP_ETH_HDR_SIZE ? LIP_ERR_RXBUF_TOO_SMALL : LIP_ERR_SUCCESS;
    if (err == LIP_ERR_SUCCESS) {
        const t_lip_eth_hdr *eth_hdr = (const t_lip_eth_hdr *)eth_pl->data;
        eth_packet->ll_frame = rx_frame;
        eth_packet->type = NTOH16(eth_hdr->type);
        eth_packet->pl.size = eth_pl->size - LIP_ETH_HDR_SIZE;
        eth_packet->pl.data = eth_pl->data + LIP_ETH_HDR_SIZE;

        eth_packet->flags = 0;

        if (lip_eth_is_bcast(eth_hdr->da))
            eth_packet->flags |= LIP_ETH_FLAGS_BROADCAST;
    }
    return err;
}

