#include "gateway_usb_setup.hpp" #include "gateway_controller.hpp" #include "driver/usb_serial_jtag.h" #include "esp_log.h" #include namespace gateway { namespace { constexpr const char* kTag = "gateway_usb"; constexpr size_t kCommandFrameMinLen = 7; } GatewayUsbSetupBridge::GatewayUsbSetupBridge(GatewayController& controller, DaliDomainService& dali_domain, GatewayUsbSetupBridgeConfig config) : controller_(controller), dali_domain_(dali_domain), config_(config) {} esp_err_t GatewayUsbSetupBridge::start() { if (started_) { return ESP_OK; } if (!config_.enabled) { ESP_LOGI(kTag, "USB Serial/JTAG setup bridge disabled; USB remains available for debug"); return ESP_OK; } if (!usb_serial_jtag_is_driver_installed()) { usb_serial_jtag_driver_config_t driver_config = {}; driver_config.rx_buffer_size = static_cast(config_.rx_buffer_size); driver_config.tx_buffer_size = static_cast(config_.tx_buffer_size); esp_err_t err = usb_serial_jtag_driver_install(&driver_config); if (err != ESP_OK) { ESP_LOGE(kTag, "failed to install USB Serial/JTAG driver: %s", esp_err_to_name(err)); return err; } } dali_domain_.addRawFrameSink([this](const DaliRawFrame& frame) { handleRawFrame(frame); }); const BaseType_t ok = xTaskCreate(&GatewayUsbSetupBridge::TaskEntry, "gateway_usb_setup", static_cast(config_.task_stack_size), this, config_.task_priority, &task_handle_); if (ok != pdPASS) { ESP_LOGE(kTag, "failed to create USB setup task"); return ESP_ERR_NO_MEM; } started_ = true; ESP_LOGI(kTag, "USB Serial/JTAG setup bridge started channel=%u", config_.channel_index); return ESP_OK; } void GatewayUsbSetupBridge::TaskEntry(void* arg) { auto* self = static_cast(arg); self->taskLoop(); } void GatewayUsbSetupBridge::taskLoop() { std::vector buffer(std::max(config_.rx_buffer_size, 64)); const TickType_t timeout = pdMS_TO_TICKS(config_.read_timeout_ms); while (true) { const int read_len = usb_serial_jtag_read_bytes(buffer.data(), buffer.size(), timeout); if (read_len > 0) { handleBytes(buffer.data(), static_cast(read_len)); } } } void GatewayUsbSetupBridge::handleBytes(const uint8_t* data, size_t len) { if (data == nullptr || len == 0) { return; } if (len >= kCommandFrameMinLen) { controller_.enqueueCommandFrame(std::vector(data, data + len)); return; } const uint8_t gateway_id = setupGatewayId(); if (!dali_domain_.writeBridgeFrame(gateway_id, data, len)) { ESP_LOGW(kTag, "failed to write USB raw setup frame channel=%u len=%u", config_.channel_index, static_cast(len)); } } void GatewayUsbSetupBridge::handleRawFrame(const DaliRawFrame& frame) { if (!config_.enabled || frame.channel_index != config_.channel_index || frame.data.empty()) { return; } const int written = usb_serial_jtag_write_bytes(frame.data.data(), frame.data.size(), pdMS_TO_TICKS(config_.write_timeout_ms)); if (written < 0 || static_cast(written) != frame.data.size()) { ESP_LOGW(kTag, "failed to forward USB raw setup frame channel=%u len=%u", frame.channel_index, static_cast(frame.data.size())); } } uint8_t GatewayUsbSetupBridge::setupGatewayId() const { for (const auto& channel : dali_domain_.channelInfo()) { if (channel.channel_index == config_.channel_index) { return channel.gateway_id; } } return config_.channel_index; } } // namespace gateway