/**
 * @file    icp67_core.c
 * @brief   ICP67 核心 — 初始化、回调注册、重传管理、发送函数、定时器
 * @details 协议引擎控制中心，包含:
 *          - icp67_init()  初始化实例、绑定内建回调、创建信号量
 *          - Sender 函数 (13个)  方向: libself_ptl → icp67 → 物理层
 *          - 重传管理 (3个)  rtx_sem 保护所有字段读写，避免竞态
 *          - 定时器处理  每 10ms tick, 超时触发重发 (最多 5 次)
 */

#include "icp67.h"
#include "myLog.h"
#include <string.h>
#include <semaphore.h>

/* =========================== 前向声明 (sender 函数) =========================== */

static void icp67_ao_get(stru_icp67 *p_icp67, stru_ti_1_2_data *p_data, uint16_t num);
static void icp67_ao_set(stru_icp67 *p_icp67, uint8_t *p_data, uint16_t num);
static void icp67_iec_point_tbl_get(stru_icp67 *p_icp67, uint16_t info_addr);
static void icp67_time_set(stru_icp67 *p_icp67, stru_ti_5 *p_data);
static void icp67_self_check_get(stru_icp67 *p_icp67);
static void icp67_upgrade_start(stru_icp67 *p_icp67, uint8_t dst);
static void icp67_dir_read(stru_icp67 *p_icp67, uint8_t *p_dir, uint8_t len,
	uint8_t *tm_start, uint8_t *tm_end);
static void icp67_file_read_act(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len);
static void icp67_file_read_confirm(stru_icp67 *p_icp67, uint8_t result, uint32_t id, uint32_t offset);
static void icp67_file_write_act(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
	uint32_t id, uint32_t size, uint16_t crc);
static void icp67_file_write(stru_icp67 *p_icp67, uint32_t id, uint32_t offset,
	uint8_t follow, uint8_t *p_data, uint16_t len);
static void icp67_file_write_end_confirm(stru_icp67 *p_icp67, uint8_t result,
	uint32_t id, uint8_t *md5);

/* =========================== 重传管理 (线程安全) =========================== */

void icp67_rtx_flag_get(stru_icp67 *p_icp67, uint8_t *flag, uint8_t *resend_cnt, uint32_t *tm_cnt)
{
	sem_wait(&p_icp67->rtx_sem);

	*flag       = p_icp67->rtx_flag;
	*resend_cnt = p_icp67->resend_cnt;
	*tm_cnt     = p_icp67->tm_cnt;

	sem_post(&p_icp67->rtx_sem);
}

void icp67_rtx_flag_set(stru_icp67 *p_icp67, uint8_t flag, uint8_t resend_cnt, uint32_t tm_cnt)
{
	sem_wait(&p_icp67->rtx_sem);

	if(ICP67_TX_FLAG == flag && ICP67_TX_FLAG == p_icp67->rtx_flag)
	{
		LOG_E("icp67_rtx_flag_set: overriding pending TX, prev resend_cnt=%d", p_icp67->resend_cnt);
	}

	p_icp67->rtx_flag   = flag;
	p_icp67->resend_cnt = resend_cnt;
	p_icp67->tm_cnt     = tm_cnt;

	if(ICP67_TX_FLAG == flag)
	{
		memcpy(p_icp67->resend_tx, p_icp67->tx, p_icp67->tx_len);
		p_icp67->resend_tx_len = p_icp67->tx_len;
	}

	sem_post(&p_icp67->rtx_sem);
}

void icp67_rtx_resend_inc(stru_icp67 *p_icp67)
{
	sem_wait(&p_icp67->rtx_sem);
	p_icp67->resend_cnt++;
	p_icp67->tm_cnt = 0;
	sem_post(&p_icp67->rtx_sem);
}

void icp67_rtx_tm_inc(stru_icp67 *p_icp67)
{
	sem_wait(&p_icp67->rtx_sem);
	p_icp67->tm_cnt++;
	sem_post(&p_icp67->rtx_sem);
}

/* =========================== 定时器处理 =========================== */

