Compare commits

...

2 Commits

Author SHA1 Message Date
8bfb8b540c feat(bt): 添加蓝牙外设首选超时配置
新增 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT 配置项,
设置为 400 以优化蓝牙连接超时参数
2026-04-17 16:17:13 +08:00
0cbb16052d 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 包含新事件源文件
2026-04-17 11:55:03 +08:00
12 changed files with 724 additions and 209 deletions

View File

@@ -55,9 +55,11 @@ target_sources(app PRIVATE
src/events/keyboard_hid_report_event.c src/events/keyboard_hid_report_event.c
src/events/mode_switch_event.c src/events/mode_switch_event.c
src/events/proto_rx_event.c src/events/proto_rx_event.c
src/events/proto_transport_state_event.c
src/events/proto_tx_event.c src/events/proto_tx_event.c
src/events/set_protocol_event.c src/events/set_protocol_event.c
src/events/theme_rgb_update_event.c src/events/theme_rgb_update_event.c
src/events/time_sync_event.c src/events/time_sync_event.c
src/events/usb_control_event.c
src/events/usb_state_event.c src/events/usb_state_event.c
) )

View File

@@ -11,6 +11,11 @@ enum proto_transport {
PROTO_TRANSPORT_COUNT, PROTO_TRANSPORT_COUNT,
}; };
enum proto_transport_link_state {
PROTO_TRANSPORT_LINK_DOWN = 0,
PROTO_TRANSPORT_LINK_READY,
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -0,0 +1,36 @@
#ifndef BLINKY_PROTO_TRANSPORT_STATE_EVENT_H_
#define BLINKY_PROTO_TRANSPORT_STATE_EVENT_H_
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#include "proto_common.h"
#ifdef __cplusplus
extern "C" {
#endif
struct proto_transport_state_event {
struct app_event_header header;
enum proto_transport transport;
enum proto_transport_link_state state;
};
APP_EVENT_TYPE_DECLARE(proto_transport_state_event);
static inline void submit_proto_transport_state_event(
enum proto_transport transport, enum proto_transport_link_state state)
{
struct proto_transport_state_event *event =
new_proto_transport_state_event();
event->transport = transport;
event->state = state;
APP_EVENT_SUBMIT(event);
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_PROTO_TRANSPORT_STATE_EVENT_H_ */

View File

@@ -0,0 +1,71 @@
#ifndef BLINKY_USB_CONTROL_EVENT_H_
#define BLINKY_USB_CONTROL_EVENT_H_
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#include <zephyr/device.h>
#ifdef __cplusplus
extern "C" {
#endif
enum usb_control_event_type {
USB_CONTROL_EVENT_CDC_LINE_STATE = 0,
USB_CONTROL_EVENT_CDC_LINE_CODING,
};
struct usb_control_event {
struct app_event_header header;
enum usb_control_event_type type;
const struct device *dev;
union {
struct {
bool dtr;
} cdc_line_state;
struct {
uint32_t baudrate;
uint8_t data_bits;
uint8_t stop_bits;
uint8_t parity;
uint8_t flow_ctrl;
} cdc_line_coding;
} data;
};
APP_EVENT_TYPE_DECLARE(usb_control_event);
static inline void submit_usb_control_cdc_line_state_event(
const struct device *dev, bool dtr)
{
struct usb_control_event *event = new_usb_control_event();
event->type = USB_CONTROL_EVENT_CDC_LINE_STATE;
event->dev = dev;
event->data.cdc_line_state.dtr = dtr;
APP_EVENT_SUBMIT(event);
}
static inline void submit_usb_control_cdc_line_coding_event(
const struct device *dev, uint32_t baudrate, uint8_t data_bits,
uint8_t stop_bits, uint8_t parity, uint8_t flow_ctrl)
{
struct usb_control_event *event = new_usb_control_event();
event->type = USB_CONTROL_EVENT_CDC_LINE_CODING;
event->dev = dev;
event->data.cdc_line_coding.baudrate = baudrate;
event->data.cdc_line_coding.data_bits = data_bits;
event->data.cdc_line_coding.stop_bits = stop_bits;
event->data.cdc_line_coding.parity = parity;
event->data.cdc_line_coding.flow_ctrl = flow_ctrl;
APP_EVENT_SUBMIT(event);
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_USB_CONTROL_EVENT_H_ */

View File

@@ -18,8 +18,6 @@ int protocol_module_process_message(enum proto_transport transport,
size_t rsp_payload_buf_size, size_t rsp_payload_buf_size,
size_t *rsp_payload_len); size_t *rsp_payload_len);
void protocol_module_reset_transport_state(enum proto_transport transport);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -55,6 +55,7 @@ CONFIG_BT_ATT_TX_COUNT=5
CONFIG_BT_L2CAP_TX_MTU=65 CONFIG_BT_L2CAP_TX_MTU=65
CONFIG_BT_BUF_ACL_RX_SIZE=69 CONFIG_BT_BUF_ACL_RX_SIZE=69
CONFIG_BT_BUF_ACL_TX_SIZE=69 CONFIG_BT_BUF_ACL_TX_SIZE=69
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
CONFIG_BT_CONN_CTX=y CONFIG_BT_CONN_CTX=y
CONFIG_BT_DEVICE_NAME="WH Mini Keyboard" CONFIG_BT_DEVICE_NAME="WH Mini Keyboard"
CONFIG_BT_DEVICE_APPEARANCE=961 CONFIG_BT_DEVICE_APPEARANCE=961

View File

@@ -14,30 +14,122 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include "proto_rx_event.h" #include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h" #include "proto_tx_event.h"
#include "protocol_module.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static struct bt_conn *active_conn; enum ble_nus_business_state {
static bool initialized; BLE_NUS_STACK_OFFLINE = 0,
static bool running; BLE_NUS_IDLE,
static bool ble_ready; BLE_NUS_WAIT_NOTIFY,
static bool tx_notify_enabled; BLE_NUS_SESSION_READY,
};
static void notif_enabled(bool enabled, void *ctx) struct ble_nus_ctx {
enum module_state lifecycle;
enum ble_nus_business_state business;
struct bt_conn *active_conn;
};
static struct ble_nus_ctx ctx = {
.lifecycle = MODULE_STATE_OFF,
.business = BLE_NUS_STACK_OFFLINE,
.active_conn = NULL,
};
static bool lifecycle_is_ready(void)
{ {
ARG_UNUSED(ctx); return ctx.lifecycle == MODULE_STATE_READY;
tx_notify_enabled = enabled;
LOG_INF("BLE NUS TX notify %s", enabled ? "enabled" : "disabled");
} }
static void received(struct bt_conn *conn, const void *data, uint16_t len, void *ctx) static enum proto_transport_link_state transport_link_state_get(void)
{ {
ARG_UNUSED(ctx); return (lifecycle_is_ready() &&
(ctx.business == BLE_NUS_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
}
if (!running || !ble_ready || (conn != active_conn)) { static void state_reconcile(enum module_state old_lifecycle,
enum ble_nus_business_state old_business)
{
enum proto_transport_link_state old_link =
((old_lifecycle == MODULE_STATE_READY) &&
(old_business == BLE_NUS_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
enum proto_transport_link_state new_link = transport_link_state_get();
if (old_link != new_link) {
submit_proto_transport_state_event(PROTO_TRANSPORT_BLE_NUS,
new_link);
}
}
static void lifecycle_set(enum module_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum ble_nus_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 ble_nus_business_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum ble_nus_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_set_stack_ready(void)
{
if (ctx.business == BLE_NUS_STACK_OFFLINE) {
business_state_set(BLE_NUS_IDLE);
}
}
static void business_state_set_from_notify(bool enabled)
{
if (ctx.business == BLE_NUS_STACK_OFFLINE) {
return;
}
if (ctx.active_conn == NULL) {
return;
}
business_state_set(enabled ? BLE_NUS_SESSION_READY :
BLE_NUS_WAIT_NOTIFY);
}
static void notif_enabled(bool enabled, void *ctx_ptr)
{
ARG_UNUSED(ctx_ptr);
LOG_INF("BLE NUS TX notify %s", enabled ? "enabled" : "disabled");
business_state_set_from_notify(enabled);
}
static void received(struct bt_conn *conn, const void *data, uint16_t len,
void *ctx_ptr)
{
ARG_UNUSED(ctx_ptr);
if (!lifecycle_is_ready() || (ctx.business == BLE_NUS_STACK_OFFLINE) ||
(conn != ctx.active_conn)) {
return; return;
} }
@@ -51,9 +143,11 @@ static struct bt_nus_cb nus_listener = {
static void reset_connection_state(void) static void reset_connection_state(void)
{ {
active_conn = NULL; ctx.active_conn = NULL;
tx_notify_enabled = false;
protocol_module_reset_transport_state(PROTO_TRANSPORT_BLE_NUS); if (ctx.business != BLE_NUS_STACK_OFFLINE) {
business_state_set(BLE_NUS_IDLE);
}
} }
static int module_init(void) static int module_init(void)
@@ -66,45 +160,41 @@ static int module_init(void)
return err; return err;
} }
reset_connection_state(); ctx = (struct ble_nus_ctx) {
.lifecycle = MODULE_STATE_OFF,
.business = BLE_NUS_STACK_OFFLINE,
.active_conn = NULL,
};
return 0; return 0;
} }
static int module_start(void) static void module_start(void)
{ {
if (running) { lifecycle_set(MODULE_STATE_READY);
return 0;
}
running = true;
return 0;
} }
static void module_pause(void) static void module_pause(void)
{ {
if (!running) { lifecycle_set(MODULE_STATE_STANDBY);
return;
}
running = false;
tx_notify_enabled = false;
} }
static bool handle_ble_peer_event(const struct ble_peer_event *event) static bool handle_ble_peer_event(const struct ble_peer_event *event)
{ {
switch (event->state) { switch (event->state) {
case PEER_STATE_CONNECTED: case PEER_STATE_CONNECTED:
if (active_conn != NULL) { if (ctx.active_conn != NULL) {
return false; return false;
} }
active_conn = event->id; ctx.active_conn = event->id;
tx_notify_enabled = false; if (ctx.business != BLE_NUS_STACK_OFFLINE) {
protocol_module_reset_transport_state(PROTO_TRANSPORT_BLE_NUS); business_state_set(BLE_NUS_WAIT_NOTIFY);
}
return false; return false;
case PEER_STATE_DISCONNECTED: case PEER_STATE_DISCONNECTED:
if (active_conn != event->id) { if (ctx.active_conn != event->id) {
return false; return false;
} }
@@ -124,11 +214,13 @@ static bool handle_proto_tx_event(const struct proto_tx_event *event)
return false; return false;
} }
if (!running || !ble_ready || (active_conn == NULL) || !tx_notify_enabled) { if ((transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) ||
(ctx.active_conn == NULL)) {
return false; return false;
} }
err = bt_nus_send(active_conn, event->dyndata.data, (uint16_t)event->dyndata.size); err = bt_nus_send(ctx.active_conn, event->dyndata.data,
(uint16_t)event->dyndata.size);
if (err) { if (err) {
LOG_WRN("bt_nus_send failed (%d)", err); LOG_WRN("bt_nus_send failed (%d)", err);
} }
@@ -147,32 +239,29 @@ static bool app_event_handler(const struct app_event_header *aeh)
} }
if (is_module_state_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 =
int err; cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (!initialized) { if (ctx.lifecycle == MODULE_STATE_OFF) {
err = module_init(); int err = module_init();
if (err) { if (err) {
module_set_state(MODULE_STATE_ERROR); lifecycle_set(MODULE_STATE_ERROR);
return false; return false;
} }
initialized = true;
} }
err = module_start(); module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
}
return false; return false;
} }
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) { if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
ble_ready = true; business_state_set_stack_ready();
if (running) {
module_set_state(MODULE_STATE_READY); if (lifecycle_is_ready()) {
lifecycle_set(MODULE_STATE_READY);
} }
return false; return false;
@@ -182,23 +271,16 @@ static bool app_event_handler(const struct app_event_header *aeh)
} }
if (is_power_down_event(aeh)) { if (is_power_down_event(aeh)) {
if (initialized) { if (ctx.lifecycle != MODULE_STATE_OFF) {
module_pause(); module_pause();
module_set_state(MODULE_STATE_STANDBY);
} }
return false; return false;
} }
if (is_wake_up_event(aeh)) { if (is_wake_up_event(aeh)) {
if (initialized) { if (ctx.lifecycle != MODULE_STATE_OFF) {
int err = module_start(); module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else if (ble_ready) {
module_set_state(MODULE_STATE_READY);
}
} }
return false; return false;

View File

@@ -0,0 +1,56 @@
#include "proto_transport_state_event.h"
static const char *transport_name(enum proto_transport transport)
{
switch (transport) {
case PROTO_TRANSPORT_USB_CDC:
return "usb_cdc";
case PROTO_TRANSPORT_BLE_NUS:
return "ble_nus";
default:
return "?";
}
}
static const char *state_name(enum proto_transport_link_state state)
{
switch (state) {
case PROTO_TRANSPORT_LINK_DOWN:
return "down";
case PROTO_TRANSPORT_LINK_READY:
return "ready";
default:
return "?";
}
}
static void log_proto_transport_state_event(const struct app_event_header *aeh)
{
const struct proto_transport_state_event *event =
cast_proto_transport_state_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "transport:%s state:%s",
transport_name(event->transport),
state_name(event->state));
}
static void profile_proto_transport_state_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct proto_transport_state_event *event =
cast_proto_transport_state_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->transport);
nrf_profiler_log_encode_uint8(buf, event->state);
}
APP_EVENT_INFO_DEFINE(proto_transport_state_event,
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
ENCODE("transport", "state"),
profile_proto_transport_state_event);
APP_EVENT_TYPE_DEFINE(proto_transport_state_event,
log_proto_transport_state_event,
&proto_transport_state_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -0,0 +1,82 @@
#include "usb_control_event.h"
static const char *control_event_name(enum usb_control_event_type type)
{
switch (type) {
case USB_CONTROL_EVENT_CDC_LINE_STATE:
return "cdc_line_state";
case USB_CONTROL_EVENT_CDC_LINE_CODING:
return "cdc_line_coding";
default:
return "?";
}
}
static void log_usb_control_event(const struct app_event_header *aeh)
{
const struct usb_control_event *event = cast_usb_control_event(aeh);
switch (event->type) {
case USB_CONTROL_EVENT_CDC_LINE_STATE:
APP_EVENT_MANAGER_LOG(aeh, "type:%s dtr:%u",
control_event_name(event->type),
event->data.cdc_line_state.dtr);
break;
case USB_CONTROL_EVENT_CDC_LINE_CODING:
APP_EVENT_MANAGER_LOG(aeh,
"type:%s baud:%u data:%u stop:%u parity:%u flow:%u",
control_event_name(event->type),
event->data.cdc_line_coding.baudrate,
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);
break;
default:
APP_EVENT_MANAGER_LOG(aeh, "type:%s",
control_event_name(event->type));
break;
}
}
static void profile_usb_control_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct usb_control_event *event = cast_usb_control_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->type);
switch (event->type) {
case USB_CONTROL_EVENT_CDC_LINE_STATE:
nrf_profiler_log_encode_uint8(buf, event->data.cdc_line_state.dtr);
break;
case USB_CONTROL_EVENT_CDC_LINE_CODING:
nrf_profiler_log_encode_uint32(buf, event->data.cdc_line_coding.baudrate);
nrf_profiler_log_encode_uint8(buf, event->data.cdc_line_coding.data_bits);
nrf_profiler_log_encode_uint8(buf, event->data.cdc_line_coding.stop_bits);
nrf_profiler_log_encode_uint8(buf, event->data.cdc_line_coding.parity);
nrf_profiler_log_encode_uint8(buf, event->data.cdc_line_coding.flow_ctrl);
break;
default:
break;
}
}
APP_EVENT_INFO_DEFINE(usb_control_event,
ENCODE(NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U32,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8,
NRF_PROFILER_ARG_U8),
ENCODE("type", "baud_or_zero", "arg1", "arg2", "arg3", "arg4"),
profile_usb_control_event);
APP_EVENT_TYPE_DEFINE(usb_control_event,
log_usb_control_event,
&usb_control_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -22,6 +22,7 @@
#include "function_bitmap_update_event.h" #include "function_bitmap_update_event.h"
#include "hid_led_event.h" #include "hid_led_event.h"
#include "proto_rx_event.h" #include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h" #include "proto_tx_event.h"
#include "protocol_module.h" #include "protocol_module.h"
#include "theme_rgb_update_event.h" #include "theme_rgb_update_event.h"
@@ -39,7 +40,14 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static bool initialized; static bool initialized;
static bool running; static bool running;
static bool hello_done[PROTO_TRANSPORT_COUNT];
enum proto_session_state {
PROTO_SESSION_DOWN = 0,
PROTO_SESSION_WAIT_HELLO,
PROTO_SESSION_ACTIVE,
};
static enum proto_session_state session_state[PROTO_TRANSPORT_COUNT];
static int decode_body(const uint8_t *payload, size_t payload_len, static int decode_body(const uint8_t *payload, size_t payload_len,
CdcPacketBody *body) CdcPacketBody *body)
@@ -151,8 +159,8 @@ static int encode_function_bitmap_state(const uint8_t *bitmap, uint8_t *payload,
static int module_init(void) static int module_init(void)
{ {
for (size_t i = 0; i < ARRAY_SIZE(hello_done); i++) { for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) {
hello_done[i] = false; session_state[i] = PROTO_SESSION_DOWN;
} }
return 0; return 0;
@@ -174,22 +182,13 @@ static void module_pause(void)
return; return;
} }
for (size_t i = 0; i < ARRAY_SIZE(hello_done); i++) { for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) {
hello_done[i] = false; session_state[i] = PROTO_SESSION_DOWN;
} }
running = false; running = false;
} }
void protocol_module_reset_transport_state(enum proto_transport transport)
{
if (transport >= PROTO_TRANSPORT_COUNT) {
return;
}
hello_done[transport] = false;
}
int protocol_module_process_message(enum proto_transport transport, int protocol_module_process_message(enum proto_transport transport,
const uint8_t *req_payload, const uint8_t *req_payload,
size_t req_payload_len, size_t req_payload_len,
@@ -216,6 +215,10 @@ int protocol_module_process_message(enum proto_transport transport,
switch (body.which_body) { switch (body.which_body) {
case CdcPacketBody_hello_req_tag: case CdcPacketBody_hello_req_tag:
if (session_state[transport] == PROTO_SESSION_DOWN) {
return -EAGAIN;
}
LOG_INF("HelloReq transport:%u protocol_version:%u", LOG_INF("HelloReq transport:%u protocol_version:%u",
transport, body.body.hello_req.protocol_version); transport, body.body.hello_req.protocol_version);
@@ -224,11 +227,11 @@ int protocol_module_process_message(enum proto_transport transport,
body.body.hello_req.protocol_version); body.body.hello_req.protocol_version);
} }
hello_done[transport] = true; session_state[transport] = PROTO_SESSION_ACTIVE;
return encode_hello_rsp(rsp_payload, rsp_payload_buf_size, rsp_payload_len); return encode_hello_rsp(rsp_payload, rsp_payload_buf_size, rsp_payload_len);
case CdcPacketBody_bitmap_tag: case CdcPacketBody_bitmap_tag:
if (!hello_done[transport]) { if (session_state[transport] != PROTO_SESSION_ACTIVE) {
return encode_error(CdcPacketBody_bitmap_tag, return encode_error(CdcPacketBody_bitmap_tag,
ErrorCode_ERROR_CODE_NOT_READY, ErrorCode_ERROR_CODE_NOT_READY,
rsp_payload, rsp_payload_buf_size, rsp_payload, rsp_payload_buf_size,
@@ -255,7 +258,7 @@ int protocol_module_process_message(enum proto_transport transport,
rsp_payload_buf_size, rsp_payload_len); rsp_payload_buf_size, rsp_payload_len);
case CdcPacketBody_time_sync_tag: case CdcPacketBody_time_sync_tag:
if (!hello_done[transport]) { if (session_state[transport] != PROTO_SESSION_ACTIVE) {
return encode_error(CdcPacketBody_time_sync_tag, return encode_error(CdcPacketBody_time_sync_tag,
ErrorCode_ERROR_CODE_NOT_READY, ErrorCode_ERROR_CODE_NOT_READY,
rsp_payload, rsp_payload_buf_size, rsp_payload, rsp_payload_buf_size,
@@ -278,7 +281,7 @@ int protocol_module_process_message(enum proto_transport transport,
rsp_payload_buf_size, rsp_payload_len); rsp_payload_buf_size, rsp_payload_len);
case CdcPacketBody_theme_rgb_tag: case CdcPacketBody_theme_rgb_tag:
if (!hello_done[transport]) { if (session_state[transport] != PROTO_SESSION_ACTIVE) {
return encode_error(CdcPacketBody_theme_rgb_tag, return encode_error(CdcPacketBody_theme_rgb_tag,
ErrorCode_ERROR_CODE_NOT_READY, ErrorCode_ERROR_CODE_NOT_READY,
rsp_payload, rsp_payload_buf_size, rsp_payload, rsp_payload_buf_size,
@@ -340,6 +343,29 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event)
return false; return false;
} }
static bool handle_proto_transport_state_event(
const struct proto_transport_state_event *event)
{
if (event->transport >= PROTO_TRANSPORT_COUNT) {
return false;
}
switch (event->state) {
case PROTO_TRANSPORT_LINK_DOWN:
session_state[event->transport] = PROTO_SESSION_DOWN;
break;
case PROTO_TRANSPORT_LINK_READY:
session_state[event->transport] = PROTO_SESSION_WAIT_HELLO;
break;
default:
return false;
}
return false;
}
static bool handle_function_bitmap_state_event( static bool handle_function_bitmap_state_event(
const struct function_bitmap_state_event *event) const struct function_bitmap_state_event *event)
{ {
@@ -353,7 +379,7 @@ static bool handle_function_bitmap_state_event(
for (enum proto_transport transport = 0; transport < PROTO_TRANSPORT_COUNT; for (enum proto_transport transport = 0; transport < PROTO_TRANSPORT_COUNT;
transport++) { transport++) {
if (!hello_done[transport]) { if (session_state[transport] != PROTO_SESSION_ACTIVE) {
continue; continue;
} }
@@ -388,7 +414,7 @@ static bool handle_hid_led_event(const struct hid_led_event *event)
PROTO_TRANSPORT_USB_CDC : PROTO_TRANSPORT_USB_CDC :
PROTO_TRANSPORT_BLE_NUS; PROTO_TRANSPORT_BLE_NUS;
if (!hello_done[transport]) { if (session_state[transport] != PROTO_SESSION_ACTIVE) {
return false; return false;
} }
@@ -412,6 +438,11 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_proto_rx_event(cast_proto_rx_event(aeh)); return handle_proto_rx_event(cast_proto_rx_event(aeh));
} }
if (is_proto_transport_state_event(aeh)) {
return handle_proto_transport_state_event(
cast_proto_transport_state_event(aeh));
}
if (is_function_bitmap_state_event(aeh)) { if (is_function_bitmap_state_event(aeh)) {
return handle_function_bitmap_state_event( return handle_function_bitmap_state_event(
cast_function_bitmap_state_event(aeh)); cast_function_bitmap_state_event(aeh));
@@ -480,5 +511,6 @@ APP_EVENT_SUBSCRIBE(MODULE, function_bitmap_state_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_led_event); APP_EVENT_SUBSCRIBE(MODULE, hid_led_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, proto_rx_event); APP_EVENT_SUBSCRIBE(MODULE, proto_rx_event);
APP_EVENT_SUBSCRIBE(MODULE, proto_transport_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -17,8 +17,9 @@
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include "proto_rx_event.h" #include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h" #include "proto_tx_event.h"
#include "protocol_module.h" #include "usb_control_event.h"
#include "usb_state_event.h" #include "usb_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); 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_TX_RING_BUF_SIZE 256
#define USB_CDC_RX_CHUNK_SIZE 32 #define USB_CDC_RX_CHUNK_SIZE 32
#define USB_CDC_PROTO_RX_BUF_SIZE 128 #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_PROTO_RX_FLUSH_DELAY K_MSEC(3)
#define USB_CDC_EXPECTED_BAUDRATE 115200U #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 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]; 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 rx_ringbuf;
static struct ring_buf tx_ringbuf; static struct ring_buf tx_ringbuf;
static struct k_work rx_work; static struct k_work rx_work;
static struct k_work_delayable control_work;
static struct k_work_delayable rx_flush_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 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) static void reset_ring_buffers(void)
{ {
@@ -62,16 +97,98 @@ static void disable_uart_io(void)
{ {
uart_irq_rx_disable(cdc_dev); uart_irq_rx_disable(cdc_dev);
uart_irq_tx_disable(cdc_dev); uart_irq_tx_disable(cdc_dev);
rx_enabled = false; ctx.proto_rx_len = 0U;
dtr_ready = false;
proto_rx_len = 0U;
k_work_cancel_delayable(&rx_flush_work); k_work_cancel_delayable(&rx_flush_work);
reset_ring_buffers(); 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) static void kick_tx(void)
{ {
if (!running || !usb_active || !dtr_ready) { if (transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) {
return; return;
} }
@@ -133,15 +250,15 @@ static void rx_work_handler(struct k_work *work)
return; 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", LOG_WRN("Drop oversized CDC protobuf message len:%u",
(uint32_t)(proto_rx_len + len)); (uint32_t)(ctx.proto_rx_len + len));
proto_rx_len = 0U; ctx.proto_rx_len = 0U;
} }
if (len > 0U) { if (len > 0U) {
memcpy(&proto_rx_buf[proto_rx_len], buffer, len); memcpy(&proto_rx_buf[ctx.proto_rx_len], buffer, len);
proto_rx_len += len; ctx.proto_rx_len += len;
k_work_reschedule(&rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY); 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); 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; return;
} }
(void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf, proto_rx_len); (void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf,
proto_rx_len = 0U; ctx.proto_rx_len);
} ctx.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);
} }
static void cdc_interrupt_handler(const struct device *dev, void *user_data) 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); irq_unlock(key);
if (written < (uint32_t)recv_len) { 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); k_work_submit(&rx_work);
@@ -271,61 +344,97 @@ static int module_init(void)
reset_ring_buffers(); reset_ring_buffers();
k_work_init(&rx_work, rx_work_handler); 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); k_work_init_delayable(&rx_flush_work, rx_flush_work_handler);
uart_irq_callback_set(cdc_dev, cdc_interrupt_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; return 0;
} }
static int module_start(void) static void module_start(void)
{ {
if (running) { lifecycle_set(MODULE_STATE_READY);
return 0; business_state_sync_from_usb();
}
running = true;
if (usb_active) {
k_work_reschedule(&control_work, K_NO_WAIT);
}
return 0;
} }
static void module_pause(void) static void module_pause(void)
{ {
if (!running) { business_state_set(USB_CDC_BUS_OFFLINE);
return; lifecycle_set(MODULE_STATE_STANDBY);
}
k_work_cancel_delayable(&control_work);
k_work_cancel_delayable(&rx_flush_work);
disable_uart_io();
running = false;
} }
static bool handle_usb_state_event(const struct usb_state_event *event) static bool handle_usb_state_event(const struct usb_state_event *event)
{ {
bool new_usb_active = (event->state == USB_STATE_ACTIVE); bool new_usb_active = (event->state == USB_STATE_ACTIVE);
if (new_usb_active == usb_active) { if (new_usb_active == ctx.usb_active) {
return false; return false;
} }
usb_active = new_usb_active; ctx.usb_active = new_usb_active;
business_state_sync_from_usb();
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);
}
return false; 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) static bool handle_proto_tx_event(const struct proto_tx_event *event)
{ {
uint32_t written; uint32_t written;
@@ -335,7 +444,7 @@ static bool handle_proto_tx_event(const struct proto_tx_event *event)
return false; return false;
} }
if (!running || !usb_active || !dtr_ready) { if (transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) {
return false; 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)); 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)) { 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)) { 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) { if (err) {
module_set_state(MODULE_STATE_ERROR); lifecycle_set(MODULE_STATE_ERROR);
return false; return false;
} }
initialized = true;
} }
err = module_start(); module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
} }
return false; return false;
} }
if (is_power_down_event(aeh)) { if (is_power_down_event(aeh)) {
if (initialized) { if (ctx.lifecycle != MODULE_STATE_OFF) {
module_pause(); module_pause();
module_set_state(MODULE_STATE_STANDBY);
} }
return false; return false;
} }
if (is_wake_up_event(aeh)) { if (is_wake_up_event(aeh)) {
if (initialized) { if (ctx.lifecycle != MODULE_STATE_OFF) {
int err = module_start(); module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
} }
return false; 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_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, proto_tx_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(MODULE, usb_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -9,12 +9,14 @@
#include <caf/events/module_suspend_event.h> #include <caf/events/module_suspend_event.h>
#include <caf/events/power_event.h> #include <caf/events/power_event.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/usb/usbd.h> #include <zephyr/usb/usbd.h>
#include <caf/events/power_manager_event.h> #include <caf/events/power_manager_event.h>
#include "usb_function_hook.h" #include "usb_function_hook.h"
#include "usb_control_event.h"
#include "usb_state_event.h" #include "usb_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -147,6 +149,54 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
{ {
ARG_UNUSED(usbd_ctx); ARG_UNUSED(usbd_ctx);
if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) {
uint32_t dtr = 0U;
int err = uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr);
if (err) {
LOG_WRN("Failed to get CDC DTR (%d)", err);
} else {
submit_usb_control_cdc_line_state_event(msg->dev, dtr != 0U);
}
return;
}
if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) {
uint32_t baudrate = 0U;
uint8_t data_bits = 0U;
uint8_t stop_bits = 0U;
uint8_t parity = 0U;
uint8_t flow_ctrl = 0U;
int err;
err = uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
if (err) {
LOG_WRN("Failed to get CDC baudrate (%d)", err);
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
{
struct uart_config cfg;
err = uart_config_get(msg->dev, &cfg);
if (err) {
LOG_WRN("uart_config_get failed (%d)", err);
} else {
data_bits = (uint8_t)cfg.data_bits;
stop_bits = (uint8_t)cfg.stop_bits;
parity = (uint8_t)cfg.parity;
flow_ctrl = (uint8_t)cfg.flow_ctrl;
}
}
#endif
submit_usb_control_cdc_line_coding_event(msg->dev, baudrate,
data_bits, stop_bits,
parity, flow_ctrl);
return;
}
switch (msg->type) { switch (msg->type) {
case USBD_MSG_VBUS_READY: case USBD_MSG_VBUS_READY:
update_power_manager_restriction(true); update_power_manager_restriction(true);