feat(protocol): 添加传输状态事件管理协议会话状态

添加了新的 proto_transport_state_event 事件类型来跟踪协议传输连接状态,
包括链接断开和就绪状态。为 BLE NUS 和 USB CDC 模块实现了状态机管理,
替换原有的简单布尔标志,提供更精确的连接状态跟踪。

- 添加 proto_transport_state_event 事件定义和实现
- 为 BLE NUS 模块引入业务状态机管理
- 为 USB CDC 模块引入业务状态机管理
- 实现协议模块中的会话状态管理
- 移除 protocol_module_reset_transport_state 函数
- 更新 CMakeLists.txt 包含新事件源文件
This commit is contained in:
2026-04-17 11:13:11 +08:00
parent 2ca02325c1
commit 0cbb16052d
11 changed files with 723 additions and 209 deletions

View File

@@ -17,8 +17,9 @@
#include <zephyr/sys/util.h>
#include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h"
#include "protocol_module.h"
#include "usb_control_event.h"
#include "usb_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -27,10 +28,22 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define USB_CDC_TX_RING_BUF_SIZE 256
#define USB_CDC_RX_CHUNK_SIZE 32
#define USB_CDC_PROTO_RX_BUF_SIZE 128
#define USB_CDC_CONTROL_POLL_INTERVAL K_MSEC(100)
#define USB_CDC_PROTO_RX_FLUSH_DELAY K_MSEC(3)
#define USB_CDC_EXPECTED_BAUDRATE 115200U
enum usb_cdc_business_state {
USB_CDC_BUS_OFFLINE = 0,
USB_CDC_WAIT_DTR,
USB_CDC_SESSION_READY,
};
struct usb_cdc_ctx {
enum module_state lifecycle;
enum usb_cdc_business_state business;
bool usb_active;
size_t proto_rx_len;
};
static const struct device *const cdc_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
static uint8_t rx_ring_buffer[USB_CDC_RX_RING_BUF_SIZE];
@@ -38,15 +51,37 @@ static uint8_t tx_ring_buffer[USB_CDC_TX_RING_BUF_SIZE];
static struct ring_buf rx_ringbuf;
static struct ring_buf tx_ringbuf;
static struct k_work rx_work;
static struct k_work_delayable control_work;
static struct k_work_delayable rx_flush_work;
static bool initialized;
static bool running;
static bool usb_active;
static bool dtr_ready;
static bool rx_enabled;
static uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE];
static size_t proto_rx_len;
static struct usb_cdc_ctx ctx = {
.lifecycle = MODULE_STATE_OFF,
.business = USB_CDC_BUS_OFFLINE,
.usb_active = false,
.proto_rx_len = 0U,
};
static void validate_line_coding(void);
static bool lifecycle_is_ready(void)
{
return ctx.lifecycle == MODULE_STATE_READY;
}
static enum proto_transport_link_state transport_link_state_get(void)
{
return (lifecycle_is_ready() &&
(ctx.business == USB_CDC_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
}
static bool control_poll_needed(enum module_state lifecycle,
enum usb_cdc_business_state business)
{
ARG_UNUSED(lifecycle);
ARG_UNUSED(business);
return false;
}
static void reset_ring_buffers(void)
{
@@ -62,16 +97,98 @@ static void disable_uart_io(void)
{
uart_irq_rx_disable(cdc_dev);
uart_irq_tx_disable(cdc_dev);
rx_enabled = false;
dtr_ready = false;
proto_rx_len = 0U;
ctx.proto_rx_len = 0U;
k_work_cancel_delayable(&rx_flush_work);
reset_ring_buffers();
}
static void state_reconcile(enum module_state old_lifecycle,
enum usb_cdc_business_state old_business)
{
enum proto_transport_link_state old_link =
((old_lifecycle == MODULE_STATE_READY) &&
(old_business == USB_CDC_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
enum proto_transport_link_state new_link = transport_link_state_get();
if ((old_lifecycle == MODULE_STATE_READY) &&
(old_business == USB_CDC_SESSION_READY) &&
(new_link == PROTO_TRANSPORT_LINK_DOWN)) {
disable_uart_io();
}
if ((old_link == PROTO_TRANSPORT_LINK_DOWN) &&
(new_link == PROTO_TRANSPORT_LINK_READY)) {
int err;
validate_line_coding();
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DCD, 1);
if (err) {
LOG_WRN("Failed to set DCD (%d)", err);
}
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DSR, 1);
if (err) {
LOG_WRN("Failed to set DSR (%d)", err);
}
uart_irq_rx_enable(cdc_dev);
}
if (old_link != new_link) {
submit_proto_transport_state_event(PROTO_TRANSPORT_USB_CDC,
new_link);
}
}
static void lifecycle_set(enum module_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum usb_cdc_business_state old_business = ctx.business;
if (ctx.lifecycle == new_state) {
return;
}
ctx.lifecycle = new_state;
state_reconcile(old_lifecycle, old_business);
module_set_state(new_state);
}
static void business_state_set(enum usb_cdc_business_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum usb_cdc_business_state old_business = ctx.business;
if (ctx.business == new_state) {
return;
}
ctx.business = new_state;
state_reconcile(old_lifecycle, old_business);
}
static void business_state_sync_from_usb(void)
{
if (!ctx.usb_active) {
business_state_set(USB_CDC_BUS_OFFLINE);
return;
}
if (!lifecycle_is_ready()) {
return;
}
if (ctx.business == USB_CDC_BUS_OFFLINE) {
business_state_set(USB_CDC_WAIT_DTR);
}
}
static void kick_tx(void)
{
if (!running || !usb_active || !dtr_ready) {
if (transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) {
return;
}
@@ -133,15 +250,15 @@ static void rx_work_handler(struct k_work *work)
return;
}
if ((proto_rx_len + len) > sizeof(proto_rx_buf)) {
if ((ctx.proto_rx_len + len) > sizeof(proto_rx_buf)) {
LOG_WRN("Drop oversized CDC protobuf message len:%u",
(uint32_t)(proto_rx_len + len));
proto_rx_len = 0U;
(uint32_t)(ctx.proto_rx_len + len));
ctx.proto_rx_len = 0U;
}
if (len > 0U) {
memcpy(&proto_rx_buf[proto_rx_len], buffer, len);
proto_rx_len += len;
memcpy(&proto_rx_buf[ctx.proto_rx_len], buffer, len);
ctx.proto_rx_len += len;
k_work_reschedule(&rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY);
}
}
@@ -151,59 +268,14 @@ static void rx_flush_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (!running || !usb_active || !dtr_ready || (proto_rx_len == 0U)) {
if ((transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) ||
(ctx.proto_rx_len == 0U)) {
return;
}
(void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf, proto_rx_len);
proto_rx_len = 0U;
}
static void control_work_handler(struct k_work *work)
{
uint32_t dtr = 0U;
int err;
ARG_UNUSED(work);
if (!running || !usb_active) {
return;
}
err = uart_line_ctrl_get(cdc_dev, UART_LINE_CTRL_DTR, &dtr);
if (err) {
LOG_WRN("Failed to get CDC DTR (%d)", err);
goto reschedule;
}
if (dtr && !dtr_ready) {
dtr_ready = true;
LOG_INF("CDC DTR set");
validate_line_coding();
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DCD, 1);
if (err) {
LOG_WRN("Failed to set DCD (%d)", err);
}
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DSR, 1);
if (err) {
LOG_WRN("Failed to set DSR (%d)", err);
}
if (!rx_enabled) {
uart_irq_rx_enable(cdc_dev);
rx_enabled = true;
}
kick_tx();
} else if (!dtr && dtr_ready) {
LOG_INF("CDC DTR cleared");
disable_uart_io();
}
reschedule:
k_work_reschedule(&control_work, USB_CDC_CONTROL_POLL_INTERVAL);
(void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf,
ctx.proto_rx_len);
ctx.proto_rx_len = 0U;
}
static void cdc_interrupt_handler(const struct device *dev, void *user_data)
@@ -229,7 +301,8 @@ static void cdc_interrupt_handler(const struct device *dev, void *user_data)
irq_unlock(key);
if (written < (uint32_t)recv_len) {
LOG_WRN("Drop %d CDC RX bytes", recv_len - (int)written);
LOG_WRN("Drop %d CDC RX bytes",
recv_len - (int)written);
}
k_work_submit(&rx_work);
@@ -271,61 +344,97 @@ static int module_init(void)
reset_ring_buffers();
k_work_init(&rx_work, rx_work_handler);
k_work_init_delayable(&control_work, control_work_handler);
k_work_init_delayable(&rx_flush_work, rx_flush_work_handler);
uart_irq_callback_set(cdc_dev, cdc_interrupt_handler);
proto_rx_len = 0U;
ctx = (struct usb_cdc_ctx) {
.lifecycle = MODULE_STATE_OFF,
.business = USB_CDC_BUS_OFFLINE,
.usb_active = false,
.proto_rx_len = 0U,
};
return 0;
}
static int module_start(void)
static void module_start(void)
{
if (running) {
return 0;
}
running = true;
if (usb_active) {
k_work_reschedule(&control_work, K_NO_WAIT);
}
return 0;
lifecycle_set(MODULE_STATE_READY);
business_state_sync_from_usb();
}
static void module_pause(void)
{
if (!running) {
return;
}
k_work_cancel_delayable(&control_work);
k_work_cancel_delayable(&rx_flush_work);
disable_uart_io();
running = false;
business_state_set(USB_CDC_BUS_OFFLINE);
lifecycle_set(MODULE_STATE_STANDBY);
}
static bool handle_usb_state_event(const struct usb_state_event *event)
{
bool new_usb_active = (event->state == USB_STATE_ACTIVE);
if (new_usb_active == usb_active) {
if (new_usb_active == ctx.usb_active) {
return false;
}
usb_active = new_usb_active;
if (!usb_active) {
k_work_cancel_delayable(&control_work);
k_work_cancel_delayable(&rx_flush_work);
protocol_module_reset_transport_state(PROTO_TRANSPORT_USB_CDC);
disable_uart_io();
} else if (running) {
k_work_reschedule(&control_work, K_NO_WAIT);
}
ctx.usb_active = new_usb_active;
business_state_sync_from_usb();
return false;
}
static bool handle_usb_control_event(const struct usb_control_event *event)
{
if (event->dev != cdc_dev) {
return false;
}
switch (event->type) {
case USB_CONTROL_EVENT_CDC_LINE_STATE:
if (!ctx.usb_active || !lifecycle_is_ready()) {
return false;
}
if (event->data.cdc_line_state.dtr) {
LOG_INF("CDC DTR set");
business_state_set(USB_CDC_SESSION_READY);
kick_tx();
} else {
LOG_INF("CDC DTR cleared");
business_state_set(USB_CDC_WAIT_DTR);
}
return false;
case USB_CONTROL_EVENT_CDC_LINE_CODING:
if (event->data.cdc_line_coding.baudrate != 0U) {
LOG_INF("CDC baudrate %u",
event->data.cdc_line_coding.baudrate);
if (event->data.cdc_line_coding.baudrate !=
USB_CDC_EXPECTED_BAUDRATE) {
LOG_WRN("Expected CDC baudrate %u, got %u",
USB_CDC_EXPECTED_BAUDRATE,
event->data.cdc_line_coding.baudrate);
}
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
if ((event->data.cdc_line_coding.data_bits != 0U) ||
(event->data.cdc_line_coding.stop_bits != 0U) ||
(event->data.cdc_line_coding.parity != 0U) ||
(event->data.cdc_line_coding.flow_ctrl != 0U)) {
LOG_INF("CDC line coding data:%u stop:%u parity:%u flow:%u",
event->data.cdc_line_coding.data_bits,
event->data.cdc_line_coding.stop_bits,
event->data.cdc_line_coding.parity,
event->data.cdc_line_coding.flow_ctrl);
}
#endif
return false;
default:
return false;
}
}
static bool handle_proto_tx_event(const struct proto_tx_event *event)
{
uint32_t written;
@@ -335,7 +444,7 @@ static bool handle_proto_tx_event(const struct proto_tx_event *event)
return false;
}
if (!running || !usb_active || !dtr_ready) {
if (transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) {
return false;
}
@@ -365,51 +474,41 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_proto_tx_event(cast_proto_tx_event(aeh));
}
if (is_usb_control_event(aeh)) {
return handle_usb_control_event(cast_usb_control_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
const struct module_state_event *event =
cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
int err;
if (ctx.lifecycle == MODULE_STATE_OFF) {
int err = module_init();
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
lifecycle_set(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
module_start();
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (initialized) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_start();
}
return false;
@@ -421,6 +520,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, proto_tx_event);
APP_EVENT_SUBSCRIBE(MODULE, usb_control_event);
APP_EVENT_SUBSCRIBE(MODULE, usb_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);