/*
 * All Rights Reserved (C) 2021-2025
 * Exoligent SARL (Colombes, France)
 * Website: www.exoligent.com
 */

#ifndef __LIB_POWERFIP_H
#define __LIB_POWERFIP_H

#include <stddef.h> // NULL
#include <stdint.h> // uint8_t, ...

#include "powerfip-drv.h"
#include "powerfip-common.h"

/**
 * ENUMERATIONS
 */
enum pwrfip_bitrate {
    _PWRFIP_BITRATE_MIN = 1,
    PWRFIP_BITRATE_31K25 = _PWRFIP_BITRATE_MIN,
    PWRFIP_BITRATE_1M,
    PWRFIP_BITRATE_2M5,
    PWRFIP_BITRATE_5M,
    PWRFIP_BITRATE_12M5,
    PWRFIP_BITRATE_25M,
    _PWRFIP_BITRATE_MAX,
    _PWRFIP_BITRATE_UNKNOWN = 0,
};
#define PWRFIP_BITRATE_RANGE (_PWRFIP_BITRATE_MAX - _PWRFIP_BITRATE_MIN)

enum pwrfip_frame_type {
    _PWRFIP_FRM_TYPE_MIN = 1,
    PWRFIP_FRM_FIP = _PWRFIP_FRM_TYPE_MIN,
    PWRFIP_FRM_WORLDFIP,
    _PWRFIP_FRM_TYPE_MAX,
    _PWRFIP_FRM_TYPE_UNKNOWN = 0,
};

/* medium */
/* fieldrive: line driver (MAU: Medium Attachment Unit) */
/* fieldual: medium redundancy */
/* MAU commands flags */
enum pwrfip_medium_cmd_flag {
    /* enable channel 1 */
    PWRFIP_MEDIUM_CMD_ENABLE_CH_1 = (1 << 0),
    /* disable channel 1 */
    PWRFIP_MEDIUM_CMD_DISABLE_CH_1 = (1 << 1),
    /* enable channel 2 */
    PWRFIP_MEDIUM_CMD_ENABLE_CH_2 = (1 << 2),
    /* disable channel 2 */
    PWRFIP_MEDIUM_CMD_DISABLE_CH_2 = (1 << 3),
    /* enable both channels */
    PWRFIP_MEDIUM_CMD_ENABLE_CH_1_2 = ((PWRFIP_MEDIUM_CMD_ENABLE_CH_1) |
                                       (PWRFIP_MEDIUM_CMD_ENABLE_CH_2)),
    /* disable both channels */
    PWRFIP_MEDIUM_CMD_DISABLE_CH_1_2 = ((PWRFIP_MEDIUM_CMD_DISABLE_CH_1) |
                                       (PWRFIP_MEDIUM_CMD_DISABLE_CH_2)),
    /* clear tx error flags for both channels */
    PWRFIP_MEDIUM_CMD_CLEAR_TX_ERR = (1 << 4),
    /* reset channel 1 */
    PWRFIP_MEDIUM_CMD_RESET_CH_1 = (1 << 5),
    /* reset channel 2 */
    PWRFIP_MEDIUM_CMD_RESET_CH_2 = (1 << 6),
    /* reset both channels */
    PWRFIP_MEDIUM_CMD_RESET_CH_1_2 = ((PWRFIP_MEDIUM_CMD_RESET_CH_1) |
                                      (PWRFIP_MEDIUM_CMD_RESET_CH_2)),
};

/* ID request type inside a periodic window */
enum pwrfip_ba_id_type {
    PWRFIP_BA_ID_DAT = 0x03,
    PWRFIP_BA_ID_MSG = 0x05,
};

/* Priority channel for aperiodic variable requests */
enum pwrfip_var_aper_channel_type {
    PWRFIP_VAR_APER_CH_NORMAL = 0,
    PWRFIP_VAR_APER_CH_URGENT,
};

/* FIP variable type */
enum pwrfip_var_type {
    /* variable to consume */
    PWRFIP_VAR_TYPE_CONS,
    /* variable to produce */
    PWRFIP_VAR_TYPE_PROD,
    /* synchronization variable */
    /* note: no payload is attached to it, but the
     * node is sensitive to (rx/tx) ID_DAT frame
     * and immediately signals the user-space with
     * a synchronous event when it occurs.
     */
    PWRFIP_VAR_TYPE_SYNC,
    _PWRFIP_VAR_TYPE_MAX,
};

