/***************************************************************************//**
 @defgroup lip_port Порт
 @brief Модуль, реализующий операции, зависящие от конкретного MAC-контроллера.

  Функции, специфичные для конкретного контроллера, вынесены в модуль порта
  для данного контроллера. Все порты находятся в директории ports/"название порта"
  и включают в себя файл lip_ll_"название порта".c.

  Порт должен реализовать:
    1. Инициализацию MAC-контроллера lip_ll_init(), которая разбита на несколько стадий
    2. Останов обмена (lip_ll_stop()) и выключение контроллера (lip_ll_close())
    3. Интерфейс доступа к регистрам PHY через MII management interface
       (lip_ll_mdio_read_start(), lip_ll_mdio_write_start(), lip_ll_mdio_rdy(),
       lip_ll_mdio_data()).
    4. Передачу пакетов (lip_ll_tx_frame() с ответным вызовом lip_mac_tx_frame_done_cb())
       и прием пакетов (по каждому принятому пакету вызвать lip_mac_rx_proc_frame()).
    5. Опционально, при реализации multicast, поддержку входа/выхода из группы.

  Порт может также иметь свой набор настроек конфигурации. Пример этих настроек
  должен быть приведен в файле ports/_lip_port_conf.h. При этом реально используется
  только один файл конфигурации lip_conf.h, т.е. эти настройки должны быть в
  проекте либо в общем файле конфигурации, либо в отдельном, который включен в общий.



******************************************************************************/

/***************************************************************************//**
 @addtogroup lip_port
 @{
 @file lip_ll.h
 @brief Файл содержит определения функций, которые должен реализовать
        порт для конкретного контроллера, и типов, которые используются в этих
        функциях.
 @author Borisov Alexey <borisov@lcard.ru>
*******************************************************************************/

#ifndef LIP_LL_H_
#define LIP_LL_H_


#include "lip_config.h"
#include "lip/link/mac/lip_mac_cfg_defs.h"
#include "lip/link/lip_vlan_cfg_defs.h"
#include "lip/app/ptp/lip_ptp_cfg_defs.h"
#include "lip/lip_defs.h"
#include "lip_ll_cfg_defs.h"
#if LIP_PTP_ENABLE
#include "lip/app/ptp/lip_ptp_defs.h"
#endif





/** Флаги, управляющие начальной инициализацией стека и phy */
typedef enum {
    /** Инициализация mac в режиме петли для тестов */
    LIP_INIT_FLAGS_MAC_LOOPBACK       = 0x100,
    /** Запрет фильтрации входных пакетов */
    LIP_INIT_FLAGS_MAC_RX_FILTER_DIS  = 0x200,
    /** Запрет оптимизации обработки входных пакетов */
    LIP_INIT_FLAGS_MAC_RX_HWPROC_DIS  = 0x400
} t_lip_init_flags;

/** Этапы инициализации. При инициализации интерфейса, lip_ll_init()
    вызывается несколько раз, с соответствующим указанием этапа */
typedef enum {
    /** Стадия перед началом инициализации. На ней должны быть настроены
        пины интерфейса с Phy */
    LIP_INIT_STAGE_PHY_INTF_INIT,
    /** Начальная инициализация MAC-контрллера. Должны быть настроены
        необходимые параметры для дальнейшего использования интерфейса к
        регистрам Phy.
        Выполняется после сброса Phy (если определено #LIP_PHY_HARDWARE_RESET).*/
    LIP_INIT_STAGE_MAC_INIT,
    /** Финальная конфигурация MAC-контроллера.
        Должны быть выполнены настройки в соответствии с режимом линка
        (можно получить через lip_phy_mode() и lip_phy_speed()) и
        разрешена передача и прием */
    LIP_INIT_STAGE_MAC_START
} t_lip_init_stage;


typedef  enum {
    /** Признак, что ответ на ARP пакет уже послан аппаратным образом. */
    LIP_LL_RXFLAGS_ARP_RESP_SENT            = (1UL << 0),
    /** Признак, что контрольная сумма IP-заголовка уже проверена аппаратно */
    LIP_LL_RXFLAGS_IP_CHECKSUM_CHECKED      = (1UL << 1),
    /** Признак, что контрольная сумма транспортного уровня (TCP/UDP/ICMP) для пакета уже проверена аппаратно */
    LIP_LL_RXFLAGS_TRANSP_CHECKSUM_CHECKED     = (1UL << 2),
    /** Признак, что принят PTP пакет */
    LIP_LL_RXFLAGS_PTP_PACKET               = (1UL << 3),
} t_lip_ll_rx_flags;


typedef enum {
    LIP_FRAME_RXINFO_FLAG_PORT_VALID   = (1UL << 0),
    LIP_FRAME_RXINFO_FLAG_TSTAMP_VALID = (1UL << 1),
    LIP_FRAME_RXINFO_FLAG_VLAN_PCP_VALID   = (1UL << 2)
} t_lip_rx_frame_info_flags;

