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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user