void icp67_timer_handler(stru_icp67 *p_icp67)
{
	uint8_t  rtx_flag   = 0;
	uint8_t  resend_cnt = 0;
	uint32_t tm_cnt     = 0;

	if(NULL == p_icp67)
	{
		return;
	}

	icp67_rtx_tm_inc(p_icp67);
	icp67_rtx_flag_get(p_icp67, &rtx_flag, &resend_cnt, &tm_cnt);

	if(ICP67_TX_FLAG == rtx_flag && resend_cnt < p_icp67->resend_max && tm_cnt >= p_icp67->tm_out)
	{
		p_icp67->send_cb(p_icp67->resend_tx, p_icp67->resend_tx_len, p_icp67->arg);

		icp67_rtx_resend_inc(p_icp67);

		LOG_I("resend cnt %d", p_icp67->resend_cnt);
		icp67_show_frame(p_icp67->resend_tx, p_icp67->resend_tx_len);
	}
}

/* =========================== 初始化 =========================== */

void icp67_init(stru_icp67 *p_icp67, icp67_send_cb send_cb, void *arg,
	uint32_t tm_out_ms, uint8_t resend_max)
{
	p_icp67->arg     = arg;
	p_icp67->send_cb = send_cb;

	p_icp67->search_frame_cb  = icp67_search_frame;
	p_icp67->decode_cb        = icp67_decode;
	p_icp67->timer_handler_cb = icp67_timer_handler;

	p_icp67->rtx_flag = 0;
	sem_init(&p_icp67->rtx_sem, 0, 1);

	p_icp67->resend_max = (0 == resend_max) ? ICP67_RESEND_MAX_DEF : resend_max;
	p_icp67->tm_out_ms  = (0 == tm_out_ms)  ? ICP67_TM_OUT_DEF_MS  : tm_out_ms;
	p_icp67->tm_out     = p_icp67->tm_out_ms / ICP67_TM_TICK_MS;
	p_icp67->resend_cnt = 0;
	p_icp67->tm_cnt     = 0;

	/* 初始化 sender 回调为内部函数 (方向: self_ptl → icp67) */
	p_icp67->method.ao_get_cb                = icp67_ao_get;
	p_icp67->method.ao_pop_out_cb            = NULL;
	p_icp67->method.ao_set_cb                = icp67_ao_set;
	p_icp67->method.iec_point_tbl_get_cb     = icp67_iec_point_tbl_get;
	p_icp67->method.iec_point_tbl_pop_out_cb = NULL;
	p_icp67->method.time_set_cb              = icp67_time_set;
	p_icp67->method.self_check_get_cb        = icp67_self_check_get;
	p_icp67->method.self_check_pop_out_cb    = NULL;
	p_icp67->method.upgrade_start_cb         = icp67_upgrade_start;

	p_icp67->method.dir_read_cb               = icp67_dir_read;
	p_icp67->method.dir_pop_out_cb            = NULL;
	p_icp67->method.file_read_act_cb          = icp67_file_read_act;
	p_icp67->method.file_read_act_confirm_cb  = NULL;
	p_icp67->method.file_read_cb              = NULL;
	p_icp67->method.file_read_confirm_cb      = icp67_file_read_confirm;
	p_icp67->method.file_write_act_cb         = icp67_file_write_act;
	p_icp67->method.file_write_act_confirm_cb = NULL;
	p_icp67->method.file_write_cb             = icp67_file_write;
	p_icp67->method.file_write_confirm_cb     = NULL;
	p_icp67->method.file_write_end_cb         = NULL;
	p_icp67->method.file_write_end_confirm_cb = icp67_file_write_end_confirm;

	p_icp67->method.zip_dir_pop_out_cb  = NULL;
	p_icp67->method.zip_file_pop_out_cb = NULL;

	p_icp67->method.mx_trans_cb   = NULL;
	p_icp67->method.st_trans_cb   = NULL;
	p_icp67->method.dd_trans_cb   = NULL;
	p_icp67->method.soe_trans_cb  = NULL;
	p_icp67->method.fault_trans_cb = NULL;
	p_icp67->method.mx_change_cb  = NULL;
}

int icp67_set_ao_cfg_md5(stru_icp67 *p_icp67, uint8_t *p_md5)
{
	if(NULL == p_icp67 || NULL == p_md5)
	{
		return -1;
	}

	memcpy(p_icp67->md5, p_md5, ICP67_MD5_LEN);

	return 0;
}

