/** * @file etcp_api.c * @brief Реализация API для приёма-передачи пакетов через ETCP */ #include "etcp_api.h" #include "etcp.h" #include "pkt_normalizer.h" #include "../lib/debug_config.h" #include #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); }