/* FIP event type */
enum pwrfip_evt_type {
    /* no event */
    PWRFIP_EVT_TYPE_NONE,
    /* permanent event (each time id_dat frame is detected) */
    /* note: this event attached to a prod/cons variable is equivalent
     * to a synchonization variable but with a defined payload */
    PWRFIP_EVT_TYPE_PERMANENT_ID,
    /* permanent event (each time rp_dat frame is detected) */
    PWRFIP_EVT_TYPE_PERMANENT,
    /* temporary event (juste once time on rp_dat detection) */
    PWRFIP_EVT_TYPE_TEMPORARY,
};

enum pwrfip_ba_startup_mode {
    PWRFIP_BA_STUP_STANDARD,
    PWRFIP_BA_STUP_OPTIMIZE_1,
    PWRFIP_BA_STUP_OPTIMIZE_2,
    /* only for mono-medium topology */
    PWRFIP_BA_STUP_OPTIMIZE_3,
};

/* SM-MPS variable type (where XY is the node address) */
enum pwrfip_sm_var_type {
    PWRFIP_SM_VAR_IDENT, /* 0x10XY sm-mps */
    PWRFIP_SM_VAR_REPORT, /* 0x11XY sm-mps */
    PWRFIP_SM_VAR_PRESENCE, /* 0x14XY sm-mps */
    PWRFIP_SM_VAR_SEG_PAR, /* 0x9008 sm-mps */
};

/**
 * Error codes
 */
enum pwrfip_error_code {
    /* to avoid to overwrite system errors */
    _PWRFIP_ERR_CODE_MIN = 256,
    /* library error codes */
    PWRFIP_ERR_DEV_ALREADY_BIND = _PWRFIP_ERR_CODE_MIN,
    PWRFIP_ERR_DEV_IRQ_HANDLER_STARTED,
    PWRFIP_ERR_DEV_IRQ_HANDLER_STOPPED,
    PWRFIP_ERR_DEV_DIAG_TASK_STARTED,
    PWRFIP_ERR_DEV_DIAG_TASK_STOPPED,
    PWRFIP_ERR_AELE_NOT_STOP,
    PWRFIP_ERR_AELE_NOT_RUN,
    PWRFIP_ERR_AELE_VAR_NOT_FOUND,
    PWRFIP_ERR_BA_NOT_STOP,
    PWRFIP_ERR_BA_NOT_RUN,
    PWRFIP_ERR_INVALID_CTX,
    PWRFIP_ERR_CFG_VAR_TYPE_UNKNOWN,
    PWRFIP_ERR_CFG_VAR_DIR,
    PWRFIP_ERR_CFG_MSG_PROD,
    PWRFIP_ERR_CFG_MSG_DIR,
    PWRFIP_ERR_CFG_VAR_EXIST,
    PWRFIP_ERR_CFG_MSG_EXIST,
    PWRFIP_ERR_CFG_VAR_BAD_LEN,
    PWRFIP_ERR_CFG_MSG_TYPE_UNKNOWN,
    PWRFIP_ERR_CFG_MSG_TX_ACK_MODE_UNKNOWN,
    PWRFIP_ERR_CFG_MSG_TX_CH_PER_EXIST,
    PWRFIP_ERR_CFG_MSG_TX_CH_PER_UNKNOWN,
    PWRFIP_ERR_CFG_MSG_TX_CH_PER_NOID,
    PWRFIP_ERR_MCYCLE_WIND_COUNT,
    PWRFIP_ERR_MCYCLE_WIND_UNKNOWN,
    PWRFIP_ERR_MCYCLE_WIND_END,
    PWRFIP_ERR_MCYCLE_PER_WIND_REQ_COUNT,
    PWRFIP_ERR_MCYCLE_PER_WIND_REQ_UNKNOWN,
    PWRFIP_ERR_MCYCLE_WIND_TIME_INC,
    PWRFIP_ERR_NODE_BSS_OVERFLOW,
    PWRFIP_ERR_NODE_HANDLER_MISSING,
    PWRFIP_ERR_NODE_FRM_TYPE_INVALID,
    PWRFIP_ERR_NODE_BITRATE_INVALID,
    PWRFIP_ERR_NODE_MSG_CAP_NOT_SUPPORTED,
    PWRFIP_ERR_NODE_RX_MSG_FIFO_SZ,
    PWRFIP_ERR_NODE_RX_MSG_SEG_CAP,
    PWRFIP_ERR_NODE_TX_MSG_FIFO_SZ,
    PWRFIP_ERR_NODE_TX_MSG_REPEAT,
    PWRFIP_ERR_NODE_BA_STUP_TIMES,
    PWRFIP_ERR_NODE_BA_REQ_FIFO_SZ,
    PWRFIP_ERR_NODE_TR_INVALID,
    PWRFIP_ERR_NODE_TS_INVALID,
    PWRFIP_ERR_NODE_IDENT_PARAM,
    PWRFIP_ERR_NODE_IDENT_LEN,
    PWRFIP_ERR_BA_STUP_TS_INVALID,
    PWRFIP_ERR_BA_STUP_PHY_ADDR_INVALID,
    PWRFIP_ERR_BA_STUP_PRIO_INVALID,
    /* communication errors codes (mailboxes) */
    _PWRFIP_ERR_COM_CODE_OFFSET,
    PWRFIP_ERR_COM_DIR_UNKNOWN = _PWRFIP_ERR_COM_CODE_OFFSET,
    PWRFIP_ERR_COM_NOT_OUTBOX,
    PWRFIP_ERR_COM_NOT_INBOX,
    PWRFIP_ERR_COM_INVAL,
    PWRFIP_ERR_COM_TMO,
    PWRFIP_ERR_COM_BUSY,
    PWRFIP_ERR_COM_NO_PKT,
    PWRFIP_ERR_COM_PKT_BAD_SZ,
    PWRFIP_ERR_COM_PKT_BAD_CMD,
    PWRFIP_ERR_COM_PKT_RES_FAILED,
    PWRFIP_ERR_COM_DMA_BAD_OP,
    _PWRFIP_ERR_CODE_MAX,
};

