#ifndef LIP_PHY_DEFS_H
#define LIP_PHY_DEFS_H

#include <stdint.h>
#include "lip/lip_defs.h"
#include "lip_phy_mdio.h"
#include "lip_phy_cfg_defs.h"
#include "ltimer.h"

#define LIP_PHY_MAX_PAIR_CNT     4 /**< Максимальное число диф. пар на физическом уровне одного физического канала */

#if LIP_PHY_USE_STATS
    #include "lip_phy_stats_defs.h"
#endif

#if LIP_PHY_USE_CABLE_TEST
#include "lip_phy_cable_test_defs.h"
#endif

#define LIP_PHY_PAIR_NUM_INVALID 0xFF /**< Код для задания недействительного номера диф. пары физического канала */

/** Скорость работы интерфейса */
typedef enum {
    LIP_PHY_SPEED_INVALID   = 0, /**< Не определена */
    LIP_PHY_SPEED_10        = 1, /**< 10 Mbit/s */
    LIP_PHY_SPEED_100       = 2, /**< 100 Mbit/s */
    LIP_PHY_SPEED_1000      = 3, /**< 1000 Mbit/s */
    LIP_PHY_SPEED_2500      = 4, /**< 2500 Mbit/s */
} t_lip_phy_speed;

/** @brief Флаги режима PHY
 *
 *  Набор флагов, которые вмести со скоростью определяют режим работы микросхемы PHY */
typedef enum {
    LIP_PHY_MODE_FLAG_DUPLEX_FULL       = (1UL << 0), /**< Признак работы в Full-Duplex (1) или Half-Duplex (0) */
    LIP_PHY_MODE_FLAG_MASTER            = (1UL << 1), /**< Признак работы как Master (1) или Slave (0) для 1000-T */
} t_lip_phy_mode_flags;

/** @brief Флаги состояния PHY */
typedef enum {
    LIP_PHY_STATE_FLAG_LINK_UP          = (1UL << 0), /**< Признак наличия действительного подключения */
    LIP_PHY_STATE_FLAG_AN_EN            = (1UL << 1), /**< Признак включения Auto-Negatiation у PHY */
    LIP_PHY_STATE_FLAG_WORK_CFG         = (1UL << 2), /**< Признак что PHY был настроен после сброса */
    LIP_PHY_STATE_FLAG_ERROR            = (1UL << 3), /**< Признак ошибки инициализации PHY */
} t_lip_phy_state_flags;


/** @brief Флаги конфигурации PHY */
typedef enum {
    LIP_PHY_CFG_FLAG_AN_DISABLED        = (1UL << 0), /**< Запрет Auto-Negatiation. Должен быть указан явно режим работы в manual_mode */
    LIP_PHY_CFG_FLAG_MULTIPORT          = (1UL << 1), /**< Признак, что устройство является многопортовым.
                                                           Имеет значения для Auto-Negation в 1000-T при определении Master-Slave */
    LIP_PHY_CFG_FLAG_ED_PD_EN           = (1UL << 2), /**< Разрешение режима понижения потребления без подключения Energy Detect Power Down, если поддерживается PHY */
    LIP_PHY_CFG_FLAG_QW_EN              = (1UL << 3), /**< Разрешение Quite-Wire Filter для улучшения EMC, если поддерживается PHY */
    LIP_PHY_CFG_FLAG_TXCLK_DLY_EN       = (1UL << 4), /**< Включение задержки клока TX на стороне PHY для интерфейса RGMII (по умолчанию TX на стороне MAC, RX - phy) */
} t_lip_phy_cfg_flags;

/** @brief Набор различных возможность, использование которых может быть согласовано
 *         с удаленной стороной с помощью процесса Auto-Negation */
