/***************************************************************************//**
  @file ltrsrv_eth.c
  @author Borisov Alexey <borisov@lcard.ru>
  @date   16.02.2012

  Файл содержит функции обработки конкретных команд от клиента к LTR-Server'у.
  В первую очередь это функции для получения длины команды и для обработки
  команды, после того как она был полностью принята.
  Эти функции занесены в общую таблицу f_cmd_tbl.
  *****************************************************************************/

#include "ltrsrv_client_p.h"
#include "ltrsrv_log.h"
#include "ltrsrv_eth.h"
#include "config.h"
#ifdef LTRD_LOGPROTO_ENABLED
    #include "ltrsrv_log_proto.h"
#endif

#include <string.h>
#include <stdio.h>
#include <stddef.h>



#define LTR_CLIENT_CMD_SET_SERVER_PROCESS_PRIORITY (LTR_CLIENT_CMD_BASE_ + 0x01) /** @todo */ //SERVER_CONTROL
#define LTR_CLIENT_CMD_GET_CRATE_TYPE              (LTR_CLIENT_CMD_BASE_ + 0x02)
#define LTR_CLIENT_CMD_GET_SERVER_PROCESS_PRIORITY (LTR_CLIENT_CMD_BASE_ + 0x03) /** @todo */ //SERVER_CONTROL
#define LTR_CLIENT_CMD_PUT_ARRAY                   (LTR_CLIENT_CMD_BASE_ + 0x80)
#define LTR_CLIENT_CMD_GET_ARRAY                   (LTR_CLIENT_CMD_BASE_ + 0x81)
#define LTR_CLIENT_CMD_GET_DESCRIPTION             (LTR_CLIENT_CMD_BASE_ + 0x82) /** @todo */
#define LTR_CLIENT_CMD_BOOT_LOADER_GET_DESCRIPTION (LTR_CLIENT_CMD_BASE_ + 0x83) /** @todo */
#define LTR_CLIENT_CMD_BOOT_LOADER_SET_DESCRIPTION (LTR_CLIENT_CMD_BASE_ + 0x84) /** @todo */
#define LTR_CLIENT_CMD_GET_CRATES                  (LTR_CLIENT_CMD_BASE_ + 0x87) //SERVER_CONTROL
#define LTR_CLIENT_CMD_GET_MODULES                 (LTR_CLIENT_CMD_BASE_ + 0x88)
#define LTR_CLIENT_CMD_GET_CRATE_RAW_DATA          (LTR_CLIENT_CMD_BASE_ + 0x89) /** @todo */
#define LTR_CLIENT_CMD_GET_CRATE_RAW_DATA_SIZE     (LTR_CLIENT_CMD_BASE_ + 0x8A) /** @todo */
#define LTR_CLIENT_CMD_LOAD_FPGA                   (LTR_CLIENT_CMD_BASE_ + 0x8B) /** @todo */
#define LTR_CLIENT_CMD_IP_GET_LIST                 (LTR_CLIENT_CMD_BASE_ + 0x91) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_ADD_LIST_ENTRY           (LTR_CLIENT_CMD_BASE_ + 0x92) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_DELETE_LIST_ENTRY        (LTR_CLIENT_CMD_BASE_ + 0x93) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_CONNECT                  (LTR_CLIENT_CMD_BASE_ + 0x94) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_DISCONNECT               (LTR_CLIENT_CMD_BASE_ + 0x95) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_CONNECT_ALL_AUTO         (LTR_CLIENT_CMD_BASE_ + 0x96) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_DISCONNECT_ALL           (LTR_CLIENT_CMD_BASE_ + 0x97) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_SET_FLAGS                (LTR_CLIENT_CMD_BASE_ + 0x98) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_GET_DISCOVERY_MODE       (LTR_CLIENT_CMD_BASE_ + 0x99) //SERVER_CONTROL
#define LTR_CLIENT_CMD_IP_SET_DISCOVERY_MODE       (LTR_CLIENT_CMD_BASE_ + 0x9A) //SERVER_CONTROL
#define LTR_CLIENT_CMD_GET_SERVER_VERSION          (LTR_CLIENT_CMD_BASE_ + 0xAA) //SERVER_CONTROL
#define LTR_CLIENT_CMD_GET_LOG_LEVEL               (LTR_CLIENT_CMD_BASE_ + 0xF0) //SERVER_CONTROL
#define LTR_CLIENT_CMD_SET_LOG_LEVEL               (LTR_CLIENT_CMD_BASE_ + 0xF1) //SERVER_CONTROL
#define LTR_CLIENT_CMD_RESTART_SERVER              (LTR_CLIENT_CMD_BASE_ + 0xF2) //SERVER_CONTROL
#define LTR_CLIENT_CMD_SHUTDOWN_SERVER             (LTR_CLIENT_CMD_BASE_ + 0xF3) //SERVER_CONTROL
#define LTR_CLIENT_CMD_BOOT_LOADER_PUT_ARRAY       (LTR_CLIENT_CMD_BASE_ + 0xFD) /** @todo */
#define LTR_CLIENT_CMD_BOOT_LOADER_GET_ARRAY       (LTR_CLIENT_CMD_BASE_ + 0xFE) /** @todo */
#define LTR_CLIENT_CMD_CALL_APPLICATION            (LTR_CLIENT_CMD_BASE_ + 0xFF) /** @todo */

#define LTR_CLIENT_CMD_EX_BASE                     (LTR_CLIENT_CMD_BASE_ + 0x321100)
#define LTR_CLIENT_CMD_EX_MSK                      (0xFFFF)
#define LTR_CLIENT_CMD_EX_HDR_SIZE                 (LTR_CLIENT_CMD_SIZE + 8UL)
#define LTR_CLIENT_CMD_EX_RESP_HDR_SIZE            (LTR_CLIENT_ACK_SIZE + 4UL)
#define LTR_CLIENT_CMD_GET_CRATE_STAT              (LTR_CLIENT_CMD_EX_BASE + 0)
#define LTR_CLIENT_CMD_GET_MODULE_STAT             (LTR_CLIENT_CMD_EX_BASE + 1)
#define LTR_CLIENT_CMD_GET_CRATE_DESCR             (LTR_CLIENT_CMD_EX_BASE + 2)
#define LTR_CLIENT_CMD_RESET_MODULE                (LTR_CLIENT_CMD_EX_BASE + 3)
#define LTR_CLIENT_CMD_SET_SERVER_PARAM            (LTR_CLIENT_CMD_EX_BASE + 4)
#define LTR_CLIENT_CMD_GET_SERVER_PARAM            (LTR_CLIENT_CMD_EX_BASE + 5)
#define LTR_CLIENT_CMD_GET_CRATES_WITH_INFO        (LTR_CLIENT_CMD_EX_BASE + 6)
#define LTR_CLIENT_CMD_CRATE_IOCTL_REQ             (LTR_CLIENT_CMD_EX_BASE + 7)


// Отклики на команды сервера
#define LTR_CLIENT_ACK_SIZE                    4
#define LTR_CLIENT_ACK_MODULE_IN_USE           0xABCDEFDD
#define LTR_CLIENT_ACK_GOOD                    0xABCDEFEE
#define LTR_CLIENT_ACK_BAD                     0xABCDEFFF
#define LTR_CLIENT_ACK_ERR_INVALID_CMD         0xABCDEF01
#define LTR_CLIENT_ACK_ERR_CRATE_NOT_FOUND     0xABCDEF02
#define LTR_CLIENT_ACK_ERR_EMPTY_SLOT          0xABCDEF03
#define LTR_CLIENT_ACK_ERR_UNSUP_FOR_SRV_CTL   0xABCDEF04
#define LTR_CLIENT_ACK_ERR_INVALID_PARAMS      0xABCDEF05
#define LTR_CLIENT_ACK_ERR_INVALID_IP_ENTRY    0xABCDEF06
#define LTR_CLIENT_ACK_ERR_INVALID_CRATE_SLOT  0xABCDEF07



