Refactor global state management for startup commands and USB handover (no Race-Condition); enhance SerialManager shutdown process and buffer handling in UVCStream.

This commit is contained in:
PhosphorosVR
2025-08-23 19:12:53 +02:00
parent e1838b4ca4
commit 3f96e468f0
7 changed files with 82 additions and 14 deletions

View File

@@ -4,15 +4,15 @@
// Forward declarations
extern void start_video_streaming(void *arg);
bool startupCommandReceived = false;
static bool s_startupCommandReceived = false;
bool getStartupCommandReceived()
{
return startupCommandReceived;
return s_startupCommandReceived;
}
void setStartupCommandReceived(bool startupCommandReceived)
{
startupCommandReceived = startupCommandReceived;
s_startupCommandReceived = startupCommandReceived;
}
static TaskHandle_t *g_serial_manager_handle = nullptr;
@@ -27,15 +27,15 @@ void setSerialManagerHandle(TaskHandle_t *serialManagerHandle)
}
// Global pause state
bool startupPaused = false;
static bool s_startupPaused = false;
bool getStartupPaused()
{
return startupPaused;
return s_startupPaused;
}
void setStartupPaused(bool startupPaused)
{
startupPaused = startupPaused;
s_startupPaused = startupPaused;
}
// Function to manually activate streaming
@@ -47,4 +47,9 @@ void activateStreaming(bool disableSetup)
void *serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr;
start_video_streaming(serialTaskHandle);
}
}
// USB handover state
static bool s_usbHandoverDone = false;
bool getUsbHandoverDone() { return s_usbHandoverDone; }
void setUsbHandoverDone(bool done) { s_usbHandoverDone = done; }

View File

@@ -21,4 +21,8 @@ void setStartupCommandReceived(bool startupCommandReceived);
bool getStartupPaused();
void setStartupPaused(bool startupPaused);
// Tracks whether USB handover from usb_serial_jtag to TinyUSB was performed
bool getUsbHandoverDone();
void setUsbHandoverDone(bool done);
#endif

View File

@@ -24,13 +24,36 @@ void SerialManager::try_receive()
int current_position = 0;
int len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20);
// If driver is uninstalled or an error occurs, abort read gracefully
if (len < 0)
{
return;
}
// since we've got something on the serial port
// we gotta keep reading until we've got the whole message
while (len)
while (len > 0)
{
memcpy(this->data + current_position, this->temp_data, len);
// Prevent buffer overflow
if (current_position + len >= BUF_SIZE)
{
int copy_len = BUF_SIZE - 1 - current_position;
if (copy_len > 0)
{
memcpy(this->data + current_position, this->temp_data, copy_len);
current_position += copy_len;
}
// Drop the rest of the input to avoid overflow
break;
}
memcpy(this->data + current_position, this->temp_data, (size_t)len);
current_position += len;
len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20);
if (len < 0)
{
// Driver likely uninstalled during handover; stop processing this cycle
break;
}
}
if (current_position)
@@ -42,9 +65,10 @@ void SerialManager::try_receive()
// Notify main that a command was received during startup
notify_startup_command_received();
const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
const auto resultMessage = result.getResult();
usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20);
const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
const auto resultMessage = result.getResult();
int written = usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20);
(void)written; // ignore errors if driver already uninstalled
}
}
@@ -79,6 +103,7 @@ void SerialManager::send_heartbeat()
sprintf(heartbeat, "{\"heartbeat\":\"openiris_setup_mode\",\"serial\":\"%s\"}\n", serial_number);
usb_serial_jtag_write_bytes(heartbeat, strlen(heartbeat), 1000 / 20);
// Ignore return value; if the driver was uninstalled, this is a no-op
}
bool SerialManager::should_send_heartbeat()
@@ -123,4 +148,19 @@ void HandleSerialManagerTask(void *pvParameters)
lastHeartbeat = currentTime;
}
}
}
void SerialManager::shutdown()
{
// Stop heartbeats; timer will be deleted by main if needed.
// Uninstall the USB Serial JTAG driver to free the internal USB for TinyUSB.
esp_err_t err = usb_serial_jtag_driver_uninstall();
if (err == ESP_OK)
{
ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled");
}
else if (err != ESP_ERR_INVALID_STATE)
{
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
}
}

View File

@@ -27,6 +27,7 @@ public:
void send_heartbeat();
bool should_send_heartbeat();
void notify_startup_command_received();
void shutdown();
private:
std::shared_ptr<CommandManager> commandManager;

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "UVCStream/UVCStream.cpp"
INCLUDE_DIRS "UVCStream"
REQUIRES esp_timer esp32-camera StateManager usb_device_uvc CameraManager
REQUIRES esp_timer esp32-camera StateManager usb_device_uvc CameraManager Helpers
)

View File

@@ -1,5 +1,9 @@
#include "UVCStream.hpp"
#include <cstdio> // for snprintf
#include "driver/usb_serial_jtag.h" // for clean handover from COM to TinyUSB
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// no deps on main globals here; handover is performed in main before calling setup when needed
static const char *UVC_STREAM_TAG = "[UVC DEVICE]";
@@ -33,7 +37,6 @@ extern "C" {
static esp_err_t UVCStreamHelpers::camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx)
{
auto *mgr = static_cast<UVCStreamManager *>(cb_ctx);
ESP_LOGI(UVC_STREAM_TAG, "Camera Start");
ESP_LOGI(UVC_STREAM_TAG, "Format: %d, width: %d, height: %d, rate: %d", format, width, height, rate);
framesize_t frame_size = FRAMESIZE_QVGA;
@@ -79,6 +82,7 @@ static void UVCStreamHelpers::camera_stop_cb(void *cb_ctx)
static uvc_fb_t *UVCStreamHelpers::camera_fb_get_cb(void *cb_ctx)
{
auto *mgr = static_cast<UVCStreamManager *>(cb_ctx);
(void)mgr;
s_fb.cam_fb_p = esp_camera_fb_get();
if (!s_fb.cam_fb_p)