typedef enum {
    LIP_PHY_AN_FEAT_MODE_10_T_FD        = (1UL << 0), /**< Режим 10BASE-T Full-Duplex */
    LIP_PHY_AN_FEAT_MODE_10_T_HD        = (1UL << 1), /**< Режим 10BASE-T Half-Duplex */
    LIP_PHY_AN_FEAT_MODE_100_TX_FD      = (1UL << 2), /**< Режим 100BASE-TX Full-Duplex */
    LIP_PHY_AN_FEAT_MODE_100_TX_HD      = (1UL << 3), /**< Режим 100BASE-TX Half-Duplex */
    LIP_PHY_AN_FEAT_MODE_100_T2_FD      = (1UL << 4), /**< Режим 100BASE-T2 Full-Duplex */
    LIP_PHY_AN_FEAT_MODE_100_T2_HD      = (1UL << 5), /**< Режим 100BASE-T2 Half-Duplex */
    LIP_PHY_AN_FEAT_MODE_100_T4         = (1UL << 6), /**< Режим 100BASE-T4 */
    LIP_PHY_AN_FEAT_MODE_1000_T_FD      = (1UL << 8), /**< Режим 1000BASE-T Full-Duplex */
    LIP_PHY_AN_FEAT_MODE_1000_T_HD      = (1UL << 9), /**< Режим 1000BASE-T Half-Duplex */

    LIP_PHY_AN_FEAT_PAUSE_SYM           = (1UL << 16), /**< Симметричное управления потоком */
    LIP_PHY_AN_FEAT_PAUSE_ASYM          = (1UL << 17), /**< Асимметричное управление потоком */

    LIP_PHY_AN_FEAT_EEE_100_TX          = (1UL << 18), /**< Использование EEE для 100BASE-TX */
    LIP_PHY_AN_FEAT_EEE_1000_T          = (1UL << 19)  /**< Использование EEE для 1000BASE-T */
} t_lip_phy_an_feature;

/** @brief Нестандартные возможности, поддерживаемые PHY */
typedef enum {
    LIP_PHY_SPEC_FEAT_REMOTE_LOOP       = (1UL << 0), /**< Поддержка режима работы #LIP_PHY_OPMODE_REMOTE_LOOP */
    LIP_PHY_SPEC_FEAT_ED_PD             = (1UL << 1), /**< Поддержка настройки #LIP_PHY_CFG_FLAG_ED_PD_EN */
    LIP_PHY_SPEC_FEAT_QW                = (1UL << 2), /**< Поддержка настройки #LIP_PHY_CFG_FLAG_QW_EN */
    LIP_PHY_SPEC_FEAT_LINK_MD           = (1UL << 3), /**< Поддержка режима работы #LIP_PHY_OPMODE_LINK_MD_TEST */
} t_lip_phy_spec_features;


/** @brief Режимы работы PHY */
typedef enum {
    LIP_PHY_OPMODE_NORMAL               = 0, /**< Штатный режим работы */
    LIP_PHY_OPMODE_PD                   = 1, /**< Режим отключенного питания */
    LIP_PHY_OPMODE_LOCAL_LOOP           = 2, /**< Работа в режиме локальной петли (со стороны контроллера) для теста интерфейса контроллер-PHY */
    LIP_PHY_OPMODE_REMOTE_LOOP          = 3, /**< Работа в режиме удаленной петли (со стороны сети) для теста канала PHY на другой стороне */
    LIP_PHY_OPMODE_LINK_MD_TEST         = 4, /**< Тест кабельного подключения */
} t_lip_phy_opmode;

/** @brief Режим передачи данных по сети для PHY  */
typedef struct {
    uint8_t speed; /**< Скорость работы. Значение из #t_lip_phy_speed */
    uint8_t flags; /**< Набор флагов из #t_lip_phy_mode_flags */
} t_lip_phy_mode;

static LINLINE bool lip_phy_mode_is_valid(const t_lip_phy_mode *mode) {
    return mode->speed != LIP_PHY_SPEED_INVALID;
}

/** @brief Параметры текущего состояния PHY */
typedef struct {
    t_lip_phy_mode mode; /**< Текущий режим работы PHY */
    uint32_t flags; /**< Флаги состояния из #t_lip_phy_state_flags */
} t_lip_phy_state;