/**
 * STRUCTURES
 */
#pragma pack(1) // 1 byte alignment

/* struct: opaque */
struct pwrfip_dev;
struct pwrfip_aele;
struct pwrfip_ba_mcycle;

/* struct: pre-declaration */
struct pwrfip_node;
struct pwrfip_var;
struct pwrfip_msg;

/* struct: configuration */
struct pwrfip_proc_cfg {
    uint32_t base_addr; /* start address offset of dual-port memory area */
    void *hnd; /* coprocessor handle */
    void (* pwrfip_ioread32)(void *hnd, uint32_t *addr, uint32_t *value);
    void (* pwrfip_iowrite32)(void *hnd, uint32_t *addr, uint32_t value);
};

struct pwrfip_node_msg_cfg {
    /* enable fip message emission/transmission */
    int enable;
    /**
     * => Reception settings
     */
    /**
     * Consumption queues sizes for FIP messages:
     * min=1, max=64, default=30 (0: default value)
     */
    uint32_t rx_fifo_size;
    /**
     * Sensitivity of the node for the message reception
     * according to the segment destination.
     * see msg_hdr/dst.seg
     * see enum pwrfip_msg_rx_seg_cap */
    uint8_t rx_segment_tab[256];
    /**
     * => Emission settings
     */
    /**
     * Aperiodic transmission message queue settings
     */
    /* min=1, max=64; default=24 (0: default value) */
    uint32_t tx_aper_fifo_size;
    /**
     * Periodic transmission message queue settings
     */
    /* min=1, max=64; default=24 (0: default value) */
    uint32_t tx_per_fifo_size[PWRFIP_MSG_TX_CH_PER_CNT];
    /* FIP identifier attached to the periodic message transmission queue.
     * 0: no ID attached, else: ID to query via ID_MSG frame
     */
    uint16_t tx_per_fifo_id[PWRFIP_MSG_TX_CH_PER_CNT];
    /**
     * Number of retries to send a message
     * if no acknowledgement received.
     */
    uint8_t tx_max_repeat;
};

struct pwrfip_node_ba_cfg {
    /* enable master capability */
    int enable;
    /**
     * => BA: StartUp/Election settings
     */
    uint32_t priority; /* [0..15] (0: higher) */
    uint32_t start_ustime;
    uint32_t election_ustime;
    /**
     * => BA: Aperiodic requests FIFOs settings
     */
    uint32_t msg_req_fifo_size;
    uint32_t urgent_var_req_fifo_size;
    uint32_t normal_var_req_fifo_size;
};

struct pwrfip_node_ident_cfg {
    char *manufacturer_name;
    char *model_name;
    uint8_t revision; /* ex: 0x23 (v2.3) */
    char *tag_name; /* [optional] */
    char *vendor_field; /* [optional] */
};

