/* Данный файл содержит объявление типов и функций, общих для разных вариантов
   портов  USB-интерфейса под конкретную систему.

   Порт должен реализовать функции инициализации ltrsrv_usb_init() и деинициализации
   ltrsrv_usb_close() поддержки USB (для поддержки нескольких портов нужен другой
   вариант иницилизации... в остальном это возможно, но также требуется проверять
   на равенство устройств с сравнением одинаковости портов). Внутри ltrsrv_usb_close()

   Порт предоставляет набор своих функций в виде структуры t_usb_port_intf.

   Каждое устройство должно обладать уникальным devid для порта. Для общего кода
   это указатель на непрозрачную структуру. Сравнение devid должно позволять
   однозначно определить, разные это устройства или те же (одно устройство
   после переподключения может считаться разными устройствами).
   Порт предоставляет функцию для сравнения devid и их освобождения.

   Порт может либо периодически получать список устройств для проверки подключения/
   отключения, либо определять их подключение/отключение асинхронным образом.

   В первом случае порт при проверке списка сперва должен вызвать p_usb_check_devlist_init(),
   затем на каждое устройство крейта вызвать p_usb_dev_is_new() и, если это
   новое устройство, добавить его p_usb_init_new_dev().
   После заверешния прохода списка, необходимо вызвать p_usb_check_devlist_finish(),
   которая автоматически посчитает, что все устройства, которые ранее были добавлены,
   но для которых от p_usb_check_devlist_init() до p_usb_check_devlist_finish()
   не было вызвано p_usb_dev_is_new(), отключены.
   При асинхронном определении отключения/подключения порт сперва на каждое
   присутствующее устройство при инициализации вызвает p_usb_init_new_dev().
   При появлении нового устройства можно проверить, что оно новое p_usb_dev_is_new()
   и вызвать p_usb_init_new_dev(). При отключении - p_usb_device_disconnected().


   Внутри p_usb_init_new_dev() создается структура t_usb_data с общей информацией
   для работы по USB и структура крейта, после чего вызывается функция открытия устройства,
   в которой порт может создать свою структуру с требуемой информацией и сохранить
   ее в поле privdata.

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

   */

#ifndef LTRSRV_USB_P_H
#define LTRSRV_USB_P_H

#include "ltrsrv_crates.h"
#include <stdio.h>

#define LTRSRV_USB_CTL_TOUT                     1500

#define LTRSRV_USB_BULK_MAX_TRANSF_CNT          20
#define LTRSRV_USB_BULK_TRANSF_SIZE             (32*1024)
#define LTRSRV_USB_BULK_SEND_TOUT               0
#define LTRSRV_USB_READ_FPGA_INFO_TOUT          1000
#define LTRSRV_USB_READ_READ_TOUT               0
#define LTRSRV_USB_DEVCHECK_TOUT                500

/* максимальное кол-во передач по bulk вначале, после которого, если не пришла
 * информация о ПЛИС, считается, что уже на придет и крейт все же регистрируется
 * в системе без нее */
#define USB_INIT_DROP_TRANSF_LIMIT             128


/* при данном кол-ве ошибок перестаем делать попытки повторного подключения */
#define LTRSRV_USB_RECONNECT_ERR_TRESHOLD       4

/* таймаут после возникновения ошибки, через который проверяется, не исчезло
 * ли устройство. елси не исчезло, то выводится сообщение об ошибке и выполняется
 * попытка повторной инициализации */
#define LTRSRV_USB_DISK_CHECK_TOUT             2000
/* таймаут, после которого сбрасывается счетчик ошибок */
#define LTRSRV_USB_ERR_CLR_TOUT                (5000 + LTRSRV_USB_RECONNECT_ERR_TRESHOLD * LTRSRV_USB_DISK_CHECK_TOUT)

/* проверка провильности описателя крейта */
#define USB_CHECK_HND(hnd) (hnd) == NULL ? LTRSRV_ERR_CRATE_HANDLE : \
    (hnd)->intf_data == NULL ? LTRSRV_ERR_CRATE_INTF_CLOSED : \
    (hnd)->par.intf != LTR_CRATE_IFACE_USB ? LTRSRV_ERR_CRATE_INVALID_INTF : 0