/** @brief Настройки работы PHY */
typedef struct {
    uint8_t  opmode; /**< Рабочий режим. Значение из #t_lip_phy_opmode */
    uint16_t flags;  /**< Флаги из #t_lip_phy_cfg_flags, определяющие дополнительные параметры настройки */
    union {
        uint32_t       an_dis_features; /**< Для рабочего режима с Auto-Negatiation определяет,
                                             какие поддерживыемые PHY возможности будут исключены
                                             из списка согласуемых параметров */
        t_lip_phy_mode manual_mode; /**< При режиме без Auto-Negation явно задает настраиваемых режим передачи */
    };
} t_lip_phy_cfg;


typedef t_lip_errs (* t_lip_phy_spec_cfg)(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_cfg *cfg);
typedef t_lip_errs (* t_lip_phy_get_mode)(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_mode *mode);
typedef t_lip_errs (* t_lip_phy_read_intflags)(const t_lip_phy_mdio_acc *regs_acc, uint32_t *intflags);
#if LIP_PHY_USE_STATS
    typedef t_lip_errs (* t_lip_phy_update_stats)(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_state *state, t_lip_phy_stats *stats);
#endif




/** Структура описывает свойства функции конкретной микросхемы PHY уровня и соедержит
    указатели на функции, выполняющие процедуры, специфические для данного типа PHY  */
typedef struct {
    const char *name; /**< Название PHY (используется только для отладки) */
    uint32_t model_id;     /**< Идентификатор данного типа PHY (может использоваться макрос LIP_PHY_MODEL_ID(vendor, model)) */
    uint32_t an_features;  /**< Поддерживаемые PHY возможности, согласуемые через Auto-Negatiation. Набор флагов из #t_lip_phy_an_feature */
    uint32_t spec_features; /**< Дополнительные нестандартные возможности, поддерживаемые PHY. Набор флагов из #t_lip_phy_spec_features */
    t_lip_phy_spec_cfg spec_cfg; /**< Функция инициализации vendor-specific регистров для конкретной модели PHY,
                                     или NULL, если не требуется */
    t_lip_phy_get_mode get_mode; /**< Функция для полчения текущего режима передачи PHY */
    t_lip_phy_read_intflags rd_intflags; /**< Чтения флагов прерывания PHY с их сбросом */
#if LIP_PHY_USE_STATS
    t_lip_phy_update_stats update_stats; /**< Функция запуска/продвижения опроса статистики PHY.  */
#endif
#if LIP_PHY_USE_CABLE_TEST
    t_lip_phy_cable_test_check_info cable_check; /**< Функции проверки кабеля, если поддерживаются */
#endif
} t_lip_phy_info;


#define LIP_PHY_ADDR_MSK_ALL        0xFFFFFFFF  /**< Маска адреса подключения PHY, означающая, что PHY может иметь любой адрес (от 0 до 31) */
#define LIP_PHY_ADDR_MSK_NOT_ZERO   0xFFFFFFFE  /**< Маска адреса подключения PHY, означающая, что PHY может иметь любой адрес кроме 0.
                                                     Как правило используется в случае, если PHY могут отвечать на 0-ой адрес как широковещаетельный и
                                                     при этом подключено большое одного PHY */
#define LIP_PHY_ADDR_MSK_FIXED(n)   (0x1UL << n) /**< Маска адреса подключения PHY, означающая, что PHY должен быть доступен по фиксируемому адресу */

typedef struct {
    uint8_t init_state; /* состояние автомата инициализации PHY */
    bool link_bg_check_req; /* признак, что был явный запрос проверки статуса линка */
    t_lclock_ticks link_check_time; /* время последней проверки наличия линка */
#if LIP_PHY_USE_CABLE_TEST
    t_lip_phy_cable_test_data cable_test; /* контекст и результат выполнения теста кабеля */
#endif
} t_lip_phy_internal_ctx;