struct pwrfip_node_prtcl_cfg {
    /* enable protocol extension */
    int enable;
    int enable_var_long_form; /* allows variables up to 1024 bytes (vs 128B)
                               * note: this parameter changes the length
                               * encoding for RP_DAT frames ('long form'
                               * instead of 'short form')
                               * (ISO/IEC 8825-1 ASN.1 - BER)
                               */
};

struct pwrfip_node_cfg {
    /**
     * General FIP settings
     */
    uint8_t fip_phy_addr; /* physical address of the agent (node address) */
    uint8_t fip_seg_num; /* fip segment number to which the agent belongs */
    uint8_t fip_frm_type; /* fip frame type (see enum pwrfip_frame_type) */
    uint8_t fip_bitrate; /* fip bitrate (see enum pwrfip_bitrate) */
    /**
     * Turn-Around time
     * The time range changes according to bitrate:
     *      @31.25Kbps  : min=320us, max=8232us, default=424us
     *      @1Mbps      : min=20us, max=258us, default=30us
     *      @2.5Mbps    : min=20us, max=103us, default=30us
     *      @5Mbps      : min=20us, max=103us, default=32us
     *      @12.5Mbps   : min=20us, max=103us, default=32us
     *      @25Mbps     : min=20us, max=103us, default=32us
     */
    uint32_t turn_around_ustime; /* turn-around time in us (0: def value) */
    /**
     * Silence time
     * The time range changes according to bitrate:
     *      @31.25Kbps  : min=2880us, max=65535us, default=4096us
     *      @1Mbps      : min=70us, max=65535us, default=150us
     *      @2.5Mbps    : min=36us, max=65535us, default=96us
     *      @5Mbps      : min=36us, max=65535us, default=92us
     *      @12.5Mbps   : min=36us, max=65535us, default=92us
     *      @25Mbps     : min=36us, max=65535us, default=92us
     */
    uint32_t silence_ustime; /* silence time in us (0: def value) */
    /**
     * Watchdog time [optional] (disabled by default)
     * The node (coprocessor) automatically disconnects from the FIP network
     * if it is not requested frequently enough by the PowerFIP API (userapp)
     */
    uint32_t watchdog_ustime; /* watchdog time in us (0: def value) */
                              /* min=0us (infinite), max=42949672us, default=0us*/
    /**
     * Extra FIP settings
     */
    int enable_bimedium; /* medium redundancy (fieldual) */
    struct pwrfip_node_msg_cfg msg; /* messaging capability [optional] */
    struct pwrfip_node_ba_cfg ba; /* master (BA) capability [optional] */
    struct pwrfip_node_ident_cfg ident; /* node identification [mandatory]
                                         * used to produce the SM-MPS
                                         * identification variable 0x10XY
                                         * (where XY is the node address)
                                         */
    struct pwrfip_node_prtcl_cfg prtcl_ext; /* protocol extension [optional]
                                             * /!\ do not use this setting if
                                             * you want to keep the strict
                                             * definition of the FIP/WorldFIP
                                             * protocol (IEC 61158)
                                             */
    /**
     * Hardware access
     * Two methods are provided to bind the FIP component with the library:
     *   1- attach a pci/pcie device (from pwrfip_dev structure)
     *      note: in this case no need to configure 'struct pwrfip_proc_cfg'
     *   2- manually handle the chip coprocessor ('struct pwrfip_proc_cfg')
     */
    struct pwrfip_dev *dev; /* pci device to bind with node */
    struct pwrfip_proc_cfg proc; /* coprocessor config (chip access) */
    /**
     * General Handlers
     */
    void *user_ctx; /* user context pointer [optional] */
    void (* pwrfip_var_handler)(struct pwrfip_node *node,
                struct pwrfip_var *var, struct pwrfip_event *evt);
    void (* pwrfip_msg_recv_handler)(struct pwrfip_node *node,
                struct pwrfip_msg *msg, struct pwrfip_event *evt);
    void (* pwrfip_msg_send_handler)(struct pwrfip_node *node,
                struct pwrfip_msg *msg, struct pwrfip_event *evt);
    void (* pwrfip_urg_req_handler)(struct pwrfip_node *node,
                struct pwrfip_event *evt);
    void (* pwrfip_nor_req_handler)(struct pwrfip_node *node,
                struct pwrfip_event *evt);
    void (* pwrfip_ba_state_handler)(struct pwrfip_node *node,
                struct pwrfip_event *evt);
    /* pwrfip_ba_sync_handler:
     * Dedicated handler for sm_mps BA synchro var [ID(0x9003)]
     * note: It is usual to use this identifier to synchronize tasks between
     * FIP nodes. By convention, the ID_DAT(0x9003) ends the macrocycle of
     * the bus arbiter.
     */
    void (* pwrfip_ba_sync_handler)(struct pwrfip_node *node,
                struct pwrfip_var *var, struct pwrfip_event *evt);