typedef enum {
    LTR_USB_CMD_RESET_FPGA              = 0,
    LTR_USB_CMD_PUT_ARRAY               = 1,
    LTR_USB_CMD_GET_ARRAY               = 2,
    LTR_USB_CMD_INIT_FPGA               = 3,
    LTR_USB_CMD_SET_DMA_PARAMETRS       = 4,
    LTR_USB_CMD_GET_MODULE_NAME         = 11,
    LTR_USB_CMD_SET_WAIT_TIME           = 12,
    LTR_USB_CMD_BOOTLOADER_PUT_ARRAY    = 13,
    LTR_USB_CMD_BOOTLOADER_GET_ARRAY    = 14,
    LTR_USB_CMD_CALL_APPLICATION        = 15
} t_ltr_crate_usb_cmds;

/* состояние автомата инициализации. необходимо добавить строку в f_usbinit_str в ltrsrv_usb.c */
typedef enum {
    USB_INIT_GET_NAME,
    USB_INIT_GET_DESCR,
    USB_INIT_GET_BOOTLOADER_VER,
    USB_INIT_GET_FIRM_VER,
    USB_INIT_GET_HARD_REV,
    USB_INIT_GET_PRIMARY_IFACE,
    USB_INIT_DMAC_INIT_0,
    USB_INIT_FPGA_RESET,
    USB_INIT_FPGA_INIT,
    USB_INIT_DMAC_INIT_1,
    USB_INIT_FPGA_READ_INFO,
    USB_INIT_SWITCH_BOOTLOADER,
    USB_INIT_SWITCH_APPLICATION,
    USB_INIT_FPGA_LOAD,
    USB_INIT_FPGA_LOAD_DONE,

    USB_INIT_DONE ,
    USB_INIT_CLEANUP ,
    USB_INIT_CLEANUP_DONE

} t_init_state;


typedef enum {
    USB_CLOSE_REASON_NONE = 0,
    USB_CLOSE_REASON_DISCONNECT,
    USB_CLOSE_REASON_CRATE_REQ,
    USB_CLOSE_REASON_BULK_RECV_FAILD,
    USB_CLOSE_REASON_BULK_SEND_FAILD,
    USB_CLOSE_REASON_BULK_OUT_OF_ORDER,
    USB_CLOSE_REASON_INIT_CTL_FAILD,
    USB_CLOSE_REASON_INIT_PROCESS,
    USB_CLOSE_REASON_RESET
} t_usb_close_reasons;


typedef enum {
    USB_INTF_FLAGS_SPECIAL_FPGA_DATA_PROC = 0x01,
    USB_INTF_FLAGS_LOAD_FPGA              = 0x02,
    USB_INIT_FLAGS_SWITCH_BOOTLOADER      = 0x04
} t_usb_intf_flags;

typedef enum {
    USB_BULK_DIR_READ  = 0,
    USB_BULK_DIR_WRITE = 1
} t_usb_bulk_dir;


typedef enum {
    USB_TRANSF_STATE_FREE        = 0,
    USB_TRANSF_STATE_PROGRESS    = 1,
    USB_TRANSF_STATE_CLEANUP     = 2
} t_usb_transf_state;


typedef struct {
    void* port_transfer;
    uint8_t *buf;
    uint16_t len;
    t_usb_transf_state state;
} t_transf_params;

typedef struct {
    t_transf_params transfs[LTRSRV_USB_BULK_MAX_TRANSF_CNT];
    uint8_t put_pos;
    uint8_t get_pos;
    t_usb_bulk_dir dir;
} t_bulk_transf_ctl;


struct st_usb_port_intf;

