feat(ble): 添加BLE NUS模块替换原有的BLE串口功能

- 将ble_serial_module替换为ble_nus_module以使用标准的BLE NUS服务
- 移除不再使用的cdc_wrapper_module和相关事件处理
- 更新协议传输层抽象,支持USB CDC和BLE NUS两种传输方式
- 创建统一的proto_rx_event和proto_tx_event替代专用的串行通信事件
- 添加proto_common.h定义传输类型枚举
- 修改protocol_module接口以支持多传输方式
- 在prj.conf中启用CONFIG_BT_ZEPHYR_NUS配置选项
This commit is contained in:
2026-04-15 10:23:12 +08:00
parent c4b205b8a1
commit bc42a4dd63
25 changed files with 538 additions and 1499 deletions

View File

@@ -25,7 +25,7 @@ target_sources(app PRIVATE
src/ble_adv_uuid16.c src/ble_adv_uuid16.c
src/ble_bas_module.c src/ble_bas_module.c
src/ble_hid_module.c src/ble_hid_module.c
src/ble_serial_module.c src/ble_nus_module.c
src/display_module.c src/display_module.c
src/encoder_module.c src/encoder_module.c
src/hid_flowctrl_module.c src/hid_flowctrl_module.c
@@ -35,15 +35,11 @@ target_sources(app PRIVATE
src/led_strip_module.c src/led_strip_module.c
src/time_sync_module.c src/time_sync_module.c
src/ui/ui_main.c src/ui/ui_main.c
src/cdc_wrapper_module.c
src/protocol_module.c src/protocol_module.c
src/usb_cdc_module.c src/usb_cdc_module.c
src/usb_device_module.c src/usb_device_module.c
src/usb_hid_module.c src/usb_hid_module.c
src/events/bat_state_event.c src/events/bat_state_event.c
src/events/ble_serial_rx_event.c
src/events/ble_serial_tx_event.c
src/events/cdc_proto_tx_event.c
src/events/datetime_event.c src/events/datetime_event.c
src/events/encoder_event.c src/events/encoder_event.c
src/events/function_bitmap_update_event.c src/events/function_bitmap_update_event.c
@@ -56,10 +52,10 @@ target_sources(app PRIVATE
src/mode_switch_module.c src/mode_switch_module.c
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_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_cdc_rx_event.c
src/events/usb_cdc_tx_event.c
src/events/usb_state_event.c src/events/usb_state_event.c
) )

View File

@@ -1,12 +0,0 @@
#ifndef BLINKY_CDC_WRAPPER_MODULE_H_
#define BLINKY_CDC_WRAPPER_MODULE_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_CDC_WRAPPER_MODULE_H_ */

View File

@@ -1,43 +0,0 @@
#ifndef BLINKY_BLE_SERIAL_RX_EVENT_H_
#define BLINKY_BLE_SERIAL_RX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ble_serial_rx_event {
struct app_event_header header;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(ble_serial_rx_event);
static inline int submit_ble_serial_rx_event(const uint8_t *data, size_t len)
{
struct ble_serial_rx_event *event;
if ((data == NULL) && (len > 0U)) {
return -EINVAL;
}
event = new_ble_serial_rx_event(len);
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_BLE_SERIAL_RX_EVENT_H_ */

View File

@@ -1,43 +0,0 @@
#ifndef BLINKY_BLE_SERIAL_TX_EVENT_H_
#define BLINKY_BLE_SERIAL_TX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ble_serial_tx_event {
struct app_event_header header;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(ble_serial_tx_event);
static inline int submit_ble_serial_tx_event(const uint8_t *data, size_t len)
{
struct ble_serial_tx_event *event;
if ((data == NULL) && (len > 0U)) {
return -EINVAL;
}
event = new_ble_serial_tx_event(len);
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_BLE_SERIAL_TX_EVENT_H_ */

View File

@@ -1,46 +0,0 @@
#ifndef BLINKY_CDC_PROTO_TX_EVENT_H_
#define BLINKY_CDC_PROTO_TX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct cdc_proto_tx_event {
struct app_event_header header;
uint8_t type;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(cdc_proto_tx_event);
static inline int submit_cdc_proto_tx_event(uint8_t type, const uint8_t *payload,
size_t payload_len)
{
struct cdc_proto_tx_event *event;
if ((payload == NULL) && (payload_len > 0U)) {
return -EINVAL;
}
event = new_cdc_proto_tx_event(payload_len);
event->type = type;
if (payload_len > 0U) {
memcpy(event->dyndata.data, payload, payload_len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_CDC_PROTO_TX_EVENT_H_ */

18
inc/events/proto_common.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef BLINKY_PROTO_COMMON_H_
#define BLINKY_PROTO_COMMON_H_
#ifdef __cplusplus
extern "C" {
#endif
enum proto_transport {
PROTO_TRANSPORT_USB_CDC = 0,
PROTO_TRANSPORT_BLE_NUS,
PROTO_TRANSPORT_COUNT,
};
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_PROTO_COMMON_H_ */

View File

@@ -0,0 +1,50 @@
#ifndef BLINKY_PROTO_RX_EVENT_H_
#define BLINKY_PROTO_RX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#include "proto_common.h"
#ifdef __cplusplus
extern "C" {
#endif
struct proto_rx_event {
struct app_event_header header;
enum proto_transport transport;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(proto_rx_event);
static inline int submit_proto_rx_event(enum proto_transport transport,
const uint8_t *data, size_t len)
{
struct proto_rx_event *event;
if ((transport >= PROTO_TRANSPORT_COUNT) ||
((data == NULL) && (len > 0U))) {
return -EINVAL;
}
event = new_proto_rx_event(len);
event->transport = transport;
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_PROTO_RX_EVENT_H_ */

View File

@@ -0,0 +1,50 @@
#ifndef BLINKY_PROTO_TX_EVENT_H_
#define BLINKY_PROTO_TX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#include "proto_common.h"
#ifdef __cplusplus
extern "C" {
#endif
struct proto_tx_event {
struct app_event_header header;
enum proto_transport transport;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(proto_tx_event);
static inline int submit_proto_tx_event(enum proto_transport transport,
const uint8_t *data, size_t len)
{
struct proto_tx_event *event;
if ((transport >= PROTO_TRANSPORT_COUNT) ||
((data == NULL) && (len > 0U))) {
return -EINVAL;
}
event = new_proto_tx_event(len);
event->transport = transport;
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_PROTO_TX_EVENT_H_ */

View File

@@ -1,43 +0,0 @@
#ifndef BLINKY_USB_CDC_RX_EVENT_H_
#define BLINKY_USB_CDC_RX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct usb_cdc_rx_event {
struct app_event_header header;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(usb_cdc_rx_event);
static inline int submit_usb_cdc_rx_event(const uint8_t *data, size_t len)
{
struct usb_cdc_rx_event *event;
if ((data == NULL) && (len > 0U)) {
return -EINVAL;
}
event = new_usb_cdc_rx_event(len);
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_USB_CDC_RX_EVENT_H_ */

View File

@@ -1,43 +0,0 @@
#ifndef BLINKY_USB_CDC_TX_EVENT_H_
#define BLINKY_USB_CDC_TX_EVENT_H_
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct usb_cdc_tx_event {
struct app_event_header header;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(usb_cdc_tx_event);
static inline int submit_usb_cdc_tx_event(const uint8_t *data, size_t len)
{
struct usb_cdc_tx_event *event;
if ((data == NULL) && (len > 0U)) {
return -EINVAL;
}
event = new_usb_cdc_tx_event(len);
if (len > 0U) {
memcpy(event->dyndata.data, data, len);
}
APP_EVENT_SUBMIT(event);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_USB_CDC_TX_EVENT_H_ */

View File

@@ -1,31 +1,25 @@
#ifndef BLINKY_PROTOCOL_MODULE_H_ #ifndef BLINKY_PROTOCOL_MODULE_H_
#define BLINKY_PROTOCOL_MODULE_H_ #define BLINKY_PROTOCOL_MODULE_H_
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "proto_common.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#define CDC_PROTO_TYPE_HELLO_REQ 0x01U int protocol_module_process_message(enum proto_transport transport,
#define CDC_PROTO_TYPE_HELLO_RSP 0x02U
#define CDC_PROTO_TYPE_BITMAP 0x10U
#define CDC_PROTO_TYPE_FUNCTION_KEY_EVENT 0x20U
#define CDC_PROTO_TYPE_LED_STATE 0x21U
#define CDC_PROTO_TYPE_TIME_SYNC 0x30U
#define CDC_PROTO_TYPE_THEME_RGB 0x31U
#define CDC_PROTO_TYPE_ACK 0x7EU
#define CDC_PROTO_TYPE_ERROR 0x7FU
int protocol_module_process_cdc_packet(uint8_t req_type,
const uint8_t *req_payload, const uint8_t *req_payload,
size_t req_payload_len, size_t req_payload_len,
uint8_t *rsp_type,
uint8_t *rsp_payload, uint8_t *rsp_payload,
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

@@ -1,26 +0,0 @@
#ifndef BLINKY_USB_DEVICE_MODULE_H_
#define BLINKY_USB_DEVICE_MODULE_H_
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
enum usb_function {
USB_FUNCTION_HID = 0x01,
USB_FUNCTION_CDC_ACM = 0x02,
};
enum usb_device_state {
USB_DEVICE_STATE_DISCONNECTED,
USB_DEVICE_STATE_POWERED,
USB_DEVICE_STATE_ACTIVE,
USB_DEVICE_STATE_SUSPENDED,
};
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_USB_DEVICE_MODULE_H_ */

View File

@@ -45,6 +45,7 @@ CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n
# BLE # BLE
CONFIG_BT=y CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y CONFIG_BT_PERIPHERAL=y
CONFIG_BT_ZEPHYR_NUS=y
CONFIG_BT_SMP=y CONFIG_BT_SMP=y
CONFIG_BT_BONDABLE=y CONFIG_BT_BONDABLE=y
CONFIG_BT_SETTINGS=y CONFIG_BT_SETTINGS=y

215
src/ble_nus_module.c Normal file
View File

@@ -0,0 +1,215 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#define MODULE ble_nus_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <caf/events/ble_common_event.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/services/nus.h>
#include <zephyr/logging/log.h>
#include "proto_rx_event.h"
#include "proto_tx_event.h"
#include "protocol_module.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static struct bt_conn *active_conn;
static bool initialized;
static bool running;
static bool ble_ready;
static bool tx_notify_enabled;
static void notif_enabled(bool enabled, void *ctx)
{
ARG_UNUSED(ctx);
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)
{
ARG_UNUSED(ctx);
if (!running || !ble_ready || (conn != active_conn)) {
return;
}
(void)submit_proto_rx_event(PROTO_TRANSPORT_BLE_NUS, data, len);
}
static struct bt_nus_cb nus_listener = {
.notif_enabled = notif_enabled,
.received = received,
};
static void reset_connection_state(void)
{
active_conn = NULL;
tx_notify_enabled = false;
protocol_module_reset_transport_state(PROTO_TRANSPORT_BLE_NUS);
}
static int module_init(void)
{
int err;
err = bt_nus_cb_register(&nus_listener, NULL);
if (err) {
LOG_ERR("bt_nus_cb_register failed (%d)", err);
return err;
}
reset_connection_state();
return 0;
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
{
if (!running) {
return;
}
running = false;
tx_notify_enabled = false;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
{
switch (event->state) {
case PEER_STATE_CONNECTED:
if (active_conn != NULL) {
return false;
}
active_conn = event->id;
tx_notify_enabled = false;
protocol_module_reset_transport_state(PROTO_TRANSPORT_BLE_NUS);
return false;
case PEER_STATE_DISCONNECTED:
if (active_conn != event->id) {
return false;
}
reset_connection_state();
return false;
default:
return false;
}
}
static bool handle_proto_tx_event(const struct proto_tx_event *event)
{
int err;
if (event->transport != PROTO_TRANSPORT_BLE_NUS) {
return false;
}
if (!running || !ble_ready || (active_conn == NULL) || !tx_notify_enabled) {
return false;
}
err = bt_nus_send(active_conn, event->dyndata.data, (uint16_t)event->dyndata.size);
if (err) {
LOG_WRN("bt_nus_send failed (%d)", err);
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_proto_tx_event(aeh)) {
return handle_proto_tx_event(cast_proto_tx_event(aeh));
}
if (is_ble_peer_event(aeh)) {
return handle_ble_peer_event(cast_ble_peer_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
}
return false;
}
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
ble_ready = true;
if (running) {
module_set_state(MODULE_STATE_READY);
}
return false;
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
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 if (ble_ready) {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, proto_tx_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -1,300 +0,0 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#define MODULE ble_serial_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <caf/events/ble_common_event.h>
#include <zephyr/bluetooth/att.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/logging/log.h>
#include "ble_serial_rx_event.h"
#include "ble_serial_tx_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define BLE_SERIAL_RX_MAX_LEN 64U
#define BLE_SERIAL_MIN_PAYLOAD_LEN 32U
#define BLE_SERIAL_NOTIFY_OVERHEAD 3U
#define BLE_SERIAL_SERVICE_UUID_VAL \
BT_UUID_128_ENCODE(0x0b7f6000, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110)
#define BLE_SERIAL_RX_UUID_VAL \
BT_UUID_128_ENCODE(0x0b7f6001, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110)
#define BLE_SERIAL_TX_UUID_VAL \
BT_UUID_128_ENCODE(0x0b7f6002, 0x38d2, 0x4f62, 0x8f6f, 0x36c4fd73a110)
static struct bt_conn *active_conn;
static bool initialized;
static bool running;
static bool ble_ready;
static bool secured;
static bool tx_notify_enabled;
static const struct bt_uuid_128 ble_serial_service_uuid =
BT_UUID_INIT_128(BLE_SERIAL_SERVICE_UUID_VAL);
static const struct bt_uuid_128 ble_serial_rx_uuid =
BT_UUID_INIT_128(BLE_SERIAL_RX_UUID_VAL);
static const struct bt_uuid_128 ble_serial_tx_uuid =
BT_UUID_INIT_128(BLE_SERIAL_TX_UUID_VAL);
static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
ARG_UNUSED(attr);
tx_notify_enabled = (value == BT_GATT_CCC_NOTIFY);
LOG_INF("BLE serial TX notify %s", tx_notify_enabled ? "enabled" : "disabled");
}
static ssize_t rx_write_handler(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf,
uint16_t len,
uint16_t offset,
uint8_t flags)
{
ARG_UNUSED(attr);
ARG_UNUSED(flags);
if (!running || !ble_ready || !secured || (conn != active_conn)) {
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
}
if (offset != 0U) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
if ((buf == NULL) || (len == 0U) || (len > BLE_SERIAL_RX_MAX_LEN)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
(void)submit_ble_serial_rx_event(buf, len);
return len;
}
BT_GATT_SERVICE_DEFINE(ble_serial_svc,
BT_GATT_PRIMARY_SERVICE(&ble_serial_service_uuid.uuid),
BT_GATT_CHARACTERISTIC(&ble_serial_rx_uuid.uuid,
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE_ENCRYPT,
NULL, rx_write_handler, NULL),
BT_GATT_CHARACTERISTIC(&ble_serial_tx_uuid.uuid,
BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_NONE,
NULL, NULL, NULL),
BT_GATT_CCC(tx_ccc_cfg_changed,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT)
);
static void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
if (conn != active_conn) {
return;
}
LOG_INF("BLE serial MTU updated tx:%u rx:%u", tx, rx);
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = mtu_updated,
};
static size_t notify_payload_max(void)
{
uint16_t mtu;
if (active_conn == NULL) {
return 0U;
}
mtu = bt_gatt_get_mtu(active_conn);
if (mtu <= BLE_SERIAL_NOTIFY_OVERHEAD) {
return 0U;
}
return (size_t)(mtu - BLE_SERIAL_NOTIFY_OVERHEAD);
}
static void reset_connection_state(void)
{
active_conn = NULL;
secured = false;
tx_notify_enabled = false;
}
static int module_init(void)
{
bt_gatt_cb_register(&gatt_callbacks);
reset_connection_state();
return 0;
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
{
if (!running) {
return;
}
running = false;
tx_notify_enabled = false;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
{
switch (event->state) {
case PEER_STATE_CONNECTED:
if (active_conn != NULL) {
return false;
}
active_conn = event->id;
secured = false;
tx_notify_enabled = false;
return false;
case PEER_STATE_SECURED:
if (active_conn != event->id) {
return false;
}
secured = true;
return false;
case PEER_STATE_DISCONNECTED:
if (active_conn != event->id) {
return false;
}
reset_connection_state();
return false;
default:
return false;
}
}
static bool handle_ble_serial_tx_event(const struct ble_serial_tx_event *event)
{
size_t payload_max;
int err;
if (!running || !ble_ready || (active_conn == NULL) || !secured || !tx_notify_enabled) {
return false;
}
payload_max = notify_payload_max();
if (payload_max < BLE_SERIAL_MIN_PAYLOAD_LEN) {
LOG_WRN("BLE serial MTU payload too small:%u", (uint32_t)payload_max);
return false;
}
if (event->dyndata.size > payload_max) {
LOG_WRN("BLE serial TX too large len:%u max:%u",
(uint32_t)event->dyndata.size, (uint32_t)payload_max);
return false;
}
err = bt_gatt_notify(active_conn, &ble_serial_svc.attrs[4],
event->dyndata.data,
(uint16_t)event->dyndata.size);
if (err) {
LOG_WRN("bt_gatt_notify failed (%d)", err);
}
return false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_ble_serial_tx_event(aeh)) {
return handle_ble_serial_tx_event(cast_ble_serial_tx_event(aeh));
}
if (is_ble_peer_event(aeh)) {
return handle_ble_peer_event(cast_ble_peer_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
}
return false;
}
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
ble_ready = true;
if (running) {
module_set_state(MODULE_STATE_READY);
}
return false;
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
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 if (ble_ready) {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, ble_serial_tx_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -1,298 +0,0 @@
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#define MODULE cdc_wrapper_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <zephyr/logging/log.h>
#include "cdc_proto_tx_event.h"
#include "protocol_module.h"
#include "usb_cdc_rx_event.h"
#include "usb_cdc_tx_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define CDC_WRAPPER_HEAD1 0xAAU
#define CDC_WRAPPER_HEAD2 0x55U
#define CDC_WRAPPER_MAX_PAYLOAD_LEN 64U
#define CDC_WRAPPER_MAX_FRAME_LEN (2U + 1U + 1U + CDC_WRAPPER_MAX_PAYLOAD_LEN + 1U)
enum frame_parse_state {
FRAME_PARSE_HEAD1,
FRAME_PARSE_HEAD2,
FRAME_PARSE_LEN,
FRAME_PARSE_TYPE,
FRAME_PARSE_PAYLOAD,
FRAME_PARSE_CHECKSUM,
};
struct cdc_frame_parser {
enum frame_parse_state state;
uint8_t len;
uint8_t type;
uint8_t checksum;
uint8_t payload[CDC_WRAPPER_MAX_PAYLOAD_LEN];
size_t payload_pos;
};
static bool initialized;
static bool running;
static struct cdc_frame_parser parser;
static void parser_reset(void)
{
parser.state = FRAME_PARSE_HEAD1;
parser.len = 0U;
parser.type = 0U;
parser.checksum = 0U;
parser.payload_pos = 0U;
}
static uint8_t frame_checksum(uint8_t len, uint8_t type,
const uint8_t *payload, size_t payload_len)
{
uint8_t checksum = CDC_WRAPPER_HEAD1 ^ CDC_WRAPPER_HEAD2 ^ len ^ type;
for (size_t i = 0; i < payload_len; i++) {
checksum ^= payload[i];
}
return checksum;
}
static void submit_tx_frame(uint8_t type, const uint8_t *payload, size_t payload_len)
{
size_t frame_len = 2U + 1U + 1U + payload_len + 1U;
uint8_t frame_buf[CDC_WRAPPER_MAX_FRAME_LEN];
frame_buf[0] = CDC_WRAPPER_HEAD1;
frame_buf[1] = CDC_WRAPPER_HEAD2;
frame_buf[2] = (uint8_t)payload_len;
frame_buf[3] = type;
memcpy(&frame_buf[4], payload, payload_len);
frame_buf[4U + payload_len] =
frame_checksum((uint8_t)payload_len, type, payload, payload_len);
(void)submit_usb_cdc_tx_event(frame_buf, frame_len);
}
static void process_complete_frame(void)
{
uint8_t rsp_type;
uint8_t rsp_payload[CDC_WRAPPER_MAX_PAYLOAD_LEN];
size_t rsp_payload_len = 0U;
int err;
err = protocol_module_process_cdc_packet(parser.type,
parser.payload,
parser.payload_pos,
&rsp_type,
rsp_payload,
sizeof(rsp_payload),
&rsp_payload_len);
if (err == -ENOTSUP) {
LOG_WRN("Ignore unsupported CDC packet type:0x%02x", parser.type);
return;
}
if (err) {
LOG_WRN("Protocol processing failed (%d)", err);
return;
}
LOG_INF("CDC response type:0x%02x len:%u",
rsp_type, (uint32_t)rsp_payload_len);
submit_tx_frame(rsp_type, rsp_payload, rsp_payload_len);
}
static void consume_byte(uint8_t byte)
{
switch (parser.state) {
case FRAME_PARSE_HEAD1:
if (byte == CDC_WRAPPER_HEAD1) {
parser.state = FRAME_PARSE_HEAD2;
}
break;
case FRAME_PARSE_HEAD2:
if (byte == CDC_WRAPPER_HEAD2) {
parser.state = FRAME_PARSE_LEN;
} else if (byte != CDC_WRAPPER_HEAD1) {
parser.state = FRAME_PARSE_HEAD1;
}
break;
case FRAME_PARSE_LEN:
if (byte > CDC_WRAPPER_MAX_PAYLOAD_LEN) {
LOG_WRN("Drop CDC frame with invalid len:%u", byte);
parser_reset();
break;
}
parser.len = byte;
parser.payload_pos = 0U;
parser.state = FRAME_PARSE_TYPE;
break;
case FRAME_PARSE_TYPE:
parser.type = byte;
parser.state = (parser.len == 0U) ? FRAME_PARSE_CHECKSUM :
FRAME_PARSE_PAYLOAD;
break;
case FRAME_PARSE_PAYLOAD:
parser.payload[parser.payload_pos++] = byte;
if (parser.payload_pos >= parser.len) {
parser.state = FRAME_PARSE_CHECKSUM;
}
break;
case FRAME_PARSE_CHECKSUM:
if (byte != frame_checksum(parser.len, parser.type,
parser.payload, parser.payload_pos)) {
LOG_WRN("Drop CDC frame with invalid checksum");
parser_reset();
break;
}
process_complete_frame();
parser_reset();
break;
default:
parser_reset();
break;
}
}
static bool handle_usb_cdc_rx_event(const struct usb_cdc_rx_event *event)
{
if (!running) {
return false;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
consume_byte(event->dyndata.data[i]);
}
return false;
}
static bool handle_cdc_proto_tx_event(const struct cdc_proto_tx_event *event)
{
if (!running) {
return false;
}
if (event->dyndata.size > CDC_WRAPPER_MAX_PAYLOAD_LEN) {
LOG_WRN("Drop CDC proto TX len:%u max:%u",
(uint32_t)event->dyndata.size,
(uint32_t)CDC_WRAPPER_MAX_PAYLOAD_LEN);
return false;
}
submit_tx_frame(event->type, event->dyndata.data, event->dyndata.size);
return false;
}
static int module_init(void)
{
parser_reset();
return 0;
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
{
if (!running) {
return;
}
running = false;
parser_reset();
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_usb_cdc_rx_event(aeh)) {
return handle_usb_cdc_rx_event(cast_usb_cdc_rx_event(aeh));
}
if (is_cdc_proto_tx_event(aeh)) {
return handle_cdc_proto_tx_event(cast_cdc_proto_tx_event(aeh));
}
if (is_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 (!initialized) {
err = module_init();
if (err) {
module_set_state(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);
}
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
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);
}
}
return false;
}
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, cdc_proto_tx_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, usb_cdc_rx_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -1,73 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include "ble_serial_rx_event.h"
#define BLE_SERIAL_RX_EVENT_LOG_BUF_LEN 384
static void log_ble_serial_rx_event(const struct app_event_header *aeh)
{
const struct ble_serial_rx_event *event = cast_ble_serial_rx_event(aeh);
char log_buf[BLE_SERIAL_RX_EVENT_LOG_BUF_LEN];
int pos;
pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"",
event->dyndata.size);
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure");
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c",
isprint(event->dyndata.data[i]) ?
event->dyndata.data[i] : '.');
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
pos += tmp;
}
pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\" hex:");
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, " %02x",
event->dyndata.data[i]);
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
break;
}
pos += tmp;
}
APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf);
}
static void profile_ble_serial_rx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct ble_serial_rx_event *event = cast_ble_serial_rx_event(aeh);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(ble_serial_rx_event,
ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("len"),
profile_ble_serial_rx_event);
APP_EVENT_TYPE_DEFINE(ble_serial_rx_event,
log_ble_serial_rx_event,
&ble_serial_rx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,62 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include "ble_serial_tx_event.h"
#define BLE_SERIAL_TX_EVENT_LOG_BUF_LEN 256
static void log_ble_serial_tx_event(const struct app_event_header *aeh)
{
const struct ble_serial_tx_event *event = cast_ble_serial_tx_event(aeh);
char log_buf[BLE_SERIAL_TX_EVENT_LOG_BUF_LEN];
int pos;
pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"",
event->dyndata.size);
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure");
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c",
isprint(event->dyndata.data[i]) ?
event->dyndata.data[i] : '.');
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
pos += tmp;
}
pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\"");
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf);
}
static void profile_ble_serial_tx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct ble_serial_tx_event *event = cast_ble_serial_tx_event(aeh);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(ble_serial_tx_event,
ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("len"),
profile_ble_serial_tx_event);
APP_EVENT_TYPE_DEFINE(ble_serial_tx_event,
log_ble_serial_tx_event,
&ble_serial_tx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,29 +0,0 @@
#include "cdc_proto_tx_event.h"
static void log_cdc_proto_tx_event(const struct app_event_header *aeh)
{
const struct cdc_proto_tx_event *event = cast_cdc_proto_tx_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "type:0x%02x len:%zu",
event->type, event->dyndata.size);
}
static void profile_cdc_proto_tx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct cdc_proto_tx_event *event = cast_cdc_proto_tx_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->type);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(cdc_proto_tx_event,
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8),
ENCODE("type", "len"),
profile_cdc_proto_tx_event);
APP_EVENT_TYPE_DEFINE(cdc_proto_tx_event,
log_cdc_proto_tx_event,
&cdc_proto_tx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -0,0 +1,42 @@
#include "proto_rx_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 void log_proto_rx_event(const struct app_event_header *aeh)
{
const struct proto_rx_event *event = cast_proto_rx_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "transport:%s len:%zu",
transport_name(event->transport),
event->dyndata.size);
}
static void profile_proto_rx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct proto_rx_event *event = cast_proto_rx_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->transport);
nrf_profiler_log_encode_uint16(buf, (uint16_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(proto_rx_event,
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U16),
ENCODE("transport", "len"),
profile_proto_rx_event);
APP_EVENT_TYPE_DEFINE(proto_rx_event,
log_proto_rx_event,
&proto_rx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -0,0 +1,42 @@
#include "proto_tx_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 void log_proto_tx_event(const struct app_event_header *aeh)
{
const struct proto_tx_event *event = cast_proto_tx_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "transport:%s len:%zu",
transport_name(event->transport),
event->dyndata.size);
}
static void profile_proto_tx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct proto_tx_event *event = cast_proto_tx_event(aeh);
nrf_profiler_log_encode_uint8(buf, event->transport);
nrf_profiler_log_encode_uint16(buf, (uint16_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(proto_tx_event,
ENCODE(NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U16),
ENCODE("transport", "len"),
profile_proto_tx_event);
APP_EVENT_TYPE_DEFINE(proto_tx_event,
log_proto_tx_event,
&proto_tx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,73 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include "usb_cdc_rx_event.h"
#define USB_CDC_RX_EVENT_LOG_BUF_LEN 384
static void log_usb_cdc_rx_event(const struct app_event_header *aeh)
{
const struct usb_cdc_rx_event *event = cast_usb_cdc_rx_event(aeh);
char log_buf[USB_CDC_RX_EVENT_LOG_BUF_LEN];
int pos;
pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"",
event->dyndata.size);
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure");
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c",
isprint(event->dyndata.data[i]) ?
event->dyndata.data[i] : '.');
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
pos += tmp;
}
pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\" hex:");
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, " %02x",
event->dyndata.data[i]);
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
break;
}
pos += tmp;
}
APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf);
}
static void profile_usb_cdc_rx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct usb_cdc_rx_event *event = cast_usb_cdc_rx_event(aeh);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(usb_cdc_rx_event,
ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("len"),
profile_usb_cdc_rx_event);
APP_EVENT_TYPE_DEFINE(usb_cdc_rx_event,
log_usb_cdc_rx_event,
&usb_cdc_rx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -1,62 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include "usb_cdc_tx_event.h"
#define USB_CDC_TX_EVENT_LOG_BUF_LEN 256
static void log_usb_cdc_tx_event(const struct app_event_header *aeh)
{
const struct usb_cdc_tx_event *event = cast_usb_cdc_tx_event(aeh);
char log_buf[USB_CDC_TX_EVENT_LOG_BUF_LEN];
int pos;
pos = snprintf(log_buf, sizeof(log_buf), "len:%zu ascii:\"",
event->dyndata.size);
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure");
return;
}
for (size_t i = 0; i < event->dyndata.size; i++) {
int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, "%c",
isprint(event->dyndata.data[i]) ?
event->dyndata.data[i] : '.');
if ((tmp < 0) || ((pos + tmp) >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
pos += tmp;
}
pos += snprintf(&log_buf[pos], sizeof(log_buf) - pos, "\"");
if ((pos < 0) || (pos >= sizeof(log_buf))) {
APP_EVENT_MANAGER_LOG(aeh, "len:%zu ascii:\"...\"",
event->dyndata.size);
return;
}
APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf);
}
static void profile_usb_cdc_tx_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct usb_cdc_tx_event *event = cast_usb_cdc_tx_event(aeh);
nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size);
}
APP_EVENT_INFO_DEFINE(usb_cdc_tx_event,
ENCODE(NRF_PROFILER_ARG_U8),
ENCODE("len"),
profile_usb_cdc_tx_event);
APP_EVENT_TYPE_DEFINE(usb_cdc_tx_event,
log_usb_cdc_tx_event,
&usb_cdc_tx_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));

