Add Phase 1 UART service foundation

Add versioned NVS-backed configuration, buffered UART1 I/O, modem
monitoring, counters, and serial console controls. Coordinate UART1
ownership with Phase 0 diagnostics and document loopback verification.
This commit is contained in:
2026-08-22 23:23:13 +02:00
parent 126314a277
commit 535c27350d
13 changed files with 1961 additions and 30 deletions
+96 -23
View File
@@ -17,6 +17,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "rs232_port_owner.h"
#include "status_led.h"
#define STATIC_SETTLE_TIME_MS 20
@@ -59,6 +60,7 @@ static int s_rts_level = 1;
static bool s_transceiver_enabled = true;
static bool s_initialized;
static bool s_uart_active;
static bool s_cleanup_fault;
typedef struct {
const char *name;
@@ -253,7 +255,7 @@ static const serial_format_t *find_serial_format(const char *name)
return NULL;
}
static int command_status(int argc, char **argv)
static int execute_status(int argc, char **argv)
{
(void)argc;
(void)argv;
@@ -278,7 +280,7 @@ static int command_status(int argc, char **argv)
return 0;
}
static int command_transceiver(int argc, char **argv)
static int execute_transceiver(int argc, char **argv)
{
if (argc != 2 || (strcmp(argv[1], "enable") != 0 && strcmp(argv[1], "disable") != 0)) {
printf("Usage: transceiver <enable|disable>\n");
@@ -300,7 +302,7 @@ static int command_transceiver(int argc, char **argv)
return 0;
}
static int command_drivers(int argc, char **argv)
static int execute_drivers(int argc, char **argv)
{
int tx;
int dtr;
@@ -402,21 +404,21 @@ static esp_err_t run_static_loopback(loopback_configuration_t configuration)
return all_passed ? ESP_OK : ESP_FAIL;
}
static int command_loopback_a(int argc, char **argv)
static int execute_loopback_a(int argc, char **argv)
{
(void)argc;
(void)argv;
return run_static_loopback(LOOPBACK_CONFIGURATION_A) == ESP_OK ? 0 : 1;
}
static int command_loopback_b(int argc, char **argv)
static int execute_loopback_b(int argc, char **argv)
{
(void)argc;
(void)argv;
return run_static_loopback(LOOPBACK_CONFIGURATION_B) == ESP_OK ? 0 : 1;
}
static int command_valid_test(int argc, char **argv)
static int execute_valid_test(int argc, char **argv)
{
(void)argc;
(void)argv;
@@ -799,24 +801,32 @@ cleanup:;
}
}
bool deletion_failed = false;
if (driver_installed) {
esp_err_t delete_error = uart_driver_delete(RS232_UART_PORT);
if (delete_error != ESP_OK) {
printf("Could not delete UART1 driver: %s\n", esp_err_to_name(delete_error));
deletion_failed = true;
s_cleanup_fault = true;
if (result == ESP_OK) {
result = delete_error;
}
}
}
/* GPIO17/18 return to static idle mode, then the transceiver is re-enabled. */
s_transceiver_enabled = true;
esp_err_t restore_error = configure_static_gpio(true);
if (restore_error != ESP_OK) {
printf("Could not restore static GPIO mode: %s\n", esp_err_to_name(restore_error));
if (result == ESP_OK) {
result = restore_error;
if (!deletion_failed) {
/* GPIO17/18 return to static idle mode, then the transceiver is re-enabled. */
s_transceiver_enabled = true;
esp_err_t restore_error = configure_static_gpio(true);
if (restore_error != ESP_OK) {
printf("Could not restore static GPIO mode: %s\n", esp_err_to_name(restore_error));
s_cleanup_fault = true;
if (result == ESP_OK) {
result = restore_error;
}
}
} else {
s_transceiver_enabled = false;
}
esp_err_t led_error = status_led_set(result == ESP_OK ? STATUS_LED_PASS : STATUS_LED_FAIL);
@@ -829,7 +839,7 @@ cleanup:;
return result;
}
static int command_uart_loopback(int argc, char **argv)
static int execute_uart_loopback(int argc, char **argv)
{
long baud_rate;
long payload_size = 256;
@@ -859,7 +869,7 @@ static int command_uart_loopback(int argc, char **argv)
return run_uart_loopback((int)baud_rate, format, (size_t)payload_size) == ESP_OK ? 0 : 1;
}
static int command_uart_suite(int argc, char **argv)
static int execute_uart_suite(int argc, char **argv)
{
(void)argc;
(void)argv;
@@ -902,10 +912,13 @@ static esp_err_t finish_flow_test(
}
/* Stop the traffic source before removing the receiver's backpressure. */
bool deletion_failed = false;
if (generator_uart_installed) {
esp_err_t delete_error = uart_driver_delete(RS232_TEST_GENERATOR_UART_PORT);
if (delete_error != ESP_OK) {
printf("Could not delete UART2 generator: %s\n", esp_err_to_name(delete_error));
deletion_failed = true;
s_cleanup_fault = true;
if (result == ESP_OK) {
result = delete_error;
}
@@ -915,19 +928,26 @@ static esp_err_t finish_flow_test(
esp_err_t delete_error = uart_driver_delete(RS232_UART_PORT);
if (delete_error != ESP_OK) {
printf("Could not delete UART1 driver: %s\n", esp_err_to_name(delete_error));
deletion_failed = true;
s_cleanup_fault = true;
if (result == ESP_OK) {
result = delete_error;
}
}
}
s_transceiver_enabled = true;
esp_err_t restore_error = configure_static_gpio(true);
if (restore_error != ESP_OK) {
printf("Could not restore static GPIO mode: %s\n", esp_err_to_name(restore_error));
if (result == ESP_OK) {
result = restore_error;
if (!deletion_failed) {
s_transceiver_enabled = true;
esp_err_t restore_error = configure_static_gpio(true);
if (restore_error != ESP_OK) {
printf("Could not restore static GPIO mode: %s\n", esp_err_to_name(restore_error));
s_cleanup_fault = true;
if (result == ESP_OK) {
result = restore_error;
}
}
} else {
s_transceiver_enabled = false;
}
esp_err_t led_error = status_led_set(result == ESP_OK ? STATUS_LED_PASS : STATUS_LED_FAIL);
@@ -1164,7 +1184,7 @@ cleanup:;
return finish_flow_test(uart1_installed, false, result);
}
static int command_cts_flow_test(int argc, char **argv)
static int execute_cts_flow_test(int argc, char **argv)
{
(void)argc;
(void)argv;
@@ -1493,7 +1513,7 @@ cleanup:;
return result;
}
static int command_rts_flow_test(int argc, char **argv)
static int execute_rts_flow_test(int argc, char **argv)
{
(void)argc;
(void)argv;
@@ -1502,6 +1522,59 @@ static int command_rts_flow_test(int argc, char **argv)
return run_rts_flow_test() == ESP_OK ? 0 : 1;
}
typedef int (*hardware_test_command_t)(int argc, char **argv);
static int run_owned_hardware_test(
hardware_test_command_t command,
int argc,
char **argv)
{
esp_err_t claim_error = rs232_port_claim(RS232_PORT_OWNER_PHASE0);
if (claim_error != ESP_OK) {
rs232_port_owner_t owner = rs232_port_get_owner();
printf("RS-232 port is owned by %s. Stop the serial service or reboot after a fault.\n",
rs232_port_owner_to_string(owner));
return 1;
}
s_cleanup_fault = false;
int result = command(argc, argv);
/* Never publish the port as idle after ambiguous cleanup or a surviving driver. */
if (s_cleanup_fault ||
uart_is_driver_installed(RS232_UART_PORT) ||
uart_is_driver_installed(RS232_TEST_GENERATOR_UART_PORT)) {
drive_transceiver_enabled(false);
rs232_port_mark_fault(RS232_PORT_OWNER_PHASE0);
printf("A diagnostic UART driver could not be removed; MAX3243 is disabled. Reboot required.\n");
return 1;
}
if (rs232_port_release(RS232_PORT_OWNER_PHASE0) != ESP_OK) {
rs232_port_mark_fault(RS232_PORT_OWNER_PHASE0);
printf("Could not release diagnostic port ownership; reboot required.\n");
return 1;
}
return result;
}
#define DEFINE_OWNED_COMMAND(name) \
static int command_##name(int argc, char **argv) \
{ \
return run_owned_hardware_test(execute_##name, argc, argv); \
}
DEFINE_OWNED_COMMAND(status)
DEFINE_OWNED_COMMAND(transceiver)
DEFINE_OWNED_COMMAND(drivers)
DEFINE_OWNED_COMMAND(loopback_a)
DEFINE_OWNED_COMMAND(loopback_b)
DEFINE_OWNED_COMMAND(valid_test)
DEFINE_OWNED_COMMAND(uart_loopback)
DEFINE_OWNED_COMMAND(uart_suite)
DEFINE_OWNED_COMMAND(cts_flow_test)
DEFINE_OWNED_COMMAND(rts_flow_test)
esp_err_t rs232_hw_test_init(void)
{
s_transceiver_enabled = true;