typedef struct st_usb_data {
    int fnd; /* признак, найдено ли это устройство при очередной проверке (для
                нахождения отключенных устройств) */
    void *devid; /* параметр одназначно опеределяющий устройство */
    t_init_state init_state; /* Состояние автомата инициализации крейта */
    int flags; /* Флаги из #t_usb_intf_flags */
    t_ltr_crate *crate; /* Указатель на описатель крейта */

    t_ltr_crate_cmd_cb proc_cmd_cb; /* callback верхнего уровня на завершение
                                       выполнение команды */
    void *proc_data;  /* указатель, который передается proc_cmd_cb */
    uint8_t *proc_buf; /* буфер, в который принимается ответ на упр. запро*/
    uint16_t proc_len; /* размер буфера на прием ответа на упр. запрос */
    uint32_t *proc_rcv_size; /* укзатель, куда нужно сохранить принятый размер
                                данных от запроса, если он может отличаться
                                от запрошенного */


    t_bulk_transf_ctl bulk_rd;
    t_bulk_transf_ctl bulk_wr;

    t_transf_params ctl;

    t_ltr_crate_snd_rdy_cb snd_rdy_cb;


    FILE *firm_file; /* Файл с прошивкой крейта */
    const char* firm_file_name; /* имя файла с прошивкой (если нужно загружать) */
    uint32_t fpga_load_size;
    char *fpga_info;
    uint16_t fpga_cur_info_size;
    uint16_t fpga_info_req_size;
    int drop_init_wrds;
    int drop_exception;


    int err_reopen_cnt; /* количество ошибок, приведших к повторному открытию данного устройства */
    t_lclock_ticks err_time;
    t_init_state close_init_state;
    t_usb_close_reasons close_reason;
    int cur_err;
    int cur_syserr;

    const struct st_usb_port_intf* port_intf;
    void *privdata;
} t_usb_data;





typedef struct st_usb_port_intf {
    int (*devopen)(t_usb_data *intf);
    int  (*start_ioctl)(t_usb_data* intf, uint8_t cmd, uint32_t param,
                       uint16_t reply_len, uint16_t data_len,
                       const uint8_t* tx_data);
    int  (*start_bulk)(t_usb_data* intf, t_bulk_transf_ctl* bulk_ctl, unsigned timeout);
    void (*cancel_transfer)(t_usb_data *intf, t_transf_params *transf);
    void (*cleanup)(t_usb_data *intf);
    FILE* (*fopen)(const char* fname, const char* mode);

    int   (*devid_cmp)(const void *dev1, const void *dev2);
    void  (*devid_free)(void *dev);
} t_usb_port_intf;


typedef struct {
    t_ltr_crate_type type;
    uint16_t vid;
    uint16_t pid;
} t_usb_dev_id;

extern t_usb_data* f_usb_crates[];
extern int f_usb_crates_cnt;
extern const t_usb_dev_id crate_usb_dev_ids[];

/* Функция должна вызываться перед началом получения списка устройств */
void p_usb_check_devlist_init(void);
/* Функция должна вызываться после завершения получения списка устройств */
void p_usb_check_devlist_finish(void);
/* Функция должна вызываться при обнаружении отключения устройства с помощью
 * асинхронных событий подключения/отключения */
void p_usb_device_disconnected(const void *devid, const t_usb_port_intf *port_intf);

/* проверка, является ли устройство с указанным признаком новым (т.е. требуется
 *  его инициализация) */
int p_usb_dev_is_new(const void *devid, const t_usb_port_intf *port_intf);
/* инициализация нового устройства (должно быть проверено p_usb_dev_is_new()).
 * после вызова контроль за devid и его осовобождение переходит под ответственность
 * общего кода */
int p_usb_init_new_dev(void *devid, const t_usb_port_intf *port_intf);



int p_usb_process_ioctl(t_usb_data *intf, int transf_err, const uint8_t* reply, unsigned actual_len);
int p_usb_process_bulk_rd(t_usb_data *intf, int transf_err, int syserr, const uint8_t* data, unsigned len);
int p_usb_process_bulk_wr(t_usb_data *intf, int transf_err, int syserr, uint8_t* data, unsigned len);
void p_usb_process_bulk_out_of_order(t_usb_data *intf, t_transf_params *transf, int transf_err, int syserr);

void p_usb_close_dev_by_err(t_usb_data* intf, t_usb_close_reasons reason, int err, int syserr);
void p_usb_cleanup_finish(t_usb_data *intf) ;
/* общая часть функции закрытия интерфейса */
int p_usb_close(void);



#endif // LTRSRV_USB_P_H