#define LTR_CLIENT_INIT_CMD_DATA_SIZE  (LTR_CRATE_SERIAL_SIZE + 2 + 4)
#define LTR_CLIENT_INIT_CMD_SIZE       (LTR_CLIENT_CMD_SIZE + LTR_CLIENT_INIT_CMD_DATA_SIZE)

#define LTR_CLIENT_CMD_IP_ENTRY_SIZE                 (28)
#define LTR_CLIENT_CMD_PUT_ARRAY_SIZE                (LTR_CLIENT_CMD_SIZE + 8)


#define CMD_EX_POS_DATA_SIZE          0
#define CMD_EX_POS_RESP_SIZE          4
#define CMD_EX_POS_DATA               8
#define CMD_EX_RESP_POS_SIZE          0
#define CMD_EX_RESP_POS_DATA          4


#define CMD_GET_UINT32(array, idx)  (array[idx+0] | \
                                    ((uint32_t)array[idx+1] << 8) | \
                                    ((uint32_t)array[idx+2] << 16) | \
                                    ((uint32_t)array[idx+3] << 24))

#define CMD_SET_UINT32(array, idx, wrd)  do { \
                                            array[idx]   = wrd&0xFF; \
                                            array[idx+1] = (wrd>>8)&0xFF; \
                                            array[idx+2] = (wrd>>16)&0xFF; \
                                            array[idx+3] = (wrd>>24)&0xFF; \
                                        } while(0)


#define CMD_RESP_DATA(client) (((t_ltr_client_cmd_resp*)client->cmd)->data)
#define CMD_EX_GET_DATA_SIZE(client) (CMD_GET_UINT32(client->cmd->data, CMD_EX_POS_DATA_SIZE))
#define CMD_EX_GET_RESP_SIZE(client) (CMD_GET_UINT32(client->cmd->data, CMD_EX_POS_RESP_SIZE))
#define CMD_EX_GET_DATA_PTR(client)  &client->cmd->data[CMD_EX_POS_DATA]
#define CMD_EX_RESP_DATA(client)     &(CMD_RESP_DATA(client)[CMD_EX_RESP_POS_DATA])
#define CMD_EX_SET_RESP_SIZE(client, resp_size)   CMD_SET_UINT32(CMD_RESP_DATA(client), CMD_EX_RESP_POS_SIZE, resp_size)

#define LTR_CMD_ARRAY_ADDR(client) (CMD_GET_UINT32(client->cmd->data, 0))
#define LTR_CMD_ARRAY_SIZE(client) (CMD_GET_UINT32(client->cmd->data, 4))

#define LTR_CLIENT_REQ_CSN_SERVER_CONTROL          "#SERVER_CONTROL"
#define LTR_CLIENT_REQ_CSN_LOGPROTO                "#LOGPROTO"
#define LTR_CLIENT_SLOT_CONTROL                    0



typedef struct {
    uint32_t snd_size;
    uint32_t flags; /** флаги */
    uint16_t crate_type;
    uint16_t crate_intf;
    uint16_t crate_state;
    uint16_t crate_mode;
    uint64_t con_time;
    uint16_t res[11];
    uint16_t modules_cnt;
    uint16_t mids[LTR_CRATE_MODULES_MAX_CNT];
    uint16_t res2[3*LTR_CRATE_MODULES_MAX_CNT];
    uint16_t ctl_clients_cnt; /** количество клиентов, подключенных по управляющему каналу к крейту */
    uint16_t total_mod_clients_cnt; /** количество клиентов, подключенные ко всем модулям */
    uint32_t res3[11];
    t_ltr_crate_stat stat;
} t_ltr_client_crate_stat;


typedef struct {
    uint32_t snd_size; /** размер структуры */
    uint16_t client_cnt; /** количество клиентов */
    uint16_t mid;
    uint32_t flags; /** флаги */
    char     name[LTR_CRATE_MODULE_NAME_SIZE];
    uint32_t res[5];
    t_ltr_module_stat stat;
} t_ltr_client_module_stat;

typedef enum {
    CMD_STATUS_DONE,
    CMD_STATUS_NEED_RESP,
    CMD_STATUS_DEFFERED
} t_cmd_status;



typedef int (*t_ltr_client_cmd_len_cb)(t_ltr_client *hnd);
typedef int (*t_ltr_client_cmd_proc_cb)(t_ltr_client *hnd, int *resp_size, t_cmd_status *status);
typedef int (*t_ltr_client_cmd_post_proc_cb)(t_ltr_client *hnd);



typedef struct {
    uint32_t cmd; /** код команды */
    uint32_t std_size; /** размер команды, если cb_size = NULL */
    t_ltr_client_cmd_len_cb cb_size; /** функция для определения резмера команды */
    t_ltr_client_cmd_proc_cb cb_proc; /** функция для обработки команды */
    t_ltr_client_cmd_post_proc_cb cb_post; /** функция, вызываемая после того как команда завершена */
} t_cmd_params;


/* таблица соответствий кодов уровня лога и выводимых строк */
static t_ltrsrv_str_tbl f_client_cmd_str[] = {
    {LTR_CLIENT_CMD_SET_SERVER_PROCESS_PRIORITY, "set server priority"},
    {LTR_CLIENT_CMD_GET_CRATE_TYPE,              "get crate type"},
    {LTR_CLIENT_CMD_GET_SERVER_PROCESS_PRIORITY, "get server priority"},
    {LTR_CLIENT_CMD_PUT_ARRAY,                   "put array"},
    {LTR_CLIENT_CMD_GET_ARRAY,                   "get array"},
    {LTR_CLIENT_CMD_GET_DESCRIPTION,             "get usb description"},
    {LTR_CLIENT_CMD_BOOT_LOADER_GET_DESCRIPTION, "get bootloader usb description"},
    {LTR_CLIENT_CMD_BOOT_LOADER_SET_DESCRIPTION, "set bootloader usb description"},
    {LTR_CLIENT_CMD_GET_CRATES,                  "get crates"},
    {LTR_CLIENT_CMD_GET_MODULES,                 "get modules"},
    {LTR_CLIENT_CMD_GET_CRATE_RAW_DATA,          "get raw data"},
    {LTR_CLIENT_CMD_GET_CRATE_RAW_DATA_SIZE,     "get raw data size"},
    {LTR_CLIENT_CMD_LOAD_FPGA,                   "load FPGA"},
    {LTR_CLIENT_CMD_IP_GET_LIST,                 "get IP-entries"},
    {LTR_CLIENT_CMD_IP_ADD_LIST_ENTRY,           "add IP-entry"},
    {LTR_CLIENT_CMD_IP_DELETE_LIST_ENTRY,        "remove IP-entry"},
    {LTR_CLIENT_CMD_IP_CONNECT,                  "connect by IP"},
    {LTR_CLIENT_CMD_IP_DISCONNECT,               "disconnect by IP"},
    {LTR_CLIENT_CMD_IP_CONNECT_ALL_AUTO,         "connect all auto"},
    {LTR_CLIENT_CMD_IP_DISCONNECT_ALL,           "disconnect all"},
    {LTR_CLIENT_CMD_IP_SET_FLAGS,                "set IP-entry flags"},
    {LTR_CLIENT_CMD_IP_GET_DISCOVERY_MODE,       "get discovery mode"},
    {LTR_CLIENT_CMD_IP_SET_DISCOVERY_MODE,       "set discovery mode"},
    {LTR_CLIENT_CMD_GET_SERVER_VERSION,          "get server version"},
    {LTR_CLIENT_CMD_GET_LOG_LEVEL,               "get log level"},
    {LTR_CLIENT_CMD_SET_LOG_LEVEL,               "set log level"},
    {LTR_CLIENT_CMD_RESTART_SERVER,              "restart server"},
    {LTR_CLIENT_CMD_SHUTDOWN_SERVER,             "shutdown server"},
    {LTR_CLIENT_CMD_BOOT_LOADER_PUT_ARRAY,       "bootloader put array"},
    {LTR_CLIENT_CMD_BOOT_LOADER_GET_ARRAY,       "bootloader get array"},
    {LTR_CLIENT_CMD_GET_CRATE_STAT,              "get crate statistic"},
    {LTR_CLIENT_CMD_GET_MODULE_STAT,             "get module statistic"},
    {LTR_CLIENT_CMD_GET_CRATE_DESCR,             "get crate descriptor"},
    {LTR_CLIENT_CMD_RESET_MODULE,                "module reset"},
    {LTR_CLIENT_CMD_SET_SERVER_PARAM,            "set server parameter"},
    {LTR_CLIENT_CMD_GET_SERVER_PARAM,            "get server parameter"},
    {LTR_CLIENT_CMD_GET_CRATES_WITH_INFO,        "get crates with info"},
    {LTR_CLIENT_CMD_CRATE_IOCTL_REQ,             "custom crate ioctl"}
};



static int f_cmd_proc_init(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crates(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crate_modules(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crate_type(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_get_list(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_add(t_ltr_client* client, int* resp_size, t_cmd_status* status);
static int f_cmd_proc_ip_rem(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_con(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_disc(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_con_auto(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_disc_all(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_set_flags(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_set_discovery_mode(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_ip_get_discovery_mode(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_log_lvl(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_set_log_lvl(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_srv_ver(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_put_array(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_array(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crate_stat(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_module_stat(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crate_descr(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_reset_module(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_set_server_param(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_server_param(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_get_crates_with_info(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_crate_ioctl(t_ltr_client *client, int *resp_size, t_cmd_status *status);
static int f_cmd_proc_load_fpga(t_ltr_client *client, int *resp_size, t_cmd_status *status);

static int f_cmd_post_srv_restart(t_ltr_client *client);
static int f_cmd_post_srv_shutdown(t_ltr_client *client);
static int f_cmd_post_init(t_ltr_client *hnd);

static int f_cmd_len_put_array(t_ltr_client *hnd);




static t_cmd_params f_cmd_tbl[] = {
    { LTR_CLIENT_CMD_INIT_CONNECTION,      LTR_CLIENT_INIT_CMD_SIZE, NULL, f_cmd_proc_init,              f_cmd_post_init},
    { LTR_CLIENT_CMD_GET_CRATES,           LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_get_crates,        NULL},
    { LTR_CLIENT_CMD_GET_MODULES,          LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_get_crate_modules, NULL},
    { LTR_CLIENT_CMD_GET_CRATE_TYPE,       LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_get_crate_type,    NULL},
    { LTR_CLIENT_CMD_IP_GET_LIST,          LTR_CLIENT_CMD_SIZE+12,   NULL, f_cmd_proc_ip_get_list,       NULL},
    { LTR_CLIENT_CMD_IP_ADD_LIST_ENTRY,    LTR_CLIENT_CMD_SIZE+12,   NULL, f_cmd_proc_ip_add,            NULL},
    { LTR_CLIENT_CMD_IP_DELETE_LIST_ENTRY, LTR_CLIENT_CMD_SIZE+8,    NULL, f_cmd_proc_ip_rem,            NULL},
    { LTR_CLIENT_CMD_IP_CONNECT,           LTR_CLIENT_CMD_SIZE+4,    NULL, f_cmd_proc_ip_con,            NULL},
    { LTR_CLIENT_CMD_IP_DISCONNECT,        LTR_CLIENT_CMD_SIZE+4,    NULL, f_cmd_proc_ip_disc,           NULL},
    { LTR_CLIENT_CMD_IP_CONNECT_ALL_AUTO,  LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_ip_con_auto,       NULL},
    { LTR_CLIENT_CMD_IP_DISCONNECT_ALL,    LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_ip_disc_all,       NULL},
    { LTR_CLIENT_CMD_IP_SET_FLAGS,         LTR_CLIENT_CMD_SIZE+12,   NULL, f_cmd_proc_ip_set_flags,      NULL},
    { LTR_CLIENT_CMD_IP_SET_DISCOVERY_MODE,LTR_CLIENT_CMD_SIZE+12,   NULL, f_cmd_proc_ip_set_discovery_mode, NULL},
    { LTR_CLIENT_CMD_IP_GET_DISCOVERY_MODE,LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_ip_get_discovery_mode, NULL},
    { LTR_CLIENT_CMD_GET_LOG_LEVEL,        LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_get_log_lvl,       NULL},
    { LTR_CLIENT_CMD_SET_LOG_LEVEL,        LTR_CLIENT_CMD_SIZE+8,    NULL, f_cmd_proc_set_log_lvl,       NULL},


    { LTR_CLIENT_CMD_RESTART_SERVER,       LTR_CLIENT_CMD_SIZE,      NULL, NULL, f_cmd_post_srv_restart},
    { LTR_CLIENT_CMD_SHUTDOWN_SERVER,      LTR_CLIENT_CMD_SIZE,      NULL, NULL, f_cmd_post_srv_shutdown},
    { LTR_CLIENT_CMD_GET_SERVER_VERSION,   LTR_CLIENT_CMD_SIZE,      NULL, f_cmd_proc_srv_ver, NULL},

    {LTR_CLIENT_CMD_GET_CRATE_STAT,       0, NULL, f_cmd_proc_get_crate_stat, NULL},
    {LTR_CLIENT_CMD_GET_MODULE_STAT,      0, NULL, f_cmd_proc_get_module_stat, NULL},
    {LTR_CLIENT_CMD_GET_CRATE_DESCR,      0, NULL, f_cmd_proc_get_crate_descr, NULL},
    {LTR_CLIENT_CMD_RESET_MODULE,         0, NULL, f_cmd_proc_reset_module, NULL},
    {LTR_CLIENT_CMD_SET_SERVER_PARAM,     0, NULL, f_cmd_proc_set_server_param, NULL},
    {LTR_CLIENT_CMD_GET_SERVER_PARAM,     0, NULL, f_cmd_proc_get_server_param, NULL},
    {LTR_CLIENT_CMD_GET_CRATES_WITH_INFO, 0, NULL, f_cmd_proc_get_crates_with_info, NULL},
    {LTR_CLIENT_CMD_CRATE_IOCTL_REQ,      0, NULL, f_cmd_proc_crate_ioctl, NULL},

    { LTR_CLIENT_CMD_PUT_ARRAY, 0, f_cmd_len_put_array, f_cmd_proc_put_array, NULL},
    { LTR_CLIENT_CMD_GET_ARRAY, LTR_CLIENT_CMD_SIZE+8, NULL, f_cmd_proc_get_array, NULL},

    {LTR_CLIENT_CMD_LOAD_FPGA, LTR_CLIENT_CMD_SIZE + 2 +( 96*1024+100), NULL,  f_cmd_proc_load_fpga, NULL}

};


const char* ltrsrv_get_client_cmd_str(int cmd) {
    return ltrsrv_log_get_str(cmd, f_client_cmd_str,
                              sizeof(f_client_cmd_str)/sizeof(f_client_cmd_str[0]));
}


static int f_cmd_ex_proc_hdr(t_ltr_client *client, uint32_t min_data_size,
                             uint32_t *pdata_size, uint32_t *presp_max_size,
                             uint8_t **pcmd_data) {
    uint32_t cmd_data_size = CMD_EX_GET_DATA_SIZE(client);

    int err = 0;

    if (cmd_data_size < min_data_size) {
        err = LTRSRV_ERR_CLIENT_CMD_PARMS;
    } else {
        if (pdata_size!=NULL)
            *pdata_size = cmd_data_size;
        if (presp_max_size!=NULL)
            *presp_max_size = CMD_EX_GET_RESP_SIZE(client);
        if (pcmd_data!=NULL)
            *pcmd_data = CMD_EX_GET_DATA_PTR(client);
    }
    return err;
}

static int f_cmd_ex_resp_buf_realloc(t_ltr_client *client, uint32_t resp_size) {
    return p_client_cmd_realloc_buf(client, resp_size + LTR_CLIENT_CMD_EX_RESP_HDR_SIZE);
}

static int f_cmd_ex_set_resp(t_ltr_client *client, int err, uint32_t resp_size,
                             int *presp_size, t_cmd_status *status) {
    if (err)
        resp_size = 0;
    CMD_EX_SET_RESP_SIZE(client, resp_size);
    *presp_size = resp_size+4;
    *status = CMD_STATUS_NEED_RESP;
    return err;
}


/***************************************************************************//**
    Функция начинает передачу ответа.
      Ответ состоит из кода подтверждения, который формируется в звависимости
      от кода ошибки, и данных, размером data_size, которые беруться из
      CMD_RESP_DATA()
    @param[in] client    описатель команды
    @param[in] cmd_err   код ошибки обработки команды
    @param[in] data_size размер данных в ответе
    @return    Код ошибки выполнения функции
    **************************************************************************/
static int f_cmd_send_ack(t_ltr_client *client, int cmd_err, int data_size) {
    t_ltr_client_cmd_resp* resp = (t_ltr_client_cmd_resp*)client->cmd;
    resp->ack = LTR_CLIENT_ACK_GOOD;

    if (cmd_err) {
        switch (cmd_err) {
            case LTRSRV_ERR_MODULE_IN_USE:
                resp->ack = LTR_CLIENT_ACK_MODULE_IN_USE;
                break;
            case LTRSRV_ERR_CLIENT_UNSUP_CMD_CODE:
                resp->ack = LTR_CLIENT_ACK_ERR_INVALID_CMD;
                break;
            case LTRSRV_ERR_CLIENT_CRATE_NOT_FOUND:
                resp->ack = LTR_CLIENT_ACK_ERR_CRATE_NOT_FOUND;
                break;
            case LTRSRV_ERR_CRATE_SLOT_EMPTY:
                resp->ack = LTR_CLIENT_ACK_ERR_EMPTY_SLOT;
                break;
            case LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL:
                resp->ack = LTR_CLIENT_ACK_ERR_UNSUP_FOR_SRV_CTL;
                break;
            case LTRSRV_ERR_CLIENT_CMD_PARMS:
                resp->ack = LTR_CLIENT_ACK_ERR_INVALID_PARAMS;
                break;
            case LTRSRV_ERR_ETH_INVALID_IP_ENTRY:
                resp->ack = LTR_CLIENT_ACK_ERR_INVALID_IP_ENTRY;
                break;
            case LTRSRV_ERR_CRATE_INVALID_SLOT_NUM:
                resp->ack = LTR_CLIENT_ACK_ERR_INVALID_CRATE_SLOT;
                break;
            default:
                resp->ack = LTR_CLIENT_ACK_BAD;
                break;
        }
    }

    client->cmd_pos = 0;
    client->cmd_size = data_size + LTR_CLIENT_ACK_SIZE;

    return p_client_cmd_proc_send(client);
}

/* поиск крейта для клиента по задданым интерфейсу и серийному номеру */
static t_ltr_crate *f_get_crate(const char *serial, t_ltr_crate_interface intf, t_ltr_client *client) {
    t_ltr_crate *crate = NULL;
    if (!(serial[0]&0xFF)) {
        /* если серийный не указан - то берем серийный либо от крейта с которым
           соединены, либо по первому, если у нас SERVER_CONTROL */
        if (client->crate!=NULL) {
            crate = client->crate;
        } else {
            crate = ltrsrv_crate_find(intf, NULL);
        }
    } else {
        crate = ltrsrv_crate_find(intf, serial);
    }
    return crate;
}


/***************************************************************************//**
  Пробуем определить длину команды по принятой ее части (client->cmd_pos в
    client->cmd). Если определелили длину, то сохраняем ее в client->cmd_size
    и ставим client->cmd_state = _CMD_STATE_RECV_FULL;
    Если определить дину нельзя, то ставим длину в client->cmd_size длину, которую
    нам нужно принять, чтобы понять какой длины вся команда и состояние cmd_state
    остается без изменений
    @param client  описатель клиента
    @return Код ошибки
    ***************************************************************************/
int p_client_cmd_get_len(t_ltr_client *client) {
    int err = 0, fnd;
    unsigned int i;

    client->last_cmd_code = client->cmd->cmd;

    /* для всех новых команд размер определяется стандартным образом - размер заголовка +
       число указанное в первом слове (второе слово указывает размер для приема) */
    if ((client->cmd->cmd & ~LTR_CLIENT_CMD_EX_MSK) == LTR_CLIENT_CMD_EX_BASE) {
        if (client->cmd_size < LTR_CLIENT_CMD_EX_HDR_SIZE) {
            client->cmd_size = LTR_CLIENT_CMD_EX_HDR_SIZE;
        } else {            
            client->cmd_size = LTR_CLIENT_CMD_EX_HDR_SIZE + CMD_EX_GET_DATA_SIZE(client);
            client->cmd_state = CMD_STATE_RECV_FINAL;
        }
    } else {
        for (i=0, fnd=0; (i < sizeof(f_cmd_tbl)/sizeof(f_cmd_tbl[0])) && !fnd; i++) {
            if (client->cmd->cmd == f_cmd_tbl[i].cmd) {
                fnd = 1;
                /* если команда фиксированной длины - то просто устанавливаем длину,
                  иначе - вызываем callback, чтобы определить длину */
                if (f_cmd_tbl[i].cb_size==NULL) {
                    client->cmd_size = f_cmd_tbl[i].std_size;
                    client->cmd_state = CMD_STATE_RECV_FINAL;
                } else {
                    err = f_cmd_tbl[i].cb_size(client);
                }
            }
        }
        if (!fnd) {
            err = LTRSRV_ERR_CLIENT_UNSUP_CMD_CODE;
            ltrsrv_client_close_req(client, err);
        }
    }
    return err;
}

/* обработка принятой команды (вызывается когда приняли все данные команды) */
int p_client_cmd_process (t_ltr_client *client) {
    int err = 0,fnd;
    int resp_size=0;
    t_cmd_status status = CMD_STATUS_DONE;
    unsigned int i;


    for (i=0, fnd=0; (i < sizeof(f_cmd_tbl)/sizeof(f_cmd_tbl[0])) && !fnd; i++) {
        if (client->cmd->cmd == f_cmd_tbl[i].cmd) {
            fnd = 1;
            if (f_cmd_tbl[i].cb_proc != NULL) {
                err = f_cmd_tbl[i].cb_proc(client, &resp_size, &status);
            } else {
                status = CMD_STATUS_NEED_RESP;
            }
        }
    }

    if (!fnd) {
        err = LTRSRV_ERR_CLIENT_UNSUP_CMD_CODE;
        ltrsrv_client_close_req(client, err);
    } else {
        /* посылка ответа, если нужно */
        if (status==CMD_STATUS_NEED_RESP)  {
            int ack_err = f_cmd_send_ack(client, err, resp_size);

            if (!err || (err == LTRSRV_ERR_MODULE_IN_USE))
                err = ack_err;
        }
    }
    return err;
}

/* обработчик команды, вызываемый после того, как был передан ответ на команду
 * клиенту */
int p_client_cmd_post_process(t_ltr_client *client) {
    int err = 0, fnd;
    unsigned int i;
    for (i=0, fnd=0; (i < sizeof(f_cmd_tbl)/sizeof(f_cmd_tbl[0])) && !fnd; i++) {
        if (client->last_cmd_code == f_cmd_tbl[i].cmd) {
            fnd = 1;
            if (f_cmd_tbl[i].cb_post != NULL)
                err = f_cmd_tbl[i].cb_post(client);
        }
    }

    if (!fnd) {
        err = LTRSRV_ERR_CLIENT_UNSUP_CMD_CODE;
        ltrsrv_client_close_req(client, err);
    }

    return err;
}







/****************************************************************************//**
  Обработка команды инициализации. Определяем, какое устанавливается соединение -
  для управления или для данных, и с каким крейтом и модулем соединяемся,
  проверяя их наличие.
  В ответ при успехе возвращаем копию команды, но всегда с заполненным серийным
  крейта
  *****************************************************************************/
static int f_cmd_proc_init(t_ltr_client *client, int *resp_size, t_cmd_status *status) {
    int err = 0;
    /* если пришла первая команда инициализации - смотрим какой серйный
       номер указал клиент и к какому слоту он подключается */
    if (client->state == LTRSRV_CLIENT_STATE_INIT) {
        const char* serial = (const char*)client->cmd->data;                
        uint8_t slot  =  client->cmd->data[LTR_CRATE_SERIAL_SIZE];
        uint8_t flags =  client->cmd->data[LTR_CRATE_SERIAL_SIZE+1];
        /* завершающий нулевой символ в конце строки с серийным, на случай, если передан мусор */
        client->cmd->data[LTR_CRATE_SERIAL_SIZE - 1] = 0;

        client->flags = flags;

        /* это подключение к управляющему каналу сервера - крейт не требуется */
        if (!strcmp(serial, LTR_CLIENT_REQ_CSN_SERVER_CONTROL)) {
            client->state = LTRSRV_CLIENT_STATE_CONTROL;
            client->slot = 0;
            client->crate = NULL;
#ifdef LTRD_LOGPROTO_ENABLED
        } else if (!strcmp(serial, LTR_CLIENT_REQ_CSN_LOGPROTO)) {
            client->state = LTRSRV_CLIENT_STATE_LOGPROTO;
            client->slot = 0;
            client->crate = NULL;            
#endif
        } else {
            /* иначе - ищем запрашиваемый крейт */
            client->crate = ltrsrv_crate_find((t_ltr_crate_interface)(flags&LTRSRV_CLIENT_FLAG_INTF_MSK), serial);
            if (client->crate != NULL) {
                /* если запрос к управляющему каналу крейта */
                if (slot == LTR_CLIENT_SLOT_CONTROL) {
                    client->state = LTRSRV_CLIENT_STATE_CONTROL;
                    client->slot = 0;
                } else {
                    /* если подключение к модулю - проверяем, что слот не пуст */
                    err = ltrsrv_crate_register_client(client->crate, --slot, client);
                    if (!err || (err == LTRSRV_ERR_MODULE_IN_USE)) {
                        client->state = LTRSRV_CLIENT_STATE_DATA_STREAM;
                        client->slot = slot;
                    }
                }
            } else {
                err = LTRSRV_ERR_CLIENT_CRATE_NOT_FOUND;
            }
        }


        if (!err || (err == LTRSRV_ERR_MODULE_IN_USE)) {
            p_client_create_logstr(client);
            ltrsrv_log_rec(LTRSRV_LOGLVL_DBG_HIGH, client->log_str, 0,
                           "New client initialized");
        }

        *resp_size = LTR_CLIENT_INIT_CMD_SIZE;
        /* в ответ на инициализацию передаем ту же команду, но заменяем
           срийный номер, на номер открытого крейта */
        memmove(CMD_RESP_DATA(client), client->cmd, LTR_CLIENT_INIT_CMD_SIZE);
        if (client->crate) {
            uint32_t tmark = ((client->crate->stat.total_start_marks & 0xFFFF) << 16)
                             | (client->crate->stat.total_sec_marks & 0xFFFF);

            strncpy((char*)CMD_RESP_DATA(client) + LTR_CLIENT_CMD_SIZE,
                    client->crate->descr.serial, LTR_CRATE_SERIAL_SIZE);
            CMD_SET_UINT32(CMD_RESP_DATA(client),
                           LTR_CLIENT_CMD_SIZE + LTR_CRATE_SERIAL_SIZE + 2,
                           tmark);
        }
        *status = CMD_STATUS_NEED_RESP;
    } else {
        err = LTRSRV_ERR_CLIENT_INVALID_INIT_CMD;
    }

    /* при ошибке инициализации соединения - соединение нужно закрыть сразу после ответа */
    if (err && (err != LTRSRV_ERR_MODULE_IN_USE))
        ltrsrv_client_close_req(client, err);


    return err;
}

static int f_cmd_post_init(t_ltr_client *client) {
    int err = 0;
    if (client->state == LTRSRV_CLIENT_STATE_DATA_STREAM) {
        ltrsrv_crate_client_init(client->crate, client->slot, client);
#ifdef LTRD_LOGPROTO_ENABLED
    } else if (client->state == LTRSRV_CLIENT_STATE_LOGPROTO) {
        err = ltrsrv_logproto_add_client(client);
#endif
    }

    return err;
}

/* Получаем списко срийных номеров крейта */
static int f_cmd_proc_get_crates(t_ltr_client *client, int *resp_size, t_cmd_status *status) {
    *resp_size = 16*LTR_CRATE_SERIAL_SIZE;
    memset(CMD_RESP_DATA(client), 0, *resp_size);
    ltrsrv_get_crates((char(*)[16])CMD_RESP_DATA(client), 16, NULL);
     *status = CMD_STATUS_NEED_RESP;
    return 0;
}

/* Получаем список модулей для крейта, с которым соединен клиент */
static int f_cmd_proc_get_crate_modules(t_ltr_client *client, int *resp_size, t_cmd_status *status) {
    int err = 0;
    if (client->crate) {
        *resp_size = LTR_CRATE_MODULES_MAX_CNT*2;
        err = ltrsrv_get_crate_modules(client->crate,
                (uint16_t*)CMD_RESP_DATA(client), LTR_CRATE_MODULES_MAX_CNT);
    } else {
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;
    }

    *status = CMD_STATUS_NEED_RESP;
    return err;
}

/* получение типа крейта и типа интерфейса связи с крейтом */
static int f_cmd_proc_get_crate_type(t_ltr_client *client, int *resp_size, t_cmd_status *status) {
    int err = 0;
    if (client->crate != NULL) {
        *resp_size = 2;
        CMD_RESP_DATA(client)[0] = client->crate->type;
        CMD_RESP_DATA(client)[1] = client->crate->par.intf;
    } else {
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;
    }
    *status = CMD_STATUS_NEED_RESP;
    return err;
}

/* получение списка сохранненых адресов с использованием фильтра по маске */
static int f_cmd_proc_ip_get_list(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint32_t msk, ip, max_cnt, fnd, returned;
    uint8_t *data = client->cmd->data;
    int resp_size = 8;
    max_cnt = CMD_GET_UINT32(data, 0);
    ip      = CMD_GET_UINT32(data, 4);
    msk     = CMD_GET_UINT32(data, 8);

    /* сперва смотрим, сколько нашли записей */
    ltrsrv_eth_get_ip_entry_list(NULL, 0, msk, ip, &fnd);

    /* вернем не больше max_cnt */
    returned = MIN(fnd, max_cnt);


    if (returned!=0) {
        /* проверяем, что буфера достаточно, чтобы передать все найденные
           записи */
        err = p_client_cmd_realloc_buf(client, resp_size + LTR_CLIENT_ACK_SIZE +
                      returned*sizeof(t_ltr_crate_client_ip_entry));
        if (!err) {
            err = ltrsrv_eth_get_ip_entry_list((t_ltr_crate_client_ip_entry*)
                            (CMD_RESP_DATA(client) + resp_size),
                            returned, msk, ip, NULL);
            if (!err)
                resp_size+=returned*sizeof(t_ltr_crate_client_ip_entry);
        }
    }

    /* в первом слове возвращаем, сколько нашли, во втором - сколько вернем */
    CMD_SET_UINT32(CMD_RESP_DATA(client), 0, fnd);
    CMD_SET_UINT32(CMD_RESP_DATA(client), 4, returned);


    *status = CMD_STATUS_NEED_RESP;
    *presp_size = resp_size;
    return err;
}

/* добавление записи в таблицу IP-адресов */
static int f_cmd_proc_ip_add(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    uint8_t *data = client->cmd->data;
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return ltrsrv_eth_add_ip_entry(CMD_GET_UINT32(data, 0), CMD_GET_UINT32(data, 4),
                                   CMD_GET_UINT32(data, 8), 1);
}


/* добавление записи в таблицу IP-адресов */
static int f_cmd_proc_ip_rem(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return ltrsrv_eth_rem_ip_entry(CMD_GET_UINT32(client->cmd->data, 0),
                                   CMD_GET_UINT32(client->cmd->data, 4), 1);
}

static int f_cmd_proc_ip_con(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
     *status = CMD_STATUS_NEED_RESP;
     *presp_size = 0;
     return ltrsrv_eth_ip_conn(CMD_GET_UINT32(client->cmd->data, 0));
}

static int f_cmd_proc_ip_disc(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
     *status = CMD_STATUS_NEED_RESP;
     *presp_size = 0;
     return ltrsrv_eth_ip_disc(CMD_GET_UINT32(client->cmd->data, 0));
}

static int f_cmd_proc_ip_con_auto(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
     *status = CMD_STATUS_NEED_RESP;
     *presp_size = 0;
     return ltrsrv_eth_start_autocon();
}

static int f_cmd_proc_ip_disc_all(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return ltrsrv_eth_ip_disc_all();
}

static int f_cmd_proc_ip_set_flags(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    uint8_t *data =  client->cmd->data;
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return ltrsrv_eth_ip_set_flags(CMD_GET_UINT32(data, 0), CMD_GET_UINT32(data, 4),
                                   CMD_GET_UINT32(data, 8), 1);
}

/* Функция оставлена только для совместимости с программами, по каким-то
   соображаниям посылающие данную команду. Ничего не делаем */
static int f_cmd_proc_ip_set_discovery_mode(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return 0;
}

/* Возвращаем всегда значение соответствующее запрету режима (так как реально он никогда не был реализован
   и в ltrd вообще нет кода его поддержки */
static int f_cmd_proc_ip_get_discovery_mode(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    CMD_SET_UINT32(CMD_RESP_DATA(client), 0, 0);
    CMD_SET_UINT32(CMD_RESP_DATA(client), 4, 0);
    *presp_size = 8;
    *status = CMD_STATUS_NEED_RESP;
    return 0;
}

static int f_cmd_proc_get_log_lvl(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    CMD_SET_UINT32(CMD_RESP_DATA(client), 0, ltrsrv_log_get_cur_lvl());
    *presp_size = 4;
    *status = CMD_STATUS_NEED_RESP;
    return 0;
}

static int f_cmd_proc_set_log_lvl(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    *status = CMD_STATUS_NEED_RESP;
    *presp_size = 0;
    return ltrsrv_log_set_lvl((t_ltrsrv_log_level)CMD_GET_UINT32(client->cmd->data, 0),
                              CMD_GET_UINT32(client->cmd->data, 4), 1);
}

static int f_cmd_proc_srv_ver(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    uint8_t *data = CMD_RESP_DATA(client);
    data[0] = LTRD_VERSION_PATCH;
    data[1] = LTRD_VERSION_REV;
    data[2] = LTRD_VERSION_MINOR;
    data[3] = LTRD_VERSION_MAJOR;
    *presp_size = 4;
    *status = CMD_STATUS_NEED_RESP;
    return 0;
}



static int f_cmd_post_srv_restart(t_ltr_client *client) {
    ltrsrv_log_rec(LTRSRV_LOGLVL_INFO, client->log_str, 0, "Request restart " LTRD_PROGRAMM_NAME);
    ltrsrv_req_server_restart();
    return 0;
}

static int f_cmd_post_srv_shutdown(t_ltr_client *client) {
    ltrsrv_log_rec(LTRSRV_LOGLVL_INFO, client->log_str, 0, "Request shutdown " LTRD_PROGRAMM_NAME);
    ltrsrv_req_server_close();
    return 0;
}


/* завершение команды PUT_ARRAY */
static int f_cmd_cb_put_array(struct st_ltr_crate *hnd, int err, void *prv_data) {
    if (prv_data!=NULL) {
        t_ltr_client* client = (t_ltr_client *)prv_data;
        client->wt_cb = 0;
        if (client->state == LTRSRV_CLIENT_STATE_CLOSED) {
            ltrsrv_client_close(client);
        } else {
            if (err) {
                ltrsrv_log_rec(LTRSRV_LOGLVL_ERR, client->log_str, err, "Can't handle client put array command");
            }
            f_cmd_send_ack(client, err, 0);
        }
    }
    return 0;
}

static int f_cmd_cb_get_array(struct st_ltr_crate *hnd, int err, void *prv_data) {
    if (prv_data!=NULL) {
        t_ltr_client* client = (t_ltr_client *)prv_data;
        client->wt_cb = 0;
        if (client->state == LTRSRV_CLIENT_STATE_CLOSED) {
            ltrsrv_client_close(client);
        } else {
            if (err) {
                ltrsrv_log_rec(LTRSRV_LOGLVL_ERR, client->log_str, err, "Can't handle client get array command");
            }
            f_cmd_send_ack(client, err, client->def_recv_size);
        }
    }
    return 0;
}

static int f_cmd_cb_crate_ioctl(struct st_ltr_crate *hnd, int err, void *prv_data) {
    if (prv_data!=NULL) {
        t_ltr_client* client = (t_ltr_client *)prv_data;
        client->wt_cb = 0;
        if (client->state == LTRSRV_CLIENT_STATE_CLOSED) {
            ltrsrv_client_close(client);
        } else {            
            CMD_EX_SET_RESP_SIZE(client, client->def_recv_size);
            if (err) {
                ltrsrv_log_rec(LTRSRV_LOGLVL_ERR, client->log_str, err, "Can't handle client crate ioctl command");
            }
            f_cmd_send_ack(client, err, client->def_recv_size+4);
        }
    }
    return 0;
}

/* обработка команды PUT_ARRAY */
static int f_cmd_proc_put_array(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    if (client->crate)  {
        /* передаем команду на запись регистров FPGA. команда будет завершена позже */
        err = ltrsrv_crate_start_client_cmd(client->crate, LTR_CRATE_CMD_CODE_WRITE, 0,
                                            LTR_CMD_ARRAY_ADDR(client), &client->cmd->data[8],
                                            LTR_CMD_ARRAY_SIZE(client), 0,
                                            f_cmd_cb_put_array, client);
        if (!err)
            client->wt_cb=1;
    } else {
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;
    }

    *status = err ? CMD_STATUS_NEED_RESP : CMD_STATUS_DEFFERED;
    return err;
}


static int f_cmd_proc_get_array(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    if (client->crate != NULL) {
        client->def_recv_size = LTR_CMD_ARRAY_SIZE(client);        
        p_client_cmd_realloc_buf(client, client->def_recv_size);
        /* передаем команду на запись регистров FPGA. команда будет завершена позже */
        err = ltrsrv_crate_start_client_cmd(client->crate, LTR_CRATE_CMD_CODE_READ, 0,
                                            LTR_CMD_ARRAY_ADDR(client), CMD_RESP_DATA(client),
                                            0, &client->def_recv_size,
                                            f_cmd_cb_get_array, client);
        if (!err)
            client->wt_cb=1;
    } else {
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;
    }

    *status = err ? CMD_STATUS_NEED_RESP : CMD_STATUS_DEFFERED;
    return err;
}





/* получение описателя крейта */
static int f_cmd_proc_get_crate_descr(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint32_t resp_size = 0;
    uint8_t* cmd_data;
    uint32_t cmd_resp_max_size;

    err = f_cmd_ex_proc_hdr(client, LTR_CRATE_SERIAL_SIZE + 1, NULL, &cmd_resp_max_size, &cmd_data);

    if (!err) {
        const char* serial = (const char*)cmd_data;
        t_ltr_crate_interface intf =  (t_ltr_crate_interface)cmd_data[LTR_CRATE_SERIAL_SIZE];
        t_ltr_crate* crate = f_get_crate(serial, intf, client);
        if (crate != NULL) {
            resp_size = crate->descr.snd_size;
            if (resp_size > cmd_resp_max_size)
                resp_size = cmd_resp_max_size;

            err = f_cmd_ex_resp_buf_realloc(client, resp_size);
            if (!err) {
                memcpy(CMD_EX_RESP_DATA(client), &crate->descr, resp_size);
            }
        }
    }

    return f_cmd_ex_set_resp(client, err, resp_size, presp_size, status);
}




/* получение статистики по крейту */
static int f_cmd_proc_get_crate_stat(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint32_t resp_size = 0;
    uint8_t* cmd_data;
    uint32_t cmd_resp_max_size;

    err = f_cmd_ex_proc_hdr(client, LTR_CRATE_SERIAL_SIZE + 1, NULL, &cmd_resp_max_size, &cmd_data);
    if (!err) {
        const char* serial = (const char*)cmd_data;
        t_ltr_crate_interface intf =  (t_ltr_crate_interface)cmd_data[LTR_CRATE_SERIAL_SIZE];
        t_ltr_crate* crate = f_get_crate(serial, intf, client);
        if (crate != NULL) {
            int i;            
            resp_size = sizeof(t_ltr_client_crate_stat);
            err = f_cmd_ex_resp_buf_realloc(client, resp_size);
            if (!err) {
                t_ltr_client_crate_stat* cstat = (t_ltr_client_crate_stat *)CMD_EX_RESP_DATA(client);

                if (resp_size > cmd_resp_max_size)
                    resp_size = cmd_resp_max_size;
                memset(cstat, 0, sizeof(t_ltr_client_crate_stat));
                cstat->snd_size = sizeof(t_ltr_client_crate_stat);
                cstat->flags = crate->par.flags;
                UINT64_ASSIGN32(cstat->con_time, crate->par.register_time);
                cstat->crate_state = crate->par.state;
                cstat->crate_mode = crate->par.mode;
                cstat->crate_type = crate->type;
                cstat->crate_intf = crate->par.intf;
                cstat->ctl_clients_cnt = p_client_get_ctl_cnt_from_crate(crate);
                cstat->modules_cnt = crate->modules_cnt;
                for (i = 0; i < crate->modules_cnt; i++) {
                    cstat->total_mod_clients_cnt += crate->modules[i].client_cnt;
                    cstat->mids[i] = CRATE_GET_MODULE_MID(crate, i);
                }
                cstat->stat = crate->stat;
            }
        } else {
            err = LTRSRV_ERR_CLIENT_CRATE_NOT_FOUND;
        }
    }

    return f_cmd_ex_set_resp(client, err, resp_size, presp_size, status);
}

static int f_cmd_proc_get_module_stat(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    unsigned int resp_size = 0;
    uint8_t* cmd_data;
    uint32_t cmd_resp_max_size;

    err = f_cmd_ex_proc_hdr(client, LTR_CRATE_SERIAL_SIZE + 4, NULL, &cmd_resp_max_size, &cmd_data);
    if (!err) {
        /* номер слота идет сразу за серийным номером */
        const char* serial = (const char*)cmd_data;
        t_ltr_crate_interface intf = (t_ltr_crate_interface)cmd_data[LTR_CRATE_SERIAL_SIZE];
        int slot = cmd_data[LTR_CRATE_SERIAL_SIZE+2] |
                   ((uint32_t)cmd_data[LTR_CRATE_SERIAL_SIZE+3] << 8) ;

        if ((slot > LTR_CRATE_MODULES_MAX_CNT) || (slot==0)) {
            err = LTRSRV_ERR_CLIENT_CMD_PARMS;
        } else {
            t_ltr_crate* crate = f_get_crate(serial, intf, client);
            slot-=1;
            if (crate!=NULL) {
                t_ltr_module* module = &crate->modules[slot];

                if (module->state != LTR_MODULE_STATE_OFF) {
                    resp_size = sizeof(t_ltr_client_module_stat);
                    err = f_cmd_ex_resp_buf_realloc(client, resp_size);
                    if (!err) {
                        t_ltr_client_module_stat *mstat = (t_ltr_client_module_stat *)CMD_EX_RESP_DATA(client);

                        if (resp_size > cmd_resp_max_size)
                            resp_size = cmd_resp_max_size;

                        memset(mstat, 0, sizeof(t_ltr_client_module_stat));
                        mstat->snd_size = sizeof(t_ltr_client_module_stat);
                        mstat->flags = module->flags;
                        mstat->mid = CRATE_GET_MODULE_MID(crate, slot);
                        strncpy(mstat->name, module->name, LTR_CRATE_MODULE_NAME_SIZE - 1);
                        mstat->name[LTR_CRATE_MODULE_NAME_SIZE - 1] = '\0';
                        mstat->client_cnt = module->client_cnt;
                        mstat->stat = module->stat;
                        mstat->stat.send_srvbuf_size = CYCLEBUF_SIZE(&module->sndbuf);
                        mstat->stat.send_srvbuf_full = CYCLEBUF_FILL_SIZE(&module->sndbuf);
                        mstat->stat.recv_srvbuf_size = CYCLEBUF_SIZE(&module->rcvbuf);
                        mstat->stat.recv_srvbuf_full = CYCLEBUF_FILL_SIZE(&module->rcvbuf);
                    }
                } else {
                    err = LTRSRV_ERR_CRATE_SLOT_EMPTY;
                }
            } else {
                err = LTRSRV_ERR_CLIENT_CRATE_NOT_FOUND;
            }
        }
    }

    return f_cmd_ex_set_resp(client, err, resp_size, presp_size, status);
}


static int f_cmd_proc_reset_module(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint8_t* cmd_data;

    err = f_cmd_ex_proc_hdr(client, LTR_CRATE_SERIAL_SIZE + 8, NULL, NULL, &cmd_data);
    if (!err) {
        const char* serial = (const char*)cmd_data;
        /* интерфейс идет сразу за серийным номером */
        t_ltr_crate_interface intf = (t_ltr_crate_interface)cmd_data[LTR_CRATE_SERIAL_SIZE];
        /* далее идет номер слота */
        int slot = cmd_data[LTR_CRATE_SERIAL_SIZE+2] |
                 ((uint32_t)cmd_data[LTR_CRATE_SERIAL_SIZE+3] << 8) ;
        /* потом 32-бита - резервные флаги */
        uint32_t flags = CMD_GET_UINT32(cmd_data, LTR_CRATE_SERIAL_SIZE+4);
        if ((slot > LTR_CRATE_MODULES_MAX_CNT) || (slot==0)) {
            err = LTRSRV_ERR_CLIENT_CMD_PARMS;
        } else {
            t_ltr_crate* crate = f_get_crate(serial, intf, client);
            if (crate!=NULL) {
                err = ltrsrv_crate_reset_module(crate, --slot, flags);
            } else {
                err = LTRSRV_ERR_CLIENT_CRATE_NOT_FOUND;
            }
        }
    }

    return f_cmd_ex_set_resp(client, err, 0, presp_size, status);
}

static int f_cmd_proc_set_server_param(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint8_t* cmd_data;
    uint32_t cmd_data_size;

    err = f_cmd_ex_proc_hdr(client, 4, &cmd_data_size, NULL, &cmd_data);
    if (!err) {
        uint32_t param = CMD_GET_UINT32(cmd_data, 0);
        err = ltrsrv_set_param((t_ltrd_params)param, &cmd_data[4], cmd_data_size-4);
    }
    return f_cmd_ex_set_resp(client, err, 0, presp_size, status);
}

static int f_cmd_proc_get_server_param(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint8_t* cmd_data;
    uint32_t resp_size;

    err = f_cmd_ex_proc_hdr(client, 4, NULL, &resp_size, &cmd_data);
    if (!err) {
        uint32_t param = CMD_GET_UINT32(cmd_data, 0);
        err = f_cmd_ex_resp_buf_realloc(client, resp_size);
        if (!err) {
            err = ltrsrv_get_param((t_ltrd_params)param,
                                   CMD_EX_RESP_DATA(client), &resp_size);
        }
    }

    return f_cmd_ex_set_resp(client, err, resp_size, presp_size, status);
}

static int f_cmd_proc_get_crates_with_info(t_ltr_client *client, int *presp_size, t_cmd_status* status) {
    int err = 0;
    uint8_t* cmd_data;
    uint32_t resp_size;
    uint32_t flags = 0;
    uint32_t data_size = 0;

    err = f_cmd_ex_proc_hdr(client, 0, &data_size, &resp_size, &cmd_data);
    if (data_size >= sizeof(flags)) {
        memcpy(&flags, cmd_data, sizeof(flags));
    }

    if (!err && (resp_size < 4))
        err = LTRSRV_ERR_CLIENT_CMD_PARMS;
    if (!err)
        err = f_cmd_ex_resp_buf_realloc(client, resp_size);
    if (!err) {
        int entry_cnt = resp_size > 8 ? (resp_size - 8)/sizeof(t_crate_info_entry) : 0;
        int fnd;
        uint8_t *resp_data = CMD_EX_RESP_DATA(client);

        ltrsrv_get_crates_info((t_crate_info_entry*)&resp_data[8], entry_cnt, flags, &fnd);

        if (entry_cnt>fnd)
            entry_cnt = fnd;

        if (resp_size > 8) {
            resp_size = 8 + entry_cnt*sizeof(t_crate_info_entry);
        } else if (resp_size != 8) {
            resp_size = 4;
        }

        CMD_SET_UINT32(resp_data, 0, fnd);

        if (resp_size>=8) {
            CMD_SET_UINT32(resp_data, 4, entry_cnt);
        }
    }

    return f_cmd_ex_set_resp(client, err, resp_size, presp_size, status);
}

static int f_cmd_proc_crate_ioctl(t_ltr_client *client, int *presp_size, t_cmd_status *status) {
    int err = 0;
    uint32_t cmd_data_size;

    err = f_cmd_ex_proc_hdr(client, 8, &cmd_data_size, &client->def_recv_size, NULL);
    if (!err)
        err = f_cmd_ex_resp_buf_realloc(client, client->def_recv_size);

    if (!err && (client->crate==NULL))
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;

    if (!err) {
        uint8_t *cmd_data = CMD_EX_GET_DATA_PTR(client);
        uint8_t *resp_data = CMD_EX_RESP_DATA(client);
        uint32_t req;
        uint32_t param;
        uint32_t snd_ctl_size;


        req = CMD_GET_UINT32(cmd_data, 0);
        param = CMD_GET_UINT32(cmd_data, 4);
        snd_ctl_size = cmd_data_size-8;
        memmove(resp_data, &cmd_data[8], snd_ctl_size);


        err = ltrsrv_crate_start_client_cmd(client->crate, LTR_CRATE_CMD_CODE_IOCTL,
                                            req, param, resp_data, snd_ctl_size,
                                            &client->def_recv_size,
                                            f_cmd_cb_crate_ioctl, client);
    }


    if (err) {
        err = f_cmd_ex_set_resp(client, err, 0, presp_size, status);
    } else {
        client->wt_cb = 1;
        *status = CMD_STATUS_DEFFERED;
    }

    return err;
}

static int f_cmd_proc_load_fpga(t_ltr_client *client, int *resp_size, t_cmd_status *status) {
    int err = 0;
    if (client->crate != NULL) {
        /** @note сейчас делаем просто перезагрузку (используется при прошивке
         *  фьюзов AVR), т.к. функции LTR010 скорее всего больше не понадобятся... */
        err = ltrsrv_crate_reopen(client->crate);
    } else {
        err = LTRSRV_ERR_CLIENT_CMD_UNSUP_FOR_SRV_CTL;
    }

    *status = CMD_STATUS_NEED_RESP;
    return err;
}

int f_cmd_len_put_array(t_ltr_client *client) {
    if (client->cmd_size < LTR_CLIENT_CMD_PUT_ARRAY_SIZE) {
        client->cmd_size = LTR_CLIENT_CMD_PUT_ARRAY_SIZE;
    } else {
        uint32_t len = LTR_CMD_ARRAY_SIZE(client);
        client->cmd_size = LTR_CLIENT_CMD_PUT_ARRAY_SIZE + len;
        client->cmd_state = CMD_STATE_RECV_FINAL;
    }
    return 0;
}