    void (* pwrfip_error_handler)(struct pwrfip_node *node, /* mandatory */
                enum pwrfip_error_code error);
    void (* pwrfip_reset_handler)(struct pwrfip_node *node); /* mandatory */
};

struct pwrfip_ba_startup_cfg {
    /* calculation method of BA times */
    enum pwrfip_ba_startup_mode mode;
    /* silence time in us */
    uint32_t silence_ustime;
    /* last fip agent address on network */
    uint8_t max_phy_addr;
    /* max priority value of BAs on network ([0..15]) */
    uint8_t max_prio;
    /* physical address of this BA */
    uint8_t my_phy_addr;
    /* priority of this BA */
    uint8_t my_prio;
};

struct pwrfip_var_cons_cfg {
    uint16_t payload_bsz;
    uint16_t flags; /* enum pwrfip_var_flags */
    uint32_t promptness_ustime;
    uint32_t evt_type; /* enum pwrfip_evt_type */
};

struct pwrfip_var_prod_cfg {
    uint16_t payload_bsz;
    uint16_t flags; /* enum pwrfip_var_flags */
    uint32_t refreshment_ustime;
    uint32_t evt_type; /* enum pwrfip_evt_type */
};

struct pwrfip_var_sync_cfg {
    uint32_t reserved[3]; /* not significant */
};

struct pwrfip_var_cfg {
    enum pwrfip_var_type type;
    /* fip identifier */
    uint16_t id;
    /* configuration areas according to variable type */
    union {
        struct pwrfip_var_cons_cfg cons;
        struct pwrfip_var_prod_cfg prod;
        struct pwrfip_var_sync_cfg sync;
    };
    /* user-specified context associated with this variable */
    void *user_ctx; /* [optional] */
    /**
     * Handler:
     * Local callback for synchro var or for prod/cons var
     * (if evt_type is set).
     * Note: If not set, the general handler of the fip node
     * will be used. (see struct pwrfip_node_cfg)
     */
    void (* pwrfip_var_handler)(struct pwrfip_node *node,
            struct pwrfip_var *var, struct pwrfip_event *evt);
};

struct pwrfip_ba_request {
    /* frame ctrl code for the request */
    enum pwrfip_ba_id_type type;
    /* fip identifier to request */
    uint16_t id;
};

struct pwrfip_ba_per_wind_cfg {
    /* number of periodic requests */
    int req_cnt;
    /* list of periodic requests */
    struct pwrfip_ba_request *req_list;
};

struct pwrfip_ba_aper_var_wind_cfg {
    /* end of aper. var window in us (relative to macrocycle start) */
    uint32_t end_ustime;
    /* enable the presence test if the ba doesn't have other tasks
     * to perform during this window */
    int enable_testp;
};

struct pwrfip_ba_aper_msg_wind_cfg {
    /* end of aper. msg window in us (relative to macrocycle start) */
    uint32_t end_ustime;
};

struct pwrfip_ba_wait_wind_cfg {
    /* end of waiting window in us (relative to macrocycle start) */
    uint32_t end_ustime;
    /* waiting type: 0=padding emission, 1=no frame during waiting */
    int is_silent;
    /* resynchronization type: 0=internal, 1=external */
    int is_ext_resync;
};

struct pwrfip_ba_wind_cfg {
    enum pwrfip_ba_wind_type type;
    union {
        /* periodic window */
        struct pwrfip_ba_per_wind_cfg per;
        /* aperiodic variable window */
        struct pwrfip_ba_aper_var_wind_cfg aper_var;
        /* aperiodic message window */
        struct pwrfip_ba_aper_msg_wind_cfg aper_msg;
        /* waiting window */
        struct pwrfip_ba_wait_wind_cfg wait;
    };
};

struct pwrfip_ba_mcycle_cfg {
    /* number of window configurations */
    int wind_cnt;
    /* list of window configurations */
    struct pwrfip_ba_wind_cfg *wind_list;
};