/** Стурктура с контекстом одной микросхемы физического уровня */
typedef struct {
    const struct st_lip_phy_iface_descr *iface_descr; /**< Описание интерфейса подключения PHY */
    t_lip_phy_mdio_acc reg_acc; /**< Информация для доступа к регистрам PHY после его обнаружения
                                     с учетом определенного адреса найденного PHY */
    const t_lip_phy_info *devinfo; /**< Информация о типе подключенной микросхеме PHY */
    t_lip_phy_cfg cfg; /**< Текущие настройки PHY */
    t_lip_phy_state state; /**< Текущее состояние PHY (cкорость, режимы работы) */
#if LIP_PHY_USE_STATS
    t_lip_phy_stats stats; /**< Результаты расчета статститки */
#endif
    t_lip_phy_internal_ctx internal; /**< Внутренние параметры текущего состояния, не доступные вне
                                          модуля PHY */
    void *user_ctx; /**< Указатель на пользовательский контекст, связанный с данным PHY.
                         Самим стеком явно не используется */
} t_lip_phy_ctx;



typedef void (* t_lip_phy_hw_reset_start)(t_lip_phy_ctx *phy_ctx);
typedef bool (* t_lip_phy_hw_reset_check_done)(t_lip_phy_ctx *phy_ctx);
typedef t_lip_errs (* t_lip_phy_user_pre_detect)(t_lip_phy_ctx *phy_ctx);
typedef t_lip_errs (* t_lip_phy_user_cfg)(t_lip_phy_ctx *phy_ctx);
typedef void (* t_lip_phy_mode_changed)(t_lip_phy_ctx *ctx);
typedef void (* t_lip_phy_link_status_changed)(t_lip_phy_ctx *ctx);


typedef enum {
    /**< Определяет, нужно ли выполнять ручной периодический опрос PHY
     *   Должен быть установлен, если опрос состояния PHY не выполняется
     *   по прерываниям */
    LIP_PHY_IFACE_FLAG_STATUS_POLL = 1UL << 0
} t_lip_phy_iface_flags;

/** Пользовательское описание интерфейса работы c одним PHY.
    Задается в зависимости от способа подключения PHY и необходимости
    пользовательской обработки различных событий интерфейса с PHY */
typedef struct st_lip_phy_iface_descr {
    const char *name; /**< Пользовательское название порта (только для отладки) */
    const t_lip_phy_mdio_iface *regs_iface; /**< Интерфейс доступа к регистрам MDIO */
    uint32_t phy_addr_msk; /**< Маска адресов на MDIO, на которых может находится PHY.
                                Для задания используются макросы #LIP_PHY_ADDR_MSK_ALL,
                                #LIP_PHY_ADDR_MSK_NOT_ZERO и #LIP_PHY_ADDR_MSK_FIXED */
    int8_t port_idx; /**< Номер порта, которому соответствует phy */
    uint32_t flags; /**< Набор флагов из #t_lip_phy_iface_flags */
    /**< Функции аппаратного сброса PHY внешним способом перед началом работы, если тебуется */
    struct  {
        t_lip_phy_hw_reset_start        start; /**< Запуск процедуры аппаратного сброса */
        t_lip_phy_hw_reset_check_done   check_done; /**< Проверка завершения процедуры аппаратного сброса */
    } hw_reset;

    /**< Дополнительные функции обратного вызова для реакции программы на различные
     * события PHY */
    struct {
        t_lip_phy_user_pre_detect       user_pre_detect; /**< Пользовательская обработка, выполняемая после перед началомо обнаружения phy */
        t_lip_phy_user_cfg              user_cfg; /**< Пользовательская обработка, выполняемая после завершения
                                              настройки параметров PHY */
        t_lip_phy_mode_changed          mode_changed; /**< Пользовательская обработка события изменения текущего
                                                  режима работы PHY */
        t_lip_phy_mode_changed          link_status_changed; /**< Пользовательская обработка изменения состояния
                                                    подключения PHY (link-up/link-down) */
    } cb;
} t_lip_phy_iface_descr;


#endif // LIP_PHY_DEFS_H