View File

@@ -18,14 +18,9 @@
#include <proto/device_comm.pb.h> #include <proto/device_comm.pb.h>
#include "theme_rgb_update_event.h" #include "proto_rx_event.h"
#include "time_sync_event.h" #include "proto_tx_event.h"
#include "cdc_proto_tx_event.h"
#include "function_bitmap_update_event.h"
#include "hid_led_event.h"
#include "key_function_event.h"
#include "protocol_module.h" #include "protocol_module.h"
#include "usb_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -34,39 +29,12 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define PROTOCOL_PRODUCT_ID 0x52F0U #define PROTOCOL_PRODUCT_ID 0x52F0U
#define PROTOCOL_FIRMWARE_MAJOR 0U #define PROTOCOL_FIRMWARE_MAJOR 0U
#define PROTOCOL_FIRMWARE_MINOR 0U #define PROTOCOL_FIRMWARE_MINOR 0U
#define PROTOCOL_CAPABILITY_FLAGS (BIT(0) | BIT(1) | BIT(2) | BIT(3)) #define PROTOCOL_CAPABILITY_FLAGS 0U
#define PROTOCOL_MAX_MSG_LEN 128U
static bool initialized; static bool initialized;
static bool running; static bool running;
static bool keyboard_core_ready; static bool hello_done[PROTO_TRANSPORT_COUNT];
static bool usb_active;
static bool hello_done;
static bool type_matches_body(uint8_t type, const CdcPacketBody *body)
{
switch (type) {
case CDC_PROTO_TYPE_HELLO_REQ:
return body->which_body == CdcPacketBody_hello_req_tag;
case CDC_PROTO_TYPE_HELLO_RSP:
return body->which_body == CdcPacketBody_hello_rsp_tag;
case CDC_PROTO_TYPE_BITMAP:
return body->which_body == CdcPacketBody_bitmap_tag;
case CDC_PROTO_TYPE_FUNCTION_KEY_EVENT:
return body->which_body == CdcPacketBody_function_key_event_tag;
case CDC_PROTO_TYPE_LED_STATE:
return body->which_body == CdcPacketBody_led_state_tag;
case CDC_PROTO_TYPE_TIME_SYNC:
return body->which_body == CdcPacketBody_time_sync_tag;
case CDC_PROTO_TYPE_THEME_RGB:
return body->which_body == CdcPacketBody_theme_rgb_tag;
case CDC_PROTO_TYPE_ACK:
return body->which_body == CdcPacketBody_ack_tag;
case CDC_PROTO_TYPE_ERROR:
return body->which_body == CdcPacketBody_error_tag;
default:
return false;
}
}
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)
@@ -123,94 +91,12 @@ static int encode_hello_rsp(uint8_t *rsp_payload, size_t rsp_payload_buf_size,
return encode_body(&body, rsp_payload, rsp_payload_buf_size, rsp_payload_len); return encode_body(&body, rsp_payload, rsp_payload_buf_size, rsp_payload_len);
} }
static int encode_ack(uint8_t acked_type, uint8_t *rsp_payload,
size_t rsp_payload_buf_size, size_t *rsp_payload_len)
{
CdcPacketBody body = CdcPacketBody_init_zero;
body.which_body = CdcPacketBody_ack_tag;
body.body.ack.acked_type = acked_type;
return encode_body(&body, rsp_payload, rsp_payload_buf_size, rsp_payload_len);
}
static int encode_error(uint8_t error_type, ErrorCode error_code,
uint8_t *rsp_payload, size_t rsp_payload_buf_size,
size_t *rsp_payload_len)
{
CdcPacketBody body = CdcPacketBody_init_zero;
body.which_body = CdcPacketBody_error_tag;
body.body.error.error_type = error_type;
body.body.error.error_code = error_code;
return encode_body(&body, rsp_payload, rsp_payload_buf_size, rsp_payload_len);
}
static int encode_function_key_event(uint16_t usage, uint8_t action,
uint8_t *payload,
size_t payload_buf_size,
size_t *payload_len)
{
CdcPacketBody body = CdcPacketBody_init_zero;
body.which_body = CdcPacketBody_function_key_event_tag;
body.body.function_key_event.usage = usage;
body.body.function_key_event.action = (KeyAction)action;
return encode_body(&body, payload, payload_buf_size, payload_len);
}
static int encode_led_state(uint32_t led_mask, uint8_t *payload,
size_t payload_buf_size, size_t *payload_len)
{
CdcPacketBody body = CdcPacketBody_init_zero;
body.which_body = CdcPacketBody_led_state_tag;
body.body.led_state.led_mask = led_mask;
return encode_body(&body, payload, payload_buf_size, payload_len);
}
static int encode_error_response(uint8_t req_type, ErrorCode error_code,
uint8_t *rsp_type, uint8_t *rsp_payload,
size_t rsp_payload_buf_size,
size_t *rsp_payload_len)
{
int err;
err = encode_error(req_type, error_code, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
if (err) {
return err;
}
*rsp_type = CDC_PROTO_TYPE_ERROR;
return 0;
}
static int encode_ack_response(uint8_t acked_type, uint8_t *rsp_type,
uint8_t *rsp_payload,
size_t rsp_payload_buf_size,
size_t *rsp_payload_len)
{
int err;
err = encode_ack(acked_type, rsp_payload, rsp_payload_buf_size,
rsp_payload_len);
if (err) {
return err;
}
*rsp_type = CDC_PROTO_TYPE_ACK;
return 0;
}
static int module_init(void) static int module_init(void)
{ {
keyboard_core_ready = false; for (size_t i = 0; i < ARRAY_SIZE(hello_done); i++) {
usb_active = false; hello_done[i] = false;
hello_done = false; }
return 0; return 0;
} }
@@ -230,14 +116,25 @@ static void module_pause(void)
return; return;
} }
hello_done = false; for (size_t i = 0; i < ARRAY_SIZE(hello_done); i++) {
hello_done[i] = false;
}
running = false; running = false;
} }
int protocol_module_process_cdc_packet(uint8_t req_type, 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,
const uint8_t *req_payload, const uint8_t *req_payload,
size_t req_payload_len, size_t req_payload_len,
uint8_t *rsp_type,
uint8_t *rsp_payload, uint8_t *rsp_payload,
size_t rsp_payload_buf_size, size_t rsp_payload_buf_size,
size_t *rsp_payload_len) size_t *rsp_payload_len)
@@ -245,7 +142,8 @@ int protocol_module_process_cdc_packet(uint8_t req_type,
CdcPacketBody body; CdcPacketBody body;
int err; int err;
if ((rsp_type == NULL) || (rsp_payload == NULL) || (rsp_payload_len == NULL)) { if ((transport >= PROTO_TRANSPORT_COUNT) ||
(rsp_payload == NULL) || (rsp_payload_len == NULL)) {
return -EINVAL; return -EINVAL;
} }
@@ -255,191 +153,53 @@ int protocol_module_process_cdc_packet(uint8_t req_type,
err = decode_body(req_payload, req_payload_len, &body); err = decode_body(req_payload, req_payload_len, &body);
if (err) { if (err) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_LENGTH, return err;
rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
} }
if (!type_matches_body(req_type, &body)) { if (body.which_body != CdcPacketBody_hello_req_tag) {
LOG_WRN("CDC type/body mismatch type:0x%02x body_case:%d", LOG_WRN("Unsupported protobuf body case %d", body.which_body);
req_type, body.which_body); return -ENOTSUP;
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_PARAM,
rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
} }
switch (req_type) { LOG_INF("HelloReq transport:%u protocol_version:%u",
case CDC_PROTO_TYPE_HELLO_REQ: transport, body.body.hello_req.protocol_version);
LOG_INF("HelloReq protocol_version:%u",
body.body.hello_req.protocol_version);
if (body.body.hello_req.protocol_version != PROTOCOL_VERSION) { if (body.body.hello_req.protocol_version != PROTOCOL_VERSION) {
LOG_WRN("Unexpected protocol version:%u", LOG_WRN("Unexpected protocol version:%u",
body.body.hello_req.protocol_version); body.body.hello_req.protocol_version);
} }
hello_done = true; hello_done[transport] = true;
err = 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);
if (err) {
return err;
} }
*rsp_type = CDC_PROTO_TYPE_HELLO_RSP; static bool handle_proto_rx_event(const struct proto_rx_event *event)
return 0;
case CDC_PROTO_TYPE_BITMAP:
if (!hello_done) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_NOT_READY,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
if (!keyboard_core_ready) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_NOT_READY,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
if (body.body.bitmap.usage_bitmap.size != KEYBOARD_PROTOCOL_BITMAP_BYTES) {
LOG_WRN("Bitmap len:%u expected:%u",
(unsigned int)body.body.bitmap.usage_bitmap.size,
KEYBOARD_PROTOCOL_BITMAP_BYTES);
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_LENGTH,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
err = submit_function_bitmap_update_event(
body.body.bitmap.usage_bitmap.bytes);
if (err) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_PARAM,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
return encode_ack_response(req_type, rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
case CDC_PROTO_TYPE_TIME_SYNC:
if (!hello_done) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_NOT_READY,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
if (body.body.time_sync.version != 1U) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_PARAM,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
submit_time_sync_event(body.body.time_sync.version,
body.body.time_sync.flags,
body.body.time_sync.timezone_min,
body.body.time_sync.utc_ms,
body.body.time_sync.accuracy_ms);
return encode_ack_response(req_type, rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
case CDC_PROTO_TYPE_THEME_RGB:
if (!hello_done) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_NOT_READY,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
if ((body.body.theme_rgb.red > 255U) ||
(body.body.theme_rgb.green > 255U) ||
(body.body.theme_rgb.blue > 255U)) {
return encode_error_response(req_type, ErrorCode_ERROR_CODE_INVALID_PARAM,
rsp_type, rsp_payload,
rsp_payload_buf_size,
rsp_payload_len);
}
submit_theme_rgb_update_event((struct theme_rgb) {
.r = (uint8_t)body.body.theme_rgb.red,
.g = (uint8_t)body.body.theme_rgb.green,
.b = (uint8_t)body.body.theme_rgb.blue,
});
return encode_ack_response(req_type, rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
default:
LOG_WRN("Unsupported CDC protocol type:0x%02x", req_type);
return encode_error_response(req_type, ErrorCode_ERROR_CODE_UNKNOWN_TYPE,
rsp_type, rsp_payload,
rsp_payload_buf_size, rsp_payload_len);
}
}
static bool handle_key_function_event(const struct key_function_event *event)
{ {
uint8_t payload[64]; uint8_t rsp_payload[PROTOCOL_MAX_MSG_LEN];
size_t payload_len; size_t rsp_payload_len = 0U;
int err; int err;
if (!running || !usb_active || !hello_done) { if (!running) {
return false; return false;
} }
err = encode_function_key_event(event->usage, event->action, payload, err = protocol_module_process_message(event->transport,
sizeof(payload), &payload_len); event->dyndata.data,
event->dyndata.size,
rsp_payload,
sizeof(rsp_payload),
&rsp_payload_len);
if (err) { if (err) {
LOG_WRN("FunctionKeyEvent encode failed (%d)", err); if (err != -ENOTSUP) {
LOG_WRN("Protocol processing failed (%d)", err);
}
return false; return false;
} }
err = submit_cdc_proto_tx_event(CDC_PROTO_TYPE_FUNCTION_KEY_EVENT, err = submit_proto_tx_event(event->transport, rsp_payload, rsp_payload_len);
payload, payload_len);
if (err) { if (err) {
LOG_WRN("FunctionKeyEvent submit failed (%d)", err); LOG_WRN("Proto TX submit failed (%d)", err);
}
return false;
}
static bool handle_hid_led_event(const struct hid_led_event *event)
{
uint8_t payload[64];
size_t payload_len;
int err;
if (!running || !usb_active || !hello_done) {
return false;
}
err = encode_led_state(event->led_bm, payload, sizeof(payload), &payload_len);
if (err) {
LOG_WRN("LedState encode failed (%d)", err);
return false;
}
err = submit_cdc_proto_tx_event(CDC_PROTO_TYPE_LED_STATE, payload, payload_len);
if (err) {
LOG_WRN("LedState submit failed (%d)", err);
}
return false;
}
static bool handle_usb_state_event(const struct usb_state_event *event)
{
if ((event->op != USB_STATE_EVENT_OP_SNAPSHOT) ||
(event->src_module_id != MODULE_ID(usb_device_module))) {
return false;
}
usb_active = (event->flags & USB_STATEF_ACTIVE) != 0U;
if (!usb_active) {
hello_done = false;
} }
return false; return false;
@@ -447,16 +207,8 @@ static bool handle_usb_state_event(const struct usb_state_event *event)
static bool app_event_handler(const struct app_event_header *aeh) static bool app_event_handler(const struct app_event_header *aeh)
{ {
if (is_key_function_event(aeh)) { if (is_proto_rx_event(aeh)) {
return handle_key_function_event(cast_key_function_event(aeh)); return handle_proto_rx_event(cast_proto_rx_event(aeh));
}
if (is_hid_led_event(aeh)) {
return handle_hid_led_event(cast_hid_led_event(aeh));
}
if (is_usb_state_event(aeh)) {
return handle_usb_state_event(cast_usb_state_event(aeh));
} }
if (is_module_state_event(aeh)) { if (is_module_state_event(aeh)) {
@@ -484,11 +236,6 @@ static bool app_event_handler(const struct app_event_header *aeh)
return false; return false;
} }
if (check_state(event, MODULE_ID(keyboard_core_module), MODULE_STATE_READY)) {
keyboard_core_ready = true;
return false;
}
return false; return false;
} }
@@ -519,9 +266,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, hid_led_event);
APP_EVENT_SUBSCRIBE(MODULE, key_function_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE(MODULE, usb_state_event); APP_EVENT_SUBSCRIBE(MODULE, proto_rx_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

@@ -16,8 +16,9 @@
#include <zephyr/sys/ring_buffer.h> #include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include "usb_cdc_rx_event.h" #include "proto_rx_event.h"
#include "usb_cdc_tx_event.h" #include "proto_tx_event.h"
#include "protocol_module.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);
@@ -25,7 +26,9 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define USB_CDC_RX_RING_BUF_SIZE 256 #define USB_CDC_RX_RING_BUF_SIZE 256
#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_CONTROL_POLL_INTERVAL K_MSEC(100) #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 #define USB_CDC_EXPECTED_BAUDRATE 115200U
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);
@@ -36,12 +39,15 @@ 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 control_work;
static struct k_work_delayable rx_flush_work;
static bool initialized; static bool initialized;
static bool running; static bool running;
static bool usb_active; static bool usb_active;
static bool usb_function_prepared; static bool usb_function_prepared;
static bool dtr_ready; static bool dtr_ready;
static bool rx_enabled; static bool rx_enabled;
static uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE];
static size_t proto_rx_len;
static bool is_usb_owner_snapshot(const struct usb_state_event *event) static bool is_usb_owner_snapshot(const struct usb_state_event *event)
{ {
@@ -66,6 +72,8 @@ static void disable_uart_io(void)
uart_irq_tx_disable(cdc_dev); uart_irq_tx_disable(cdc_dev);
rx_enabled = false; rx_enabled = false;
dtr_ready = false; dtr_ready = false;
proto_rx_len = 0U;
k_work_cancel_delayable(&rx_flush_work);
reset_ring_buffers(); reset_ring_buffers();
} }
@@ -133,8 +141,30 @@ static void rx_work_handler(struct k_work *work)
return; return;
} }
(void)submit_usb_cdc_rx_event(buffer, len); if ((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;
} }
if (len > 0U) {
memcpy(&proto_rx_buf[proto_rx_len], buffer, len);
proto_rx_len += len;
k_work_reschedule(&rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY);
}
}
}
static void rx_flush_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (!running || !usb_active || !dtr_ready || (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) static void control_work_handler(struct k_work *work)
@@ -250,8 +280,10 @@ 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(&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); uart_irq_callback_set(cdc_dev, cdc_interrupt_handler);
usb_function_prepared = false; usb_function_prepared = false;
proto_rx_len = 0U;
return 0; return 0;
} }
@@ -276,6 +308,7 @@ static void module_pause(void)
} }
k_work_cancel_delayable(&control_work); k_work_cancel_delayable(&control_work);
k_work_cancel_delayable(&rx_flush_work);
disable_uart_io(); disable_uart_io();
running = false; running = false;
} }
@@ -304,6 +337,8 @@ static bool handle_usb_state_event(const struct usb_state_event *event)
if (!usb_active) { if (!usb_active) {
k_work_cancel_delayable(&control_work); 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(); disable_uart_io();
} else if (running) { } else if (running) {
k_work_reschedule(&control_work, K_NO_WAIT); k_work_reschedule(&control_work, K_NO_WAIT);
@@ -312,11 +347,15 @@ static bool handle_usb_state_event(const struct usb_state_event *event)
return false; return false;
} }
static bool handle_usb_cdc_tx_event(const struct usb_cdc_tx_event *event) static bool handle_proto_tx_event(const struct proto_tx_event *event)
{ {
uint32_t written; uint32_t written;
unsigned int key; unsigned int key;
if (event->transport != PROTO_TRANSPORT_USB_CDC) {
return false;
}
if (!running || !usb_active || !dtr_ready) { if (!running || !usb_active || !dtr_ready) {
return false; return false;
} }
@@ -343,8 +382,8 @@ static bool app_event_handler(const struct app_event_header *aeh)
return handle_usb_state_event(cast_usb_state_event(aeh)); return handle_usb_state_event(cast_usb_state_event(aeh));
} }
if (is_usb_cdc_tx_event(aeh)) { if (is_proto_tx_event(aeh)) {
return handle_usb_cdc_tx_event(cast_usb_cdc_tx_event(aeh)); return handle_proto_tx_event(cast_proto_tx_event(aeh));
} }
if (is_module_state_event(aeh)) { if (is_module_state_event(aeh)) {
@@ -402,7 +441,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, usb_state_event); APP_EVENT_SUBSCRIBE(MODULE, usb_state_event);
APP_EVENT_SUBSCRIBE(MODULE, usb_cdc_tx_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);