/* =========================== 回调注册 setter (17个) =========================== */

void icp67_set_ao_pop_out_cb(stru_icp67 *p_icp67, icp67_ao_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.ao_pop_out_cb = cb;
}

void icp67_set_iec_point_tbl_pop_out_cb(stru_icp67 *p_icp67, icp67_iec_point_tbl_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.iec_point_tbl_pop_out_cb = cb;
}

void icp67_set_self_check_pop_out_cb(stru_icp67 *p_icp67, icp67_self_check_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.self_check_pop_out_cb = cb;
}

void icp67_set_dir_pop_out_cb(stru_icp67 *p_icp67, icp67_dir_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.dir_pop_out_cb = cb;
}

void icp67_set_file_read_act_confirm_cb(stru_icp67 *p_icp67, icp67_file_read_act_confirm_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.file_read_act_confirm_cb = cb;
}

void icp67_set_file_read_cb(stru_icp67 *p_icp67, icp67_file_read_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.file_read_cb = cb;
}

void icp67_set_file_write_act_confirm_cb(stru_icp67 *p_icp67, icp67_file_write_act_confirm_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.file_write_act_confirm_cb = cb;
}

void icp67_set_file_write_confirm_cb(stru_icp67 *p_icp67, icp67_file_write_confirm_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.file_write_confirm_cb = cb;
}

void icp67_set_file_write_end_cb(stru_icp67 *p_icp67, icp67_file_write_end_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.file_write_end_cb = cb;
}

void icp67_set_zip_dir_pop_out_cb(stru_icp67 *p_icp67, icp67_zip_dir_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.zip_dir_pop_out_cb = cb;
}

void icp67_set_zip_file_pop_out_cb(stru_icp67 *p_icp67, icp67_zip_file_pop_out_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.zip_file_pop_out_cb = cb;
}

void icp67_set_mx_trans_cb(stru_icp67 *p_icp67, icp67_mx_trans_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.mx_trans_cb = cb;
}

void icp67_set_st_trans_cb(stru_icp67 *p_icp67, icp67_st_trans_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.st_trans_cb = cb;
}

void icp67_set_dd_trans_cb(stru_icp67 *p_icp67, icp67_dd_trans_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.dd_trans_cb = cb;
}

void icp67_set_soe_trans_cb(stru_icp67 *p_icp67, icp67_soe_trans_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.soe_trans_cb = cb;
}

void icp67_set_fault_trans_cb(stru_icp67 *p_icp67, icp67_fault_trans_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.fault_trans_cb = cb;
}

void icp67_set_mx_change_cb(stru_icp67 *p_icp67, icp67_mx_change_cb cb)
{
	if(NULL == p_icp67) return;
	p_icp67->method.mx_change_cb = cb;
}

/* =========================== Sender 函数 (方向: self_ptl → icp67 → 物理层) =========================== */