typedef enum {
    LIP_FRAME_TXINFO_FLAG_PORTS_FWD     = (1UL << 0), /**< Признак, что нужно пакет на указанные вручную порты */
    LIP_FRAME_TXINFO_FLAG_PORTS_BLK_OVD = (1UL << 1), /**< Разрешение передачи на заблокированные порты */
    LIP_FRAME_TXINFO_FLAG_TSTAMP_REQ    = (1UL << 2), /**< Необходимость получить метку времени для пакета */
    LIP_FRAME_TXINFO_FLAG_VLAN          = (1UL << 3), /**< Признак, что пакет должен принадлежать указанному vlan */
    LIP_FRAME_TXINFO_FLAG_RAW           = (1UL << 4), /**< Признак, что пакет нужно посылать как есть (без добавления PAD, CRC и т.п.) */
    LIP_FRAME_TXINFO_FLAG_FINISH_CB     = (1UL << 5), /**< Необходимость вызова пользовательской callback-функции по завершению передачи */
    LIP_FRAME_TXINFO_FLAG_SRC_MAC_BCAST = (1UL << 6), /**< В качестве источника используется broadcast mac, вместо MAC хоста */
} t_lip_tx_frame_info_flags;


struct st_lip_tx_frame;

typedef void (*t_lip_tx_frame_cb)(struct st_lip_tx_frame *frame);



typedef struct {
    uint32_t ll_flags;
    uint16_t flags; /**< Набор флагов из #t_lip_rx_frame_info_flags */
#if LIP_MULTIPORT_ENABLE
    uint32_t rx_port; /**< Маска портов, указывающая с какого порта принят пакет (если установлен флаг #LIP_FRAME_RXINFO_FLAG_PORT_VALID) */
#endif
#if LIP_VLAN_ENABLE
    uint8_t vlan_pcp; /**< VLAN PCP, код приоритета пакета из тэга VLAN (если установлен флаг #LIP_FRAME_RXINFO_FLAG_VLAN_PCP_VALID) */
#endif
#if LIP_PTP_ENABLE
    t_lip_ptp_tstmp tstmp;
#endif
} t_lip_rx_frame_info;


typedef struct {
    t_lip_tx_frame_cb finish_cb;
    void *user_ptr;
    uint16_t flags; /**< Набор флагов из #t_lip_tx_frame_info_flags */
    uint8_t  priority; /**< Приоритет передаваемого пакета */
#if LIP_PTP_ENABLE
    uint8_t  ptp_msg_type;
#endif
#if LIP_MULTIPORT_ENABLE
    uint32_t port_mask; /**< Маска портов, на которые должен быть передан пакет (если установлен флаг #LIP_FRAME_TXINFO_FLAG_PORTS_FWD) */
#endif
#if LIP_VLAN_ENABLE
    uint16_t vlan;
#endif
} t_lip_tx_frame_info;

typedef enum {
    LIP_FRAME_TXSTATUS_FLAG_FINISH      = 1 << 0,
    LIP_FRAME_TXSTATUS_FLAG_ERROR       = 1 << 1,
    LIP_FRAME_TXSTATUS_FLAG_TSTMP_VALID = 1 << 2,
} t_lip_tx_frame_status_flags;

typedef struct {
    uint16_t flags; /**< Набор флагов из #t_lip_tx_frame_status_flags */
    uint16_t error;
#if LIP_PTP_ENABLE
    t_lip_ptp_tstmp tstmp;
#endif
} t_lip_tx_frame_status;

typedef struct {
    uint8_t *start_ptr; /* указатель на начало буфера */
    uint8_t *end_ptr;   /* указатель на байт за последним доступным для заполнения байтом буфера */
    uint8_t *cur_ptr;   /* текущий указатель для заполнения буфера (меняется разными уровнями стека) */
} t_lip_tx_hdr_buf;

/* описание кадра для передачи */
typedef struct st_lip_tx_frame {
    t_lip_tx_frame_info info; /* информация для формирования кадра */
    t_lip_tx_frame_status status; /* статус выполнения передачи кадра */
    t_lip_tx_hdr_buf buf;  /* описание основного буфера данных для кадра */
    const uint8_t *exdata_ptr; /* указетель на второй вспомогательный буфер с данными для передачи после buf, если требуется */
    uint32_t exdata_size;    /* размер на второй вспомогательный буфер с данными для передачи после buf, если требуется */
} t_lip_tx_frame;


typedef  struct {
    t_lip_rx_frame_info info;
    t_lip_payload       pl;
    uint8_t fifo_num;
}  t_lip_rx_frame;

typedef struct {
    t_lip_rx_frame_info info;  /**< Информация о принятом кадре от порта */
    t_lip_payload       pl_main; /**< описание буфера с принятыми данными кадра (или первой его части, если действителен tail) */
#if LIP_LL_PORT_FEATURE_RX_SPLIT
    t_lip_payload       pl_tail; /**< описание буфера с второй частью данных кадра (если не используется, то размер должен быть 0) */
#endif
} t_lip_rx_ll_frame;