struct pwrfip_node_infos {
    /* user configuration about node */
    struct pwrfip_node_cfg cfg;
    /* coprocessor unique id */
    uint64_t cpu_id;
    /* data size of the node contained in the bss space of the cpu */
    uint32_t bss_bsz; /* in bytes */
    uint32_t bss_max_bsz; /* max data size allowed */
    /**
     * Versions
     */
    /* coprocessor gateware version (fpga) */
    struct pwrfip_version gw_version;
    /* coprocessor firmware version (soft-cpu) */
    struct pwrfip_version fw_version;
    /* driver version */
    struct pwrfip_version drv_version;
    /* library version */
    struct pwrfip_version lib_version;
};

/* struct: op */
struct pwrfip_node {
    /* node informations */
    struct pwrfip_node_infos *infos;
    /* reserved opaque structure */
    void *priv;
};

struct pwrfip_var {
    /* identifier of the variable */
    uint16_t id;
    /* useful var byte size */
    uint16_t bsz;
    /* useful var data */
    uint8_t *buffer;
    struct {
        /* time (us) between variable user writing and
         * its effective production on network */
        uint32_t prod_ustime;
        /* frame transmission time (us) on the network */
        uint32_t send_ustime;
        /* time (us) between last variable reception from the
         * network and its reading by user */
        uint32_t cons_ustime;
    } time_info;
    /* enum pwrfip_var_err_code */
    uint8_t error;
    /* user-specified context */
    void *user_ctx;
    /* reserved opaque structure */
    void *priv;
};

struct pwrfip_msg_addr {
    union {
        uint32_t addr; /* data link layer address */
        struct {
            union {
                uint8_t seg; /* fip segment */
                struct {
                    /* segment number */
                    uint8_t seg_num:7;
                    /* 0: individual, 1: group */
                    uint8_t seg_group:1;
                };
            };
            union {
                uint16_t lsap; /* link service access point */
                struct {
                    /* lsap number */
                    uint16_t lsap_num:15;
                    /* 0: individual, 1: group */
                    uint16_t lsap_group:1;
                };
            };
            uint8_t reserved;
        };
    };
};

struct pwrfip_msg_hdr {
    struct pwrfip_msg_addr src; /* source address */
    struct pwrfip_msg_addr dst; /* destination address */
};

struct pwrfip_msg_tx_cfg {
    uint8_t channel; /* enum pwrfip_msg_tx_channel */
    uint8_t ack_mode; /* enum pwrfip_msg_tx_ack_mode */
};

struct pwrfip_msg_rx_cfg {
    uint16_t reserved; /* not significant */
};

struct pwrfip_msg_cfg {
    enum pwrfip_msg_type type;
    /* message header */
    struct pwrfip_msg_hdr hdr;
    union {
        struct pwrfip_msg_tx_cfg tx;
        struct pwrfip_msg_rx_cfg rx;
    };
    /* user-specified context associated with this message */
    void *user_ctx;
    /**
     * Handler:
     * Local callback for message.
     * Note: If not set, the general handler of the fip node
     * will be used. (see struct pwrfip_node_cfg)
     */
    void (* pwrfip_msg_handler)(struct pwrfip_node *node,
            struct pwrfip_msg *msg, struct pwrfip_event *evt);
};

struct pwrfip_msg {
    /* message header */
    struct pwrfip_msg_hdr hdr;
    /* useful msg byte size (without header) .
     * range = [1;256] byte(s)
     */
    uint16_t bsz;
    /* useful msg data */
    uint8_t *buffer;
    /* rx/tx epoch info */
    uint64_t epoch;
    /* rx/tx channel info
     *  - tx: enum pwrfip_msg_tx_channel
     *  - rx: always 0
     */
    uint8_t channel_num;
    /* enum pwrfip_msg_err_code */
    uint8_t error;
    /* user-specified context */
    void *user_ctx;
    /* reserved opaque structure */
    void *priv;
};

struct pwrfip_sm_identification {
    char manufacturer_name[128];
    char model_name[128];
    uint8_t revision; /* ex: 0x23 (v2.3) */
    char tag_name[128];
    uint8_t smmps_class;
    char vendor_field[128];
    struct pwrfip_var *var;  /* pointer to 0x10XY SM-MPS variable */
};

union pwrfip_sm_report_ch_status {
    uint16_t value;
    struct {
        uint16_t tx_quality_ch1:1;
        uint16_t tx_quality_ch2:1;
        uint16_t rx_quality_ch1:1;
        uint16_t rx_quality_ch2:1;
        uint16_t valid_ch1:1;
        uint16_t valid_ch2:1;
        uint16_t traffic_ch1:1;
        uint16_t traffic_ch2:1;
        uint16_t summary_ch1:1;
        uint16_t summary_ch2:1;
        uint16_t reserved:6;
    };
};