static void icp67_ao_get(stru_icp67 *p_icp67, stru_ti_1_2_data *p_data, uint16_t num)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	uint16_t  i;

	if(NULL == p_icp67 || NULL == p_data || 0 == num)
	{
		LOG_E("icp67_ao_get: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_1, COT_5, TI_1_INFO_ADDR, p_tx, &tx_len);

	{
		stru_ti_1_2 *p_asdu = (stru_ti_1_2 *)&p_tx[tx_len];

		memcpy(p_asdu->md5, p_icp67->md5, ICP67_MD5_LEN);
		p_asdu->num = num;
		tx_len += sizeof(stru_ti_1_2);
	}

	for(i = 0; i < num; i++)
	{
		stru_ti_1_2_data *p = (stru_ti_1_2_data *)&p_tx[tx_len];

		memcpy(p, &p_data[i], sizeof(stru_ti_1_2_data));
		tx_len += sizeof(stru_ti_1_2_data);
	}

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_ao_set(stru_icp67 *p_icp67, uint8_t *p_data, uint16_t num)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	uint16_t  pos    = 0;
	uint16_t  i;

	if(NULL == p_icp67 || NULL == p_data || 0 == num)
	{
		LOG_E("icp67_ao_set: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_2, COT_6, TI_1_INFO_ADDR, p_tx, &tx_len);

	{
		stru_ti_1_2 *p_asdu = (stru_ti_1_2 *)&p_tx[tx_len];

		memcpy(p_asdu->md5, p_icp67->md5, ICP67_MD5_LEN);
		p_asdu->num = num;
		tx_len += sizeof(stru_ti_1_2);
	}

	for(i = 0; i < num; i++)
	{
		stru_ti_1_2_data *p_src = (stru_ti_1_2_data *)&p_data[pos];

		memcpy(&p_tx[tx_len], p_src, sizeof(stru_ti_1_2_data_info) + p_src->data_len);
		tx_len += (sizeof(stru_ti_1_2_data_info) + p_src->data_len);
		pos    += (sizeof(stru_ti_1_2_data) + p_src->data_len);
	}

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_iec_point_tbl_get(stru_icp67 *p_icp67, uint16_t info_addr)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;

	if(NULL == p_icp67)
	{
		LOG_E("icp67_iec_point_tbl_get: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_3, COT_5, info_addr, p_tx, &tx_len);
	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_time_set(stru_icp67 *p_icp67, stru_ti_5 *p_data)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;

	if(NULL == p_icp67 || NULL == p_data)
	{
		LOG_E("icp67_time_set: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_5, COT_5, TI_5_INFO_ADDR, p_tx, &tx_len);

	{
		stru_ti_5 *p_asdu = (stru_ti_5 *)&p_tx[tx_len];

		memcpy(p_asdu, p_data, sizeof(stru_ti_5));
		tx_len += sizeof(stru_ti_5);
	}

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_self_check_get(stru_icp67 *p_icp67)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;

	if(NULL == p_icp67)
	{
		LOG_E("icp67_self_check_get: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_6, COT_5, TI_6_INFO_ADDR, p_tx, &tx_len);
	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_upgrade_start(stru_icp67 *p_icp67, uint8_t dst)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;

	if(NULL == p_icp67)
	{
		LOG_E("icp67_upgrade_start: invalid input parameter");
		return;
	}

	icp67_make_head(dst, TI_9, COT_5, TI_9_INFO_ADDR, p_tx, &tx_len);
	p_tx[tx_len++] = ICP67_UPGRADE_TYPE;
	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_dir_read(stru_icp67 *p_icp67, uint8_t *p_dir, uint8_t len,
	uint8_t *tm_start, uint8_t *tm_end)
{
	uint8_t  *p_tx    = p_icp67->tx;
	uint16_t  tx_len  = 0;
	stru_ti_11_1      *p_asdu;
	stru_ti_11_1_info *p_info;

	if(NULL == p_icp67 || NULL == p_dir || 0 == len)
	{
		LOG_E("icp67_dir_read: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, COT_5, TI_11_INFO_ADDR, p_tx, &tx_len);

	p_asdu = (stru_ti_11_1 *)&p_tx[tx_len];
	p_asdu->cmd = FILE_CMD_1;
	p_asdu->id  = 0;
	p_asdu->len = len;
	tx_len += sizeof(stru_ti_11_1);

	memcpy(&p_tx[tx_len], p_dir, len);
	tx_len += len;

	p_info = (stru_ti_11_1_info *)&p_tx[tx_len];
	if(tm_start && tm_end)
	{
		p_info->flag = 1;
		memcpy(p_info->start_tm, tm_start, ICP67_TM_STRUCT_LEN);
		memcpy(p_info->end_tm, tm_end, ICP67_TM_STRUCT_LEN);
	}
	else
	{
		p_info->flag = 0;
		memset(p_info->start_tm, 0, ICP67_TM_STRUCT_LEN);
		memset(p_info->end_tm, 0, ICP67_TM_STRUCT_LEN);
	}
	tx_len += sizeof(stru_ti_11_1_info);

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_file_read_act(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	stru_ti_11_3 *p_asdu;

	if(NULL == p_icp67 || NULL == p_name || 0 == len)
	{
		LOG_E("icp67_file_read_act: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, COT_5, TI_11_INFO_ADDR, p_tx, &tx_len);

	p_asdu = (stru_ti_11_3 *)&p_tx[tx_len];
	p_asdu->cmd = FILE_CMD_3;
	p_asdu->len = len;
	tx_len += sizeof(stru_ti_11_3);

	memcpy(&p_tx[tx_len], p_name, len);
	tx_len += len;

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_file_read_confirm(stru_icp67 *p_icp67, uint8_t result, uint32_t id, uint32_t offset)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	uint8_t   cot;

	if(NULL == p_icp67)
	{
		LOG_E("icp67_file_read_confirm: invalid input parameter");
		return;
	}

	cot = (0 == result ? COT_7 : COT_8);

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, cot, TI_11_INFO_ADDR, p_tx, &tx_len);

	{
		stru_ti_11_6 *p_asdu = (stru_ti_11_6 *)&p_tx[tx_len];

		p_asdu->cmd    = FILE_CMD_6;
		p_asdu->id     = id;
		p_asdu->offset = offset;
		p_asdu->result = result;
		tx_len += sizeof(stru_ti_11_6);
	}

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_file_write_act(stru_icp67 *p_icp67, uint8_t *p_name, uint8_t len,
	uint32_t id, uint32_t size, uint16_t crc)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	stru_ti_11_7       *p_asdu;
	stru_ti_11_file_info *p_info;

	if(NULL == p_icp67 || NULL == p_name || 0 == len)
	{
		LOG_E("icp67_file_write_act: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, COT_5, TI_11_INFO_ADDR, p_tx, &tx_len);

	p_asdu = (stru_ti_11_7 *)&p_tx[tx_len];
	p_asdu->cmd = FILE_CMD_7;
	p_asdu->len = len;
	tx_len += sizeof(stru_ti_11_7);

	memcpy(&p_tx[tx_len], p_name, len);
	tx_len += len;

	p_info = (stru_ti_11_file_info *)&p_tx[tx_len];
	p_info->id   = id;
	p_info->size = size;
	p_info->crc  = crc;
	tx_len += sizeof(stru_ti_11_file_info);

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);

	LOG_I("icp67_file_write_act: id=%d, size=%d, crc=%d, name=%s", id, size, crc, p_name);
}

static void icp67_file_write(stru_icp67 *p_icp67, uint32_t id, uint32_t offset,
	uint8_t follow, uint8_t *p_data, uint16_t len)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	stru_ti_11_9 *p_asdu;

	if(NULL == p_icp67 || NULL == p_data || 0 == len)
	{
		LOG_E("icp67_file_write: invalid input parameter");
		return;
	}

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, COT_5, TI_11_INFO_ADDR, p_tx, &tx_len);

	p_asdu = (stru_ti_11_9 *)&p_tx[tx_len];
	p_asdu->cmd    = FILE_CMD_9;
	p_asdu->id     = id;
	p_asdu->offset = offset;
	p_asdu->follow = follow;
	p_asdu->len    = len;
	tx_len += sizeof(stru_ti_11_9);

	memcpy(&p_tx[tx_len], p_data, len);
	tx_len += len;

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);

	icp67_rtx_flag_set(p_icp67, ICP67_TX_FLAG, 0, 0);
}

static void icp67_file_write_end_confirm(stru_icp67 *p_icp67, uint8_t result,
	uint32_t id, uint8_t *md5)
{
	uint8_t  *p_tx   = p_icp67->tx;
	uint16_t  tx_len = 0;
	uint8_t   cot;

	if(NULL == p_icp67 || NULL == md5)
	{
		LOG_E("icp67_file_write_end_confirm: invalid input parameter");
		return;
	}

	cot = (0 == result ? COT_7 : COT_8);

	icp67_make_head(ENUM_DEV_TYPE_SYS, TI_11, cot, TI_11_INFO_ADDR, p_tx, &tx_len);

	{
		stru_ti_11_11 *p_asdu = (stru_ti_11_11 *)&p_tx[tx_len];

		p_asdu->cmd    = FILE_CMD_11;
		p_asdu->id     = id;
		p_asdu->result = result;
		memcpy(p_asdu->md5, md5, ICP67_MD5_LEN);
		tx_len += sizeof(stru_ti_11_11);
	}

	icp67_make_tail(p_tx, &tx_len);

	p_icp67->tx_len = tx_len;
	p_icp67->send_cb(p_tx, tx_len, p_icp67->arg);
}