/***************************************************************************//**
@brief Инициализация контроллера.

Данная функция вызывается несколько во время инициализации phy с разными
параметрами stage, который и определяет, что сейчас должно быть
инициализировано
@param[in] stage    Этап инициализации, определяет, что должно быть выполнено
                    по вызову функций. Одно из значений из перечисления
                    #t_lip_init_stage.
@param[in] flags   Набор флагов из #t_lip_init_flags, управляющих инициализацией
*******************************************************************************/
t_lip_errs lip_ll_init(t_lip_init_stage stage, int flags);


/***************************************************************************//**
   @brief Завершение обмена данными по контроллеру.

   Функция должна остановить все передачи и запретить новые. На этом
    этапе PHY еще работает.
 *****************************************************************************/
void lip_ll_stop(void);

/***************************************************************************//**
   @brief Выключение контроллера.

   Функция отключает сам контроллер (до этого обмен должен быть завершен с
   помощью lip_ll_stop().
   На этом этапе PHY уже может быть отключено.
 ******************************************************************************/
void lip_ll_close(void);

void lip_ll_pull(void);

/***************************************************************************//**
   @brief Установка mac-адреса

   Функция передает установленный mac-адрес. Может использоваться контроллером
   для аппаратной фильтрации входных пакетов.
   @param[in] mac_addr  Установленный MAC-адрес (массив из #LIP_MAC_ADDR_SIZE байт)
 ******************************************************************************/
void lip_ll_set_mac_addr(const uint8_t *mac_addr);




/* ---------- Функции для работы с phy по MII Management интерфейсу ----------*/
/***************************************************************************//**
   @brief Запуск операции записи в регистр PHY
   @param[in] dev_addr  Адрес физического устройства
   @param[in] reg_addr  Адрес регистра
   @param[in] value     Записываемое значение
   @return Код ошибки
 ******************************************************************************/
t_lip_errs lip_ll_mdio_write_start(uint8_t phyaddr, uint8_t regaddr, uint16_t value);
/***************************************************************************//**
   @brief Запуск операции чтения из регистра PHY
   @param[in] dev_addr  Адрес физического устройства
   @param[in] reg_addr  Адрес регистра
   @return Код ошибки
 ******************************************************************************/
t_lip_errs lip_ll_mdio_read_start(uint8_t phyaddr, uint8_t regaddr);
/***************************************************************************//**
   @brief Проверка, завершена ли предыдущая операция чтения/записи регистра PHY
   @return 1, если операция завершена, 0 - если интерфейс занят
 ******************************************************************************/
bool lip_ll_mdio_rdy(void);
/***************************************************************************//**
   @brief Получения данных, считанных из регистра PHY во время последней операции

   Функция вызывается после вызова lip_ll_mdio_read_start() с последующим
   ожиданием, пока lip_ll_mdio_rdy() вернет 1, чтобы прочитать считанное значение
 ******************************************************************************/
t_lip_errs lip_ll_mdio_data(uint16_t *data);


/**  @brief Разрешение прерываний от MAC-контроллера */
void lip_ll_irq_enable(void);
/**  @brief Запрет прерываний от MAC-контроллера */
void lip_ll_irq_disable(void);

/***************************************************************************//**
   @brief Начало передачи сфорированного кадра данных

   По вызову данной функции порт должен поставить кадр в свою очередь на передачу.
   В случае успешного добавления кадра на передачу функция возвращает LIP_ERR_SUCCESS
   и после должна вызвать lip_mac_tx_frame_done_cb() по завершению передачи кадра,
   предварительно обновив статус передачи кадра. Вызов lip_mac_tx_frame_done_cb()
   может выполняться из обработчика прерывания.

   @param frame         Описание передаваемого кадра
   @return              Код ошибки
 ******************************************************************************/
t_lip_errs lip_ll_tx_frame(t_lip_tx_frame *frame);

#if LIP_PTP_ENABLE && LIP_LL_PORT_FEATURE_HW_PTP_TSTMP
    void lip_ll_tstmp_enable(void);
    void lip_ll_tstmp_disable(void);
    bool lip_ll_tstmp_is_enabled(void);
    t_lip_errs lip_ll_tstmp_read(t_lip_ptp_tstmp *tstmp);
#endif



/* ----------------- Функции для работы с групповыми передачами --------------*/
#ifdef LIP_USE_IGMP
    /***********************************************************************//**
       @brief Вход в группу с указанным MAC-адресом

       После вызова этой функции аппаратно должен быть разрешен прием
       пакетов с указанным групповым MAC-адресом

       @param[in] mac  MAC-адрес группы
       @return         Код ошибки
     */
    t_lip_errs lip_ll_join(const uint8_t* mac);
    /***********************************************************************//**
       @brief Выход из группы.

       При вызове этой функции порт может аппаратно запретить прием
       пакетов с указанным групповым MAC-адресом
       @param[in] mac  MAC-адрес группы
       @return         Код ошибки
     **************************************************************************/
    t_lip_errs lip_ll_leave(const uint8_t* mac);
#endif

#if LIP_LL_PORT_FEATURE_IPADDR_TRACKING
    void lip_ll_ip_addr_changed(void);
#endif



#endif /* LIP_LL_H_ */

/** @}*/
