You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.8 KiB
179 lines
5.8 KiB
/** |
|
* @file etcp_api.c |
|
* @brief Реализация API для приёма-передачи пакетов через ETCP |
|
*/ |
|
|
|
#include "etcp_api.h" |
|
#include "etcp.h" |
|
#include "pkt_normalizer.h" |
|
#include "../lib/debug_config.h" |
|
#include <string.h> |
|
|
|
#define DEBUG_CATEGORY_ETCP_API 1 |
|
|
|
#define ETCP_MAX_BINDINGS 256 /**< Максимальное количество bindings (по одному на каждый ID 0-255) */ |
|
|
|
/** |
|
* @brief Структура binding для ID |
|
*/ |
|
struct ETCP_BINDING { |
|
etcp_recv_fn callback; /**< Коллбэк для обработки пакетов */ |
|
uint8_t active; /**< Флаг активности binding */ |
|
}; |
|
|
|
// Таблица bindings (индекс = ID пакета) |
|
static struct ETCP_BINDING g_bindings[ETCP_MAX_BINDINGS]; |
|
static int g_api_initialized = 0; |
|
|
|
void etcp_api_init(void) { |
|
if (g_api_initialized) { |
|
return; |
|
} |
|
|
|
memset(g_bindings, 0, sizeof(g_bindings)); |
|
g_api_initialized = 1; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API initialized"); |
|
} |
|
|
|
void etcp_api_deinit(void) { |
|
if (!g_api_initialized) { |
|
return; |
|
} |
|
|
|
memset(g_bindings, 0, sizeof(g_bindings)); |
|
g_api_initialized = 0; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API deinitialized"); |
|
} |
|
|
|
int etcp_bind(uint8_t id, etcp_recv_fn callback) { |
|
if (!g_api_initialized) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: API not initialized"); |
|
return -1; |
|
} |
|
|
|
if (!callback) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: NULL callback for id=%u", id); |
|
return -1; |
|
} |
|
|
|
if (g_bindings[id].active) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_bind: ID %u already bound, overwriting", id); |
|
} |
|
|
|
g_bindings[id].callback = callback; |
|
g_bindings[id].active = 1; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Bound ID %u to callback %p", id, (void*)callback); |
|
return 0; |
|
} |
|
|
|
int etcp_unbind(uint8_t id) { |
|
if (!g_api_initialized) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: API not initialized"); |
|
return -1; |
|
} |
|
|
|
if (!g_bindings[id].active) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: ID %u not bound", id); |
|
return -1; |
|
} |
|
|
|
g_bindings[id].callback = NULL; |
|
g_bindings[id].active = 0; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u", id); |
|
return 0; |
|
} |
|
|
|
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
if (!conn) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL connection"); |
|
return -1; |
|
} |
|
|
|
if (!entry) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL entry"); |
|
return -1; |
|
} |
|
|
|
if (!conn->normalizer) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Connection has no normalizer"); |
|
return -1; |
|
} |
|
|
|
struct PKTNORM* pn = conn->normalizer; |
|
|
|
if (!pn->input) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Normalizer has no input queue"); |
|
return -1; |
|
} |
|
|
|
// Помещаем entry в очередь input normalizer |
|
// queue_data_put забирает ownership entry |
|
int result = queue_data_put(pn->input, entry, 0); |
|
|
|
if (result != 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_send: queue_data_put failed (queue full?)"); |
|
return -1; |
|
} |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending"); |
|
return 0; |
|
} |
|
|
|
void etcp_recv(struct ll_queue* queue, void* arg) { |
|
struct ETCP_CONN* conn = (struct ETCP_CONN*)arg; |
|
|
|
if (!queue || !conn) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Invalid arguments (queue=%p, conn=%p)", |
|
(void*)queue, (void*)conn); |
|
return; |
|
} |
|
|
|
// Получаем элемент из очереди |
|
struct ll_entry* entry = queue_data_get(queue); |
|
if (!entry) { |
|
// Очередь пуста - это нормально, просто возобновляем коллбэк |
|
queue_resume_callback(queue); |
|
return; |
|
} |
|
|
|
// Проверяем что есть данные |
|
if (!entry->dgram || entry->len == 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Empty packet received"); |
|
queue_entry_free(entry); |
|
queue_dgram_free(entry); |
|
queue_resume_callback(queue); |
|
return; |
|
} |
|
|
|
// Получаем ID пакета (первый байт кодограммы) |
|
uint8_t id = entry->dgram[0]; |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Received packet with ID %u from conn %p", |
|
id, (void*)conn); |
|
|
|
// Ищем binding для этого ID |
|
if (id < ETCP_MAX_BINDINGS && g_bindings[id].active && g_bindings[id].callback) { |
|
// Вызываем коллбэк |
|
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Delivering packet ID %u to callback %p", |
|
id, (void*)g_bindings[id].callback); |
|
g_bindings[id].callback(conn, entry); |
|
} else { |
|
// Нет binding для этого ID - по умолчанию считаем что это данные (ID=0) |
|
if (g_bindings[0].active && g_bindings[0].callback) { |
|
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u, routing to default (ID=0)", id); |
|
g_bindings[0].callback(conn, entry); |
|
} else { |
|
// Нет default binding - освобождаем пакет |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u and no default handler, dropping packet", id); |
|
queue_entry_free(entry); |
|
queue_dgram_free(entry); |
|
} |
|
} |
|
|
|
// Возобновляем коллбэк для получения следующего пакета |
|
queue_resume_callback(queue); |
|
}
|
|
|