struct pwrfip_sm_report {
    uint16_t rx_ok_ch1;
    uint16_t rx_ok_ch2;
    uint16_t rx_nok_ch1;
    uint16_t rx_nok_ch2;
    union pwrfip_sm_report_ch_status channel_status;
    struct pwrfip_var *var; /* pointer to 0x11XY SM-MPS variable */
};

struct pwrfip_sm_presence {
    uint8_t ident_len; /* length of sm_mps identification var */
    uint8_t ba_state; /* enum pwrfip_ba_state */
    /* BA priority of the node (if master supported) */
    uint8_t ba_prio; /* [0..15] (0: higher) */
    struct pwrfip_var *var; /* pointer to 0x14XY SM-MPS variable */
};

struct pwrfip_sm_presence_list {
    int count; /* count of present fip nodes */
    uint8_t addr[256]; /* addresses of present fip nodes */
    struct pwrfip_var *var; /* pointer to 0x9002 SM-MPS variable */
};

struct pwrfip_sm_ba_sync {
    uint8_t addr; /* node address of bus arbiter */
    uint8_t mcycle_num; /* macrocycle number in progress */
    struct pwrfip_var *var; /* pointer to 0x9003 SM-MPS variable */
};

#pragma pack()

#ifdef __cplusplus
extern "C" {
#endif

#if defined(_WIN32) || defined(_WIN64)
#   if defined(PWRFIP_API_STATIC)
#       define PWRFIP_API
#   else
#       if defined(PWRFIP_API_SHARED_EXPORT)
#           define PWRFIP_API __declspec(dllexport)
#       else
#           define PWRFIP_API __declspec(dllimport)
#       endif
#   endif
#else
#   define PWRFIP_API
#endif

/**
 * FUNCTIONS
 */
/* library: init/exit, infos, errors */
PWRFIP_API int pwrfip_init();
PWRFIP_API void pwrfip_exit();
PWRFIP_API const struct pwrfip_version *pwrfip_version_get();
PWRFIP_API int pwrfip_error_get();
PWRFIP_API const char *pwrfip_strerror(int err);

/* device */
/* note: section reserved to pci/pcie devices */
PWRFIP_API int pwrfip_device_list_get(
                struct pwrfip_dev_infos *dev_infos, int *dev_cnt);
PWRFIP_API struct pwrfip_dev *pwrfip_device_open(uint8_t dev_id);
PWRFIP_API int pwrfip_device_reset(struct pwrfip_dev *dev);
PWRFIP_API int pwrfip_device_close(struct pwrfip_dev *dev);
PWRFIP_API int pwrfip_device_infos_get(struct pwrfip_dev *dev,
                struct pwrfip_dev_infos *info);
PWRFIP_API int pwrfip_device_report_get(struct pwrfip_dev *dev,
                struct pwrfip_dev_report *report);

/* node */
PWRFIP_API struct pwrfip_node *pwrfip_node_init(struct pwrfip_node_cfg *cfg);
PWRFIP_API int pwrfip_node_exit(struct pwrfip_node *node);
PWRFIP_API int pwrfip_ba_startup_calculate(uint32_t *stup_ustime,
                uint32_t *elec_ustime, struct pwrfip_ba_startup_cfg *cfg);
PWRFIP_API int pwrfip_node_status_get(struct pwrfip_node *node,
                struct pwrfip_node_status *status);
PWRFIP_API int pwrfip_ba_status_get(struct pwrfip_node *node,
                struct pwrfip_ba_status *status);
PWRFIP_API int pwrfip_node_report_get(struct pwrfip_node *node,
                struct pwrfip_node_report *report);

PWRFIP_API int pwrfip_node_start(struct pwrfip_aele *aele,
                struct pwrfip_ba_mcycle **mcycle_list, int mcycle_cnt);
PWRFIP_API int pwrfip_node_stop(struct pwrfip_node *node);

/* aele: application/layer entities */
PWRFIP_API struct pwrfip_aele *pwrfip_aele_create(
                struct pwrfip_node *node);
PWRFIP_API int pwrfip_aele_delete(struct pwrfip_aele *aele);

PWRFIP_API struct pwrfip_var *pwrfip_var_create(
                struct pwrfip_aele *aele, struct pwrfip_var_cfg *cfg);
PWRFIP_API int pwrfip_var_delete(struct pwrfip_var *var);

PWRFIP_API struct pwrfip_var *pwrfip_sm_var_create(
                struct pwrfip_aele *aele, enum pwrfip_sm_var_type type,
                uint8_t node_addr);

/* ba: bus arbiter (macrocycles) */
PWRFIP_API struct pwrfip_ba_mcycle *pwrfip_ba_mcycle_create(
                struct pwrfip_node *node, struct pwrfip_ba_mcycle_cfg *cfg);
PWRFIP_API int pwrfip_ba_mcycle_delete(
                struct pwrfip_ba_mcycle *mcycle);

PWRFIP_API int pwrfip_ba_start(struct pwrfip_ba_mcycle *mcycle);
PWRFIP_API int pwrfip_ba_commute(struct pwrfip_ba_mcycle *mcycle);
PWRFIP_API int pwrfip_ba_stop(struct pwrfip_node *node);

/* medium */
PWRFIP_API int pwrfip_medium_status_get(struct pwrfip_node *node,
                struct pwrfip_medium_status *status);
PWRFIP_API int pwrfip_medium_cmd_set(struct pwrfip_node *node,
                uint16_t flags /* enum pwrfip_medium_cmd_flag */);

/* events */
/**
 * Management tasks: pwrfip_its_process() + pwrfip_diag_process()
 * Note: No need to be called by user if a pci/pcie device is bound to
 * the node (see struct pwrfip_dev). It will be called automatically by
 * the internal interrupt handler
 * */
PWRFIP_API int pwrfip_its_process(struct pwrfip_node *node);
PWRFIP_API int pwrfip_diag_process(struct pwrfip_node *node);

/* variables (mps) */
PWRFIP_API int pwrfip_var_write(struct pwrfip_var *var);
PWRFIP_API int pwrfip_var_read(struct pwrfip_var *var);
PWRFIP_API int pwrfip_var_evt_set(struct pwrfip_var *var,
                enum pwrfip_evt_type type);
PWRFIP_API int pwrfip_var_type_set(struct pwrfip_var *var,
                enum pwrfip_var_type type);

/* aperiodic variables requests */
PWRFIP_API int pwrfip_varidlist_aper_request(struct pwrfip_node *node,
                uint16_t *varid_list, int varid_cnt,
                enum pwrfip_var_aper_channel_type channel,
                uint8_t *ch_error);
PWRFIP_API int pwrfip_varlist_aper_request(struct pwrfip_node *node,
                struct pwrfip_var **var_list, int var_cnt,
                enum pwrfip_var_aper_channel_type channel,
                uint8_t *ch_error);
PWRFIP_API int pwrfip_var_aper_channel_purge(struct pwrfip_node *node,
                enum pwrfip_var_aper_channel_type channel,
                uint16_t *purge_cnt, uint8_t *ch_error);

/* messages */
PWRFIP_API struct pwrfip_msg *pwrfip_msg_create(
                struct pwrfip_aele *aele, struct pwrfip_msg_cfg *cfg);
PWRFIP_API int pwrfip_msg_delete(struct pwrfip_msg *msg);
PWRFIP_API int pwrfip_msg_tx_channel_purge(struct pwrfip_node *node,
                enum pwrfip_msg_tx_channel channel,
                uint16_t *purge_cnt, uint8_t *ch_error);
PWRFIP_API int pwrfip_msg_send(struct pwrfip_msg *msg);

/* system management variables (sm-mps) */
PWRFIP_API int pwrfip_sm_identification_get(struct pwrfip_node *node,
                uint8_t node_addr, struct pwrfip_sm_identification *ident);
PWRFIP_API int pwrfip_sm_report_get(struct pwrfip_node *node,
                uint8_t node_addr, struct pwrfip_sm_report *report);
PWRFIP_API int pwrfip_sm_presence_get(struct pwrfip_node *node,
                uint8_t node_addr, struct pwrfip_sm_presence *pres);
PWRFIP_API int pwrfip_sm_presence_list_get(struct pwrfip_node *node,
                struct pwrfip_sm_presence_list *pres_list);
PWRFIP_API int pwrfip_sm_ba_sync_get(struct pwrfip_node *node,
                struct pwrfip_sm_ba_sync *ba_sync);

#ifdef __cplusplus
}
#endif

#endif /* __LIB_POWERFIP_H */
