From 3971d7c4b25e8cd0ea59608df82fa330d4af9e4e Mon Sep 17 00:00:00 2001 From: skiinder Date: Fri, 24 Apr 2026 10:54:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(proto):=20=E6=B7=BB=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E9=80=9A=E4=BF=A1=E5=8D=8F=E8=AE=AEv1=E4=BF=AE?= =?UTF-8?q?=E8=AE=A2=E7=89=88=E5=8F=8A=E7=BB=9F=E4=B8=80=E5=B8=A7=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增docs/device_communication_protocol_v1.md文档,定义V1修订版协议 - CDC和BLE GATT均改为直接传输业务消息,去掉外层协议封装 - BLE改为使用NUS(Nordic UART Service)替代原有GATT服务 - 统一键盘位图为29字节格式,FunctionKeyEvent改为上报全键盘位图 - 顶层消息增加msg_id和reply_to字段用于请求响应匹配 - Ack和Error合并为统一Response消息类型 - CDC和NUS均增加统一外层帧格式:magic(2) + len(1) + protobuf - 添加Proto frame常量定义及长度验证逻辑 - 更新proto文件定义,包含DeviceMessage统一信封和ResponseCode枚举 - 重构hid_flowctrl_module.c中的上下文访问方式,统一使用ctx前缀 --- docs/device_communication_protocol_v1.md | 302 +++++++++++++++++++++++ inc/events/proto_common.h | 5 + inc/events/proto_rx_event.h | 3 +- inc/events/proto_tx_event.h | 3 +- proto/device_comm.proto | 31 ++- src/ble_nus_module.c | 5 + src/hid_flowctrl_module.c | 192 +++++++------- src/keyboard_core_module.c | 156 ++++++------ src/protocol_module.c | 250 ++++++++++++------- src/usb_cdc_module.c | 77 +++--- 10 files changed, 703 insertions(+), 321 deletions(-) create mode 100644 docs/device_communication_protocol_v1.md diff --git a/docs/device_communication_protocol_v1.md b/docs/device_communication_protocol_v1.md new file mode 100644 index 0000000..0a676b4 --- /dev/null +++ b/docs/device_communication_protocol_v1.md @@ -0,0 +1,302 @@ +# 下位机通信协议 v1 修订版 + +本版基于原 `device_communication_protocol_v1.md` 修订,变更点如下: + +- `CDC` 去掉外层协议,直接传业务消息 +- `BLE GATT` 改为使用 `NUS (Nordic UART Service)` +- `Bitmap` 统一为 29 字节键盘位图格式 +- `FunctionKeyEvent` 不再上报单键按下/释放,改为上报全键盘位图,格式与 `Bitmap` 一致 +- 顶层消息增加 `msg_id` / `reply_to` +- `Ack` 与 `Error` 合并为统一 `Response` +- `CDC` 与 `NUS` 均增加统一外层帧:`magic(2) + len(1) + protobuf` + +## 1. 总体原则 + +- 标准键盘输入继续走 `HID / BLE HID`,不变。 +- 私有通信继续保留两条通道: + - `USB`:走 `CDC` + - `BLE`:走 `NUS` +- 两条通道统一承载同一套 `protobuf` 业务消息。 +- `CDC` 与 `NUS` 均使用完全相同的外层帧格式,帧内承载 `protobuf` 业务消息。 +- 当前版本不做应用层分片。 + +说明: + +- 顶层 `protobuf` 消息名改为 `DeviceMessage`,表示独立于具体传输通道的统一业务信封。 +- 当前版本使用统一外层帧,`CDC` 与 `NUS` 都按完整帧进行发送与解析。 + +## 2. 共享业务消息 + +统一的 `protobuf` 业务消息为 `DeviceMessage`,内部 `oneof body` 可取以下类型: + +| 消息 | 方向 | 说明 | +| --- | --- | --- | +| `HelloReq` | Host -> Device | 握手请求 | +| `HelloRsp` | Device -> Host | 握手响应 | +| `Bitmap` | Host -> Device | 下发功能键位图配置 | +| `FunctionKeyEvent` | Device -> Host | 上报当前全键盘状态位图 | +| `LedState` | Device -> Host | 上报键盘 LED 状态 | +| `TimeSync` | Host -> Device | 下发时间同步 | +| `ThemeRgb` | Host -> Device | 下发主题颜色 | +| `Response` | Device -> Host | 控制命令统一响应 | + +顶层公共字段: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `msg_id` | `uint32` | 当前消息 ID;`0` 表示未使用 | +| `reply_to` | `uint32` | 当前消息响应的请求 ID;`0` 表示不是响应 | + +规则: + +- Host 发起的请求消息应填写唯一 `msg_id`。 +- `HelloRsp` 与 `Response` 应填写 `reply_to = 原请求.msg_id`。 +- `FunctionKeyEvent`、`LedState` 等异步上报消息应设置 `reply_to = 0`。 +- 当前版本建议每条链路同一时刻仅保留 `1` 条 in-flight 控制请求。 + +## 3. CDC 协议 + +CDC 在线路上的真实字节格式如下: + +```text +magic(2B) + len(1B) + protobuf(DeviceMessage) +``` + +约束: + +- `magic` 固定为 `0xAA55`,按小端发送,即线路字节序为 `0x55 0xAA`。 +- `len` 为后续 `protobuf(DeviceMessage)` 的字节长度,范围 `0..64`。 +- Host -> Device 与 Device -> Host 都发送完整帧。 +- 当前版本要求单条业务消息一次完整发送,不做应用层分片与重组。 + +## 4. NUS 协议 + +BLE 私有通信改为使用 `NUS (Nordic UART Service)`。 + +NUS 线路上的真实字节格式如下: + +```text +magic(2B) + len(1B) + protobuf(DeviceMessage) +``` + +方向定义: + +- Host -> Device:向 `RX` 特征写入完整帧 +- Device -> Host:通过 `TX Notify` 上报完整帧 + +## 5. NUS 服务定义 + +Service UUID: + +```text +6E400001-B5A3-F393-E0A9-E50E24DCCA9E +``` + +Characteristic 定义: + +| 名称 | UUID | 属性 | 用途 | +| --- | --- | --- | --- | +| `RX` | `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` | `Write`, `Write Without Response` | Host -> Device | +| `TX` | `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` | `Notify` | Device -> Host | + +约束: + +- 连接后主机必须先订阅 `TX`。 +- 当前版本不做应用层分片。 +- 因为增加了 `magic + len` 外层帧,建议协商 `ATT_MTU >= 64`,保证完整帧可一次发送。 + +## 6. 外层帧定义 + +外层帧字段如下: + +| 字段 | 大小 | 说明 | +| --- | --- | --- | +| `magic` | `2` 字节 | 固定 `0xAA55`,线路字节序 `55 AA` | +| `len` | `1` 字节 | `protobuf(DeviceMessage)` 长度,最大 `64` | +| `payload` | `len` 字节 | `protobuf(DeviceMessage)` | + +## 7. protobuf 字段定义 + +以下是业务字段语义。 + +### HelloReq + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `protocol_version` | `uint32` | 当前固定为 `1` | + +### HelloRsp + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `protocol_version` | `uint32` | 当前固定为 `1` | +| `vendor_id` | `uint32` | 当前建议 `0x1209` | +| `product_id` | `uint32` | 当前建议 `0x0001` | +| `firmware_major` | `uint32` | 固件主版本 | +| `firmware_minor` | `uint32` | 固件次版本 | +| `capability_flags` | `uint32` | 能力位 | + +### Bitmap + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `usage_bitmap` | `bytes` | 固定 29 字节,表示功能键配置位图 | + +语义: + +- `Bitmap.usage_bitmap` 长度固定为 `29` 字节。 +- 第 `0` 字节表示 `0xE0` 到 `0xE7` 这 8 个控制键。 +- 第 `1` 到第 `28` 字节表示 `0x00` 到 `0xDF`。 +- 位值为 `1` 表示该 usage 被配置为功能键。 +- 位值为 `0` 表示该 usage 不是功能键,继续按普通键处理。 + +位映射规则: + +- 第 `0` 字节: + - `bit0 -> 0xE0` + - `bit1 -> 0xE1` + - ... + - `bit7 -> 0xE7` +- 第 `1` 字节: + - `bit0 -> 0x00` + - `bit1 -> 0x01` + - ... + - `bit7 -> 0x07` +- 第 `2` 字节: + - `bit0 -> 0x08` + - ... + - `bit7 -> 0x0F` +- 依此类推。 +- 第 `28` 字节: + - `bit0 -> 0xD8` + - ... + - `bit7 -> 0xDF` + +### FunctionKeyEvent + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `usage_bitmap` | `bytes` | 固定 29 字节,表示当前全键盘状态位图 | + +语义: + +- `FunctionKeyEvent` 不再使用 `usage + action` 的单键事件格式。 +- `FunctionKeyEvent.usage_bitmap` 长度固定为 `29` 字节。 +- 位图编码方式与 `Bitmap.usage_bitmap` 完全一致。 +- 位值为 `1` 表示该 usage 当前处于按下状态。 +- 位值为 `0` 表示该 usage 当前处于释放状态。 +- 该消息表示一次完整键盘状态快照,而不是单个按键变化。 + +### LedState + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `led_mask` | `uint32` | NumLock/CapsLock 等位掩码 | + +### TimeSync + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `version` | `uint32` | 当前固定 `1` | +| `flags` | `uint32` | 标志位 | +| `timezone_min` | `sint32` | 时区偏移,单位分钟 | +| `utc_ms` | `fixed64` | UTC 毫秒时间戳 | +| `accuracy_ms` | `fixed32` | 精度,毫秒 | + +### ThemeRgb + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `red` | `uint32` | `0..255` | +| `green` | `uint32` | `0..255` | +| `blue` | `uint32` | `0..255` | + +### Response + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `error_code` | `enum ResponseCode` | 响应结果;`OK` 表示成功,其余表示失败原因 | + +## 8. 枚举定义 + +### ResponseCode + +| 值 | 含义 | +| ---: | --- | +| `0` | `OK` | +| `1` | `UNKNOWN_TYPE` | +| `2` | `INVALID_LENGTH` | +| `3` | `INVALID_PARAM` | +| `4` | `NOT_READY` | + +说明: + +- 原 `KeyAction` 枚举已不再使用,因为 `FunctionKeyEvent` 改为完整位图上报。 + +## 9. CapabilityFlags 定义 + +`HelloRsp.capability_flags` 先按以下位定义: + +| Bit | 含义 | +| ---: | --- | +| `0` | 支持功能位图配置 | +| `1` | 支持时间同步 | +| `2` | 支持主题切换 | +| `3` | 支持 LED 状态上报 | +| `4` | 支持全键盘位图事件上报 | + +当前建议最小返回值: + +- 若仅先做握手:可先返回 `0` +- 若 `Bitmap` 可用:打开 `bit0` +- 若 `FunctionKeyEvent` 全键盘位图上报可用:打开 `bit4` + +## 10. 握手流程 + +### CDC 握手 + +1. Host 打开串口 +2. Host 发送 `magic + len + protobuf(DeviceMessage{hello_req})` +3. Device 解析外层帧与 `protobuf(DeviceMessage)` +4. Device 取出 `hello_req` +5. Device 返回 `magic + len + protobuf(DeviceMessage{hello_rsp})` + +### NUS 握手 + +1. Host 连接 BLE +2. Host 发现 `NUS Service` +3. Host 订阅 `TX Notify` +4. Host 向 `RX` 写入完整帧 +5. Device 解析外层帧与 `protobuf(DeviceMessage)` +6. Device 通过 `TX Notify` 发回完整帧 + +说明: + +- 文中 `protobuf(HelloReq)` / `protobuf(HelloRsp)` 指业务上发送对应的 `DeviceMessage` 消息,其 `oneof body` 分别为 `hello_req` / `hello_rsp`。 + +## 11. 设备行为规则 + +- 上电默认所有按键都是普通键。 +- 普通键的标准输入行为继续走 `HID / BLE HID`,不变。 +- 收到 `Bitmap` 后,位图中标记为功能键的按键按功能键逻辑处理。 +- `FunctionKeyEvent` 上报的是当前全键盘状态快照,编码格式与 `Bitmap` 一致。 +- `LedState` 在 LED 状态变化时上报。 +- `TimeSync` 收到后更新时间管理模块。 +- `ThemeRgb` 收到后更新主题模块。 +- 控制类请求处理完成后统一返回 `Response`。 +- 不支持的消息类型也应返回 `Response { error_code = UNKNOWN_TYPE }`,不得静默丢弃。 + +## 12. Response 规则 + +- `HelloReq` 不返回 `Response`,而是直接返回 `HelloRsp`,且 `reply_to = HelloReq.msg_id`。 +- `Bitmap`、`TimeSync`、`ThemeRgb` 处理完成后应返回 `Response`。 +- `Response.error_code = OK` 表示成功。 +- 不支持的消息类型返回 `Response { error_code = UNKNOWN_TYPE }`。 +- 长度错误、参数错误、模块未就绪时分别返回 `INVALID_LENGTH`、`INVALID_PARAM`、`NOT_READY`。 + +示例: + +- 收到 `Bitmap` 成功处理 + 返回 `Response { error_code = OK }` +- 收到 `ThemeRgb` 参数非法 + 返回 `Response { error_code = INVALID_PARAM }` diff --git a/inc/events/proto_common.h b/inc/events/proto_common.h index f04c398..7b7c64e 100644 --- a/inc/events/proto_common.h +++ b/inc/events/proto_common.h @@ -16,6 +16,11 @@ enum proto_transport_link_state { PROTO_TRANSPORT_LINK_READY, }; +#define PROTO_FRAME_MAGIC 0xAA55U +#define PROTO_FRAME_HEADER_SIZE 3U +#define PROTO_MAX_PAYLOAD_LEN 64U +#define PROTO_MAX_FRAME_LEN (PROTO_FRAME_HEADER_SIZE + PROTO_MAX_PAYLOAD_LEN) + #ifdef __cplusplus } #endif diff --git a/inc/events/proto_rx_event.h b/inc/events/proto_rx_event.h index 394674f..b8e642d 100644 --- a/inc/events/proto_rx_event.h +++ b/inc/events/proto_rx_event.h @@ -28,7 +28,8 @@ static inline int submit_proto_rx_event(enum proto_transport transport, struct proto_rx_event *event; if ((transport >= PROTO_TRANSPORT_COUNT) || - ((data == NULL) && (len > 0U))) { + ((data == NULL) && (len > 0U)) || + (len > PROTO_MAX_FRAME_LEN)) { return -EINVAL; } diff --git a/inc/events/proto_tx_event.h b/inc/events/proto_tx_event.h index 8010ae5..0e14050 100644 --- a/inc/events/proto_tx_event.h +++ b/inc/events/proto_tx_event.h @@ -28,7 +28,8 @@ static inline int submit_proto_tx_event(enum proto_transport transport, struct proto_tx_event *event; if ((transport >= PROTO_TRANSPORT_COUNT) || - ((data == NULL) && (len > 0U))) { + ((data == NULL) && (len > 0U)) || + (len > PROTO_MAX_FRAME_LEN)) { return -EINVAL; } diff --git a/proto/device_comm.proto b/proto/device_comm.proto index 84da6c1..b302e3f 100644 --- a/proto/device_comm.proto +++ b/proto/device_comm.proto @@ -1,5 +1,13 @@ syntax = "proto3"; +enum ResponseCode { + RESPONSE_CODE_OK = 0; + RESPONSE_CODE_UNKNOWN_TYPE = 1; + RESPONSE_CODE_INVALID_LENGTH = 2; + RESPONSE_CODE_INVALID_PARAM = 3; + RESPONSE_CODE_NOT_READY = 4; +} + message HelloReq { uint32 protocol_version = 1; } @@ -39,24 +47,14 @@ message ThemeRgb { uint32 blue = 3; } -message Ack { - uint32 acked_type = 1; +message Response { + ResponseCode error_code = 1; } -enum ErrorCode { - ERROR_CODE_NONE = 0; - ERROR_CODE_UNKNOWN_TYPE = 1; - ERROR_CODE_INVALID_LENGTH = 2; - ERROR_CODE_INVALID_PARAM = 3; - ERROR_CODE_NOT_READY = 4; -} +message DeviceMessage { + uint32 msg_id = 10; + uint32 reply_to = 11; -message Error { - uint32 error_type = 1; - ErrorCode error_code = 2; -} - -message CdcPacketBody { oneof body { HelloReq hello_req = 1; HelloRsp hello_rsp = 2; @@ -65,7 +63,6 @@ message CdcPacketBody { LedState led_state = 5; TimeSync time_sync = 6; ThemeRgb theme_rgb = 7; - Ack ack = 8; - Error error = 9; + Response response = 8; } } diff --git a/src/ble_nus_module.c b/src/ble_nus_module.c index f856db5..0929b2f 100644 --- a/src/ble_nus_module.c +++ b/src/ble_nus_module.c @@ -174,6 +174,11 @@ static void received(struct bt_conn *conn, const void *data, uint16_t len, return; } + if ((len < PROTO_FRAME_HEADER_SIZE) || (len > PROTO_MAX_FRAME_LEN)) { + LOG_WRN("BLE NUS drop invalid framed RX len:%u", len); + return; + } + (void)submit_proto_rx_event(PROTO_TRANSPORT_BLE_NUS, data, len); } diff --git a/src/hid_flowctrl_module.c b/src/hid_flowctrl_module.c index ae2a905..7be1534 100644 --- a/src/hid_flowctrl_module.c +++ b/src/hid_flowctrl_module.c @@ -98,19 +98,6 @@ static struct hid_flowctrl_module_ctx ctx = { }, }; -#define lifecycle ctx.lc -#define channel_state ctx.channel_state -#define pending_keys ctx.pending_keys -#define pending_consumer_latest ctx.pending_consumer_latest -#define consumer_fifo ctx.consumer_fifo -#define consumer_fifo_head ctx.consumer_fifo_head -#define consumer_fifo_tail ctx.consumer_fifo_tail -#define consumer_fifo_count ctx.consumer_fifo_count -#define in_flight ctx.in_flight -#define current_transport ctx.current_transport -#define next_sequence ctx.next_sequence -#define running module_lifecycle_is_running(&ctx.lc) - static bool current_transport_to_channel(enum keyboard_report_type report_type, enum hid_send_channel *channel) { @@ -118,7 +105,7 @@ static bool current_transport_to_channel(enum keyboard_report_type report_type, return false; } - switch (current_transport) { + switch (ctx.current_transport) { case HID_TRANSPORT_POLICY_USB: *channel = (report_type == KEYBOARD_REPORT_TYPE_KEYS) ? HID_SEND_CH_USB_KEYS : @@ -136,44 +123,47 @@ static bool current_transport_to_channel(enum keyboard_report_type report_type, static void clear_pending_reports(void) { - memset(&pending_keys, 0, sizeof(pending_keys)); - memset(&pending_consumer_latest, 0, sizeof(pending_consumer_latest)); - consumer_fifo_head = 0U; - consumer_fifo_tail = 0U; - consumer_fifo_count = 0U; - memset(&in_flight, 0, sizeof(in_flight)); + memset(&ctx.pending_keys, 0, sizeof(ctx.pending_keys)); + memset(&ctx.pending_consumer_latest, 0, sizeof(ctx.pending_consumer_latest)); + ctx.consumer_fifo_head = 0U; + ctx.consumer_fifo_tail = 0U; + ctx.consumer_fifo_count = 0U; + memset(&ctx.in_flight, 0, sizeof(ctx.in_flight)); } static void consumer_fifo_push(enum keyboard_report_type report_type, enum keyboard_protocol_mode protocol_mode, const uint8_t *data, size_t size) { - if (consumer_fifo_count == HID_FLOWCTRL_FIFO_DEPTH) { + if (ctx.consumer_fifo_count == HID_FLOWCTRL_FIFO_DEPTH) { LOG_WRN("Consumer FIFO full, dropping oldest pulse"); - consumer_fifo_head = (consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH; - consumer_fifo_count--; + ctx.consumer_fifo_head = + (ctx.consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH; + ctx.consumer_fifo_count--; } - struct queued_report *entry = &consumer_fifo[consumer_fifo_tail]; + struct queued_report *entry = &ctx.consumer_fifo[ctx.consumer_fifo_tail]; entry->report_type = report_type; entry->protocol_mode = protocol_mode; entry->size = size; memcpy(entry->data, data, size); - consumer_fifo_tail = (consumer_fifo_tail + 1U) % HID_FLOWCTRL_FIFO_DEPTH; - consumer_fifo_count++; + ctx.consumer_fifo_tail = + (ctx.consumer_fifo_tail + 1U) % HID_FLOWCTRL_FIFO_DEPTH; + ctx.consumer_fifo_count++; } static bool consumer_fifo_pop(struct queued_report *entry) { - if (consumer_fifo_count == 0U) { + if (ctx.consumer_fifo_count == 0U) { return false; } - *entry = consumer_fifo[consumer_fifo_head]; - consumer_fifo_head = (consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH; - consumer_fifo_count--; + *entry = ctx.consumer_fifo[ctx.consumer_fifo_head]; + ctx.consumer_fifo_head = + (ctx.consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH; + ctx.consumer_fifo_count--; return true; } @@ -182,9 +172,9 @@ static bool channel_can_send_report(enum hid_send_channel channel, enum keyboard_report_type report_type, enum keyboard_protocol_mode protocol_mode) { - const struct hid_channel_state_data *state = &channel_state[channel]; + const struct hid_channel_state_data *state = &ctx.channel_state[channel]; - if (in_flight[channel].active) { + if (ctx.in_flight[channel].active) { return false; } @@ -205,7 +195,7 @@ static void try_send_keys(void) { enum hid_send_channel channel; - if (!pending_keys.valid) { + if (!ctx.pending_keys.valid) { return; } @@ -213,20 +203,20 @@ static void try_send_keys(void) return; } - if (!channel_can_send_report(channel, pending_keys.report_type, - pending_keys.protocol_mode)) { + if (!channel_can_send_report(channel, ctx.pending_keys.report_type, + ctx.pending_keys.protocol_mode)) { return; } - in_flight[channel].active = true; - in_flight[channel].channel = channel; - in_flight[channel].report_type = pending_keys.report_type; - in_flight[channel].sequence = next_sequence++; - (void)submit_hid_tx_report_event(channel, pending_keys.report_type, - pending_keys.protocol_mode, - in_flight[channel].sequence, - pending_keys.data, pending_keys.size); - pending_keys.valid = false; + ctx.in_flight[channel].active = true; + ctx.in_flight[channel].channel = channel; + ctx.in_flight[channel].report_type = ctx.pending_keys.report_type; + ctx.in_flight[channel].sequence = ctx.next_sequence++; + (void)submit_hid_tx_report_event(channel, ctx.pending_keys.report_type, + ctx.pending_keys.protocol_mode, + ctx.in_flight[channel].sequence, + ctx.pending_keys.data, ctx.pending_keys.size); + ctx.pending_keys.valid = false; } static void try_send_consumer_fifo(void) @@ -234,7 +224,7 @@ static void try_send_consumer_fifo(void) struct queued_report queued; enum hid_send_channel channel; - if (consumer_fifo_count == 0U) { + if (ctx.consumer_fifo_count == 0U) { return; } @@ -248,20 +238,20 @@ static void try_send_consumer_fifo(void) if (!channel_can_send_report(channel, queued.report_type, queued.protocol_mode)) { - if (queued.protocol_mode == channel_state[channel].protocol_mode) { + if (queued.protocol_mode == ctx.channel_state[channel].protocol_mode) { consumer_fifo_push(queued.report_type, queued.protocol_mode, queued.data, queued.size); } return; } - in_flight[channel].active = true; - in_flight[channel].channel = channel; - in_flight[channel].report_type = queued.report_type; - in_flight[channel].sequence = next_sequence++; + ctx.in_flight[channel].active = true; + ctx.in_flight[channel].channel = channel; + ctx.in_flight[channel].report_type = queued.report_type; + ctx.in_flight[channel].sequence = ctx.next_sequence++; (void)submit_hid_tx_report_event(channel, queued.report_type, queued.protocol_mode, - in_flight[channel].sequence, + ctx.in_flight[channel].sequence, queued.data, queued.size); } @@ -269,7 +259,7 @@ static void try_send_consumer_latest(void) { enum hid_send_channel channel; - if (!pending_consumer_latest.valid) { + if (!ctx.pending_consumer_latest.valid) { return; } @@ -278,27 +268,28 @@ static void try_send_consumer_latest(void) } if (!channel_can_send_report(channel, - pending_consumer_latest.report_type, - pending_consumer_latest.protocol_mode)) { + ctx.pending_consumer_latest.report_type, + ctx.pending_consumer_latest.protocol_mode)) { return; } - in_flight[channel].active = true; - in_flight[channel].channel = channel; - in_flight[channel].report_type = pending_consumer_latest.report_type; - in_flight[channel].sequence = next_sequence++; + ctx.in_flight[channel].active = true; + ctx.in_flight[channel].channel = channel; + ctx.in_flight[channel].report_type = + ctx.pending_consumer_latest.report_type; + ctx.in_flight[channel].sequence = ctx.next_sequence++; (void)submit_hid_tx_report_event(channel, - pending_consumer_latest.report_type, - pending_consumer_latest.protocol_mode, - in_flight[channel].sequence, - pending_consumer_latest.data, - pending_consumer_latest.size); - pending_consumer_latest.valid = false; + ctx.pending_consumer_latest.report_type, + ctx.pending_consumer_latest.protocol_mode, + ctx.in_flight[channel].sequence, + ctx.pending_consumer_latest.data, + ctx.pending_consumer_latest.size); + ctx.pending_consumer_latest.valid = false; } static void try_send_next(void) { - if (!running) { + if (!module_lifecycle_is_running(&ctx.lc)) { return; } @@ -310,7 +301,7 @@ static void try_send_next(void) static bool handle_keyboard_hid_report_event( const struct keyboard_hid_report_event *event) { - if (!running || + if (!module_lifecycle_is_running(&ctx.lc) || ((event->mode != MODE_SWITCH_USB) && (event->mode != MODE_SWITCH_BLE))) { return false; } @@ -321,17 +312,18 @@ static bool handle_keyboard_hid_report_event( event->dyndata.data, event->dyndata.size); } else if (event->report_type == KEYBOARD_REPORT_TYPE_KEYS) { - pending_keys.valid = true; - pending_keys.report_type = event->report_type; - pending_keys.protocol_mode = event->protocol_mode; - pending_keys.size = event->dyndata.size; - memcpy(pending_keys.data, event->dyndata.data, event->dyndata.size); + ctx.pending_keys.valid = true; + ctx.pending_keys.report_type = event->report_type; + ctx.pending_keys.protocol_mode = event->protocol_mode; + ctx.pending_keys.size = event->dyndata.size; + memcpy(ctx.pending_keys.data, event->dyndata.data, + event->dyndata.size); } else { - pending_consumer_latest.valid = true; - pending_consumer_latest.report_type = event->report_type; - pending_consumer_latest.protocol_mode = event->protocol_mode; - pending_consumer_latest.size = event->dyndata.size; - memcpy(pending_consumer_latest.data, event->dyndata.data, + ctx.pending_consumer_latest.valid = true; + ctx.pending_consumer_latest.report_type = event->report_type; + ctx.pending_consumer_latest.protocol_mode = event->protocol_mode; + ctx.pending_consumer_latest.size = event->dyndata.size; + memcpy(ctx.pending_consumer_latest.data, event->dyndata.data, event->dyndata.size); } @@ -346,11 +338,12 @@ static bool handle_hid_channel_state_event( return false; } - channel_state[event->channel].report_ready_bm = event->report_ready_bm; - channel_state[event->channel].protocol_mode = event->protocol_mode; + ctx.channel_state[event->channel].report_ready_bm = + event->report_ready_bm; + ctx.channel_state[event->channel].protocol_mode = event->protocol_mode; if (event->report_ready_bm == 0U) { - in_flight[event->channel].active = false; + ctx.in_flight[event->channel].active = false; } try_send_next(); @@ -363,17 +356,17 @@ static bool handle_hid_report_sent_event(const struct hid_report_sent_event *eve return false; } - if (!in_flight[event->channel].active) { + if (!ctx.in_flight[event->channel].active) { return false; } - if (event->sequence != in_flight[event->channel].sequence) { + if (event->sequence != ctx.in_flight[event->channel].sequence) { LOG_WRN("Unexpected HID sent sequence %u (expected %u)", - event->sequence, in_flight[event->channel].sequence); + event->sequence, ctx.in_flight[event->channel].sequence); return false; } - in_flight[event->channel].active = false; + ctx.in_flight[event->channel].active = false; if (event->error) { LOG_WRN("HID report send failed for seq %u", event->sequence); @@ -386,12 +379,13 @@ static bool handle_hid_report_sent_event(const struct hid_report_sent_event *eve static bool handle_transport_policy_event( const struct transport_policy_event *event) { - bool transport_changed = (current_transport != event->hid_transport); + bool transport_changed = + (ctx.current_transport != event->hid_transport); - current_transport = event->hid_transport; + ctx.current_transport = event->hid_transport; if (transport_changed || - (current_transport == HID_TRANSPORT_POLICY_NONE)) { + (ctx.current_transport == HID_TRANSPORT_POLICY_NONE)) { clear_pending_reports(); } @@ -402,21 +396,21 @@ static bool handle_transport_policy_event( static int do_init(void) { clear_pending_reports(); - current_transport = HID_TRANSPORT_POLICY_USB; - memset(channel_state, 0, sizeof(channel_state)); - channel_state[HID_SEND_CH_USB_KEYS].protocol_mode = + ctx.current_transport = HID_TRANSPORT_POLICY_USB; + memset(ctx.channel_state, 0, sizeof(ctx.channel_state)); + ctx.channel_state[HID_SEND_CH_USB_KEYS].protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; - channel_state[HID_SEND_CH_USB_CONSUMER].protocol_mode = + ctx.channel_state[HID_SEND_CH_USB_CONSUMER].protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; - channel_state[HID_SEND_CH_BLE_SHARED].protocol_mode = + ctx.channel_state[HID_SEND_CH_BLE_SHARED].protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; - next_sequence = 1U; + ctx.next_sequence = 1U; return 0; } static int do_start(void) { - if (running) { + if (module_lifecycle_is_running(&ctx.lc)) { return 0; } @@ -426,7 +420,7 @@ static int do_start(void) static int do_stop(void) { - if (!running) { + if (!module_lifecycle_is_running(&ctx.lc)) { return 0; } @@ -462,23 +456,23 @@ static bool app_event_handler(const struct app_event_header *aeh) cast_module_state_event(aeh); if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { - (void)module_set_lifecycle(&lifecycle, LC_RUNNING); + (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); } return false; } if (is_power_down_event(aeh)) { - if (module_lifecycle_is_initialized(&lifecycle)) { - (void)module_set_lifecycle(&lifecycle, LC_STOPPED); + if (module_lifecycle_is_initialized(&ctx.lc)) { + (void)module_set_lifecycle(&ctx.lc, LC_STOPPED); } return false; } if (is_wake_up_event(aeh)) { - if (module_lifecycle_is_initialized(&lifecycle)) { - (void)module_set_lifecycle(&lifecycle, LC_RUNNING); + if (module_lifecycle_is_initialized(&ctx.lc)) { + (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); } return false; diff --git a/src/keyboard_core_module.c b/src/keyboard_core_module.c index b5fff6c..8b15dc9 100644 --- a/src/keyboard_core_module.c +++ b/src/keyboard_core_module.c @@ -125,16 +125,6 @@ static struct keyboard_core_module_ctx ctx = { }, }; -#define lifecycle ctx.lc -#define keyboard_state ctx.keyboard_state -#define reports_cache ctx.reports_cache -#define function_usage_mask ctx.function_usage_mask -#define transport_protocol_modes ctx.transport_protocol_modes -#define current_transport ctx.current_transport -#define mode_valid ctx.mode_valid -#define settings_active ctx.settings_active -#define running module_lifecycle_is_running(&ctx.lc) - static bool policy_to_transport(enum hid_transport_policy policy, enum hid_transport *transport) { @@ -156,8 +146,9 @@ static enum keyboard_protocol_mode active_protocol_mode_get(void) { enum hid_transport transport; - if (mode_valid && policy_to_transport(current_transport, &transport)) { - return transport_protocol_modes[transport]; + if (ctx.mode_valid && + policy_to_transport(ctx.current_transport, &transport)) { + return ctx.transport_protocol_modes[transport]; } return KEYBOARD_PROTOCOL_MODE_REPORT; @@ -269,38 +260,39 @@ static bool consumer_key_update(uint16_t consumer_id, bool pressed) return false; } - bool was_pressed = (keyboard_state.consumer_bits & BIT(consumer_id)) != 0U; + bool was_pressed = + (ctx.keyboard_state.consumer_bits & BIT(consumer_id)) != 0U; if (was_pressed == pressed) { return false; } - WRITE_BIT(keyboard_state.consumer_bits, consumer_id, pressed); + WRITE_BIT(ctx.keyboard_state.consumer_bits, consumer_id, pressed); return true; } static void keyboard_state_clear(void) { - memset(&keyboard_state, 0, sizeof(keyboard_state)); + memset(&ctx.keyboard_state, 0, sizeof(ctx.keyboard_state)); } static void function_usage_mask_clear(void) { - memset(function_usage_mask, 0, sizeof(function_usage_mask)); + memset(ctx.function_usage_mask, 0, sizeof(ctx.function_usage_mask)); } static void reports_cache_invalidate(void) { - reports_cache.boot_valid = false; - reports_cache.nkro_valid = false; - reports_cache.consumer_valid = false; + ctx.reports_cache.boot_valid = false; + ctx.reports_cache.nkro_valid = false; + ctx.reports_cache.consumer_valid = false; } static void build_effective_hid_bitmap(uint8_t bitmap[KEYBOARD_PROTOCOL_BITMAP_BYTES]) { for (size_t i = 0; i < KEYBOARD_PROTOCOL_BITMAP_BYTES; i++) { - bitmap[i] = keyboard_state.pressed_usage_bitmap[i] & - (uint8_t)~keyboard_state.function_pressed_bitmap[i]; + bitmap[i] = ctx.keyboard_state.pressed_usage_bitmap[i] & + (uint8_t)~ctx.keyboard_state.function_pressed_bitmap[i]; } } @@ -347,7 +339,7 @@ static void build_nkro_report(uint8_t report[KEYBOARD_NKRO_REPORT_SIZE]) static uint16_t active_consumer_usage_get(void) { for (uint8_t consumer_id = 0; consumer_id < KEYBOARD_CONSUMER_CTRL_COUNT; consumer_id++) { - if ((keyboard_state.consumer_bits & BIT(consumer_id)) != 0U) { + if ((ctx.keyboard_state.consumer_bits & BIT(consumer_id)) != 0U) { return consumer_usage_map[consumer_id]; } } @@ -366,9 +358,9 @@ static void submit_consumer_fifo_frame(uint16_t usage_id) enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get(); enum mode_switch_mode mode; - if (!running || !mode_valid || - settings_active || - !transport_policy_to_mode(current_transport, &mode) || + if (!module_lifecycle_is_running(&ctx.lc) || !ctx.mode_valid || + ctx.settings_active || + !transport_policy_to_mode(ctx.current_transport, &mode) || (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) { return; } @@ -414,24 +406,25 @@ static void emit_keys_report(bool force) enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get(); enum mode_switch_mode mode; - if (!mode_valid || !transport_policy_to_mode(current_transport, &mode)) { + if (!ctx.mode_valid || + !transport_policy_to_mode(ctx.current_transport, &mode)) { return; } - if (settings_active) { + if (ctx.settings_active) { return; } if (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) { build_boot_report(report_buf); report_size = KEYBOARD_BOOT_REPORT_SIZE; - cache_buf = reports_cache.boot_report; - cache_valid = &reports_cache.boot_valid; + cache_buf = ctx.reports_cache.boot_report; + cache_valid = &ctx.reports_cache.boot_valid; } else { build_nkro_report(report_buf); report_size = KEYBOARD_NKRO_REPORT_SIZE; - cache_buf = reports_cache.nkro_report; - cache_valid = &reports_cache.nkro_valid; + cache_buf = ctx.reports_cache.nkro_report; + cache_valid = &ctx.reports_cache.nkro_valid; } if (!force && *cache_valid && (memcmp(cache_buf, report_buf, report_size) == 0)) { @@ -452,21 +445,23 @@ static void emit_consumer_report(bool force) enum keyboard_protocol_mode protocol_mode = active_protocol_mode_get(); enum mode_switch_mode mode; - if (!mode_valid || !transport_policy_to_mode(current_transport, &mode) || - settings_active || + if (!ctx.mode_valid || + !transport_policy_to_mode(ctx.current_transport, &mode) || + ctx.settings_active || (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT)) { return; } build_consumer_report(report_buf); - if (!force && reports_cache.consumer_valid && - (memcmp(reports_cache.consumer_report, report_buf, + if (!force && ctx.reports_cache.consumer_valid && + (memcmp(ctx.reports_cache.consumer_report, report_buf, KEYBOARD_CONSUMER_REPORT_SIZE) == 0)) { return; } - memcpy(reports_cache.consumer_report, report_buf, KEYBOARD_CONSUMER_REPORT_SIZE); - reports_cache.consumer_valid = true; + memcpy(ctx.reports_cache.consumer_report, report_buf, + KEYBOARD_CONSUMER_REPORT_SIZE); + ctx.reports_cache.consumer_valid = true; (void)submit_keyboard_hid_report_event( mode, KEYBOARD_REPORT_TYPE_CONSUMER, protocol_mode, @@ -485,7 +480,8 @@ static void emit_all_reports(bool force) static void emit_function_state_event(void) { - (void)submit_function_bitmap_state_event(keyboard_state.pressed_usage_bitmap); + (void)submit_function_bitmap_state_event( + ctx.keyboard_state.pressed_usage_bitmap); } static void emit_release_reports(enum hid_transport_policy transport_policy) @@ -526,11 +522,11 @@ static int do_init(void) keyboard_state_clear(); reports_cache_invalidate(); function_usage_mask_clear(); - mode_valid = false; - settings_active = false; - transport_protocol_modes[HID_TRANSPORT_USB] = + ctx.mode_valid = false; + ctx.settings_active = false; + ctx.transport_protocol_modes[HID_TRANSPORT_USB] = KEYBOARD_PROTOCOL_MODE_REPORT; - transport_protocol_modes[HID_TRANSPORT_BLE] = + ctx.transport_protocol_modes[HID_TRANSPORT_BLE] = KEYBOARD_PROTOCOL_MODE_REPORT; return 0; @@ -538,7 +534,7 @@ static int do_init(void) static int do_start(void) { - if (running) { + if (module_lifecycle_is_running(&ctx.lc)) { return 0; } @@ -547,18 +543,18 @@ static int do_start(void) static int do_stop(void) { - if (!running) { + if (!module_lifecycle_is_running(&ctx.lc)) { return 0; } - if (mode_valid) { - emit_release_reports(current_transport); + if (ctx.mode_valid) { + emit_release_reports(ctx.current_transport); } emit_function_state_event(); keyboard_state_clear(); reports_cache_invalidate(); - mode_valid = false; + ctx.mode_valid = false; return 0; } @@ -568,11 +564,11 @@ static bool handle_button_event(const struct button_event *event) const struct keymap_entry *entry; bool changed; - if (!running) { + if (!module_lifecycle_is_running(&ctx.lc)) { return false; } - if (settings_active) { + if (ctx.settings_active) { return false; } @@ -585,7 +581,7 @@ static bool handle_button_event(const struct button_event *event) if (entry->usage_type == KEY_USAGE_TYPE_KEYBOARD) { bool routed_to_function; - changed = usage_bitmap_write(keyboard_state.pressed_usage_bitmap, + changed = usage_bitmap_write(ctx.keyboard_state.pressed_usage_bitmap, entry->usage_id, event->pressed); if (!changed) { return false; @@ -593,14 +589,14 @@ static bool handle_button_event(const struct button_event *event) if (event->pressed) { routed_to_function = - usage_bitmap_test(function_usage_mask, entry->usage_id); - (void)usage_bitmap_write(keyboard_state.function_pressed_bitmap, + usage_bitmap_test(ctx.function_usage_mask, entry->usage_id); + (void)usage_bitmap_write(ctx.keyboard_state.function_pressed_bitmap, entry->usage_id, routed_to_function); } else { routed_to_function = - usage_bitmap_test(keyboard_state.function_pressed_bitmap, + usage_bitmap_test(ctx.keyboard_state.function_pressed_bitmap, entry->usage_id); - (void)usage_bitmap_write(keyboard_state.function_pressed_bitmap, + (void)usage_bitmap_write(ctx.keyboard_state.function_pressed_bitmap, entry->usage_id, false); } @@ -624,23 +620,24 @@ static bool handle_transport_policy_event( { bool transport_changed; - if (!running) { - current_transport = event->hid_transport; + if (!module_lifecycle_is_running(&ctx.lc)) { + ctx.current_transport = event->hid_transport; return false; } - transport_changed = mode_valid && (current_transport != event->hid_transport); + transport_changed = + ctx.mode_valid && (ctx.current_transport != event->hid_transport); if (transport_changed) { - emit_release_reports(current_transport); + emit_release_reports(ctx.current_transport); emit_function_state_event(); keyboard_state_clear(); reports_cache_invalidate(); } - current_transport = event->hid_transport; - mode_valid = (current_transport != HID_TRANSPORT_POLICY_NONE); + ctx.current_transport = event->hid_transport; + ctx.mode_valid = (ctx.current_transport != HID_TRANSPORT_POLICY_NONE); - if (mode_valid) { + if (ctx.mode_valid) { emit_all_reports(true); } @@ -649,11 +646,11 @@ static bool handle_transport_policy_event( static bool handle_encoder_event(const struct encoder_event *event) { - if (!running || !mode_valid) { + if (!module_lifecycle_is_running(&ctx.lc) || !ctx.mode_valid) { return false; } - if (settings_active) { + if (ctx.settings_active) { return false; } @@ -671,7 +668,8 @@ static bool handle_encoder_event(const struct encoder_event *event) static bool handle_function_bitmap_update_event( const struct function_bitmap_update_event *event) { - memcpy(function_usage_mask, event->bitmap, sizeof(function_usage_mask)); + memcpy(ctx.function_usage_mask, event->bitmap, + sizeof(ctx.function_usage_mask)); return false; } @@ -698,11 +696,13 @@ static bool app_event_handler(const struct app_event_header *aeh) return false; } - if (transport_protocol_modes[event->transport] != event->protocol_mode) { - transport_protocol_modes[event->transport] = event->protocol_mode; + if (ctx.transport_protocol_modes[event->transport] != + event->protocol_mode) { + ctx.transport_protocol_modes[event->transport] = + event->protocol_mode; - if (running && mode_valid && - policy_to_transport(current_transport, &active_transport) && + if (module_lifecycle_is_running(&ctx.lc) && ctx.mode_valid && + policy_to_transport(ctx.current_transport, &active_transport) && (active_transport == event->transport)) { reports_cache_invalidate(); emit_keys_report(true); @@ -725,14 +725,14 @@ static bool app_event_handler(const struct app_event_header *aeh) const struct settings_mode_event *event = cast_settings_mode_event(aeh); - if (settings_active == event->active) { + if (ctx.settings_active == event->active) { return false; } - settings_active = event->active; - if (settings_active) { - if (mode_valid) { - emit_release_reports(current_transport); + ctx.settings_active = event->active; + if (ctx.settings_active) { + if (ctx.mode_valid) { + emit_release_reports(ctx.current_transport); } emit_function_state_event(); keyboard_state_clear(); @@ -746,23 +746,23 @@ static bool app_event_handler(const struct app_event_header *aeh) const struct module_state_event *event = cast_module_state_event(aeh); if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { - (void)module_set_lifecycle(&lifecycle, LC_RUNNING); + (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); } return false; } if (is_power_down_event(aeh)) { - if (module_lifecycle_is_initialized(&lifecycle)) { - (void)module_set_lifecycle(&lifecycle, LC_STOPPED); + if (module_lifecycle_is_initialized(&ctx.lc)) { + (void)module_set_lifecycle(&ctx.lc, LC_STOPPED); } return false; } if (is_wake_up_event(aeh)) { - if (module_lifecycle_is_initialized(&lifecycle)) { - (void)module_set_lifecycle(&lifecycle, LC_RUNNING); + if (module_lifecycle_is_initialized(&ctx.lc)) { + (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); } return false; diff --git a/src/protocol_module.c b/src/protocol_module.c index 9b1c33e..3f637bd 100644 --- a/src/protocol_module.c +++ b/src/protocol_module.c @@ -36,7 +36,7 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define PROTOCOL_FIRMWARE_MAJOR 0U #define PROTOCOL_FIRMWARE_MINOR 0U #define PROTOCOL_CAPABILITY_FLAGS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4)) -#define PROTOCOL_MAX_MSG_LEN 128U +#define PROTOCOL_MAX_MSG_LEN PROTO_MAX_FRAME_LEN enum proto_session_state { PROTO_SESSION_DOWN = 0, @@ -72,8 +72,6 @@ static struct protocol_module_ctx ctx = { }, }; -#define session_state ctx.session_state - static const char *proto_session_name(enum proto_session_state state) { switch (state) { @@ -110,7 +108,7 @@ static void proto_session_set(enum proto_transport transport, return; } - old_state = session_state[transport]; + old_state = ctx.session_state[transport]; if (old_state == new_state) { LOG_INF("Protocol session keep transport:%s state:%s reason:%s", proto_transport_name(transport), @@ -118,14 +116,14 @@ static void proto_session_set(enum proto_transport transport, return; } - session_state[transport] = new_state; + ctx.session_state[transport] = new_state; LOG_INF("Protocol session transport:%s %s -> %s reason:%s", proto_transport_name(transport), proto_session_name(old_state), proto_session_name(new_state), reason); } static int decode_body(const uint8_t *payload, size_t payload_len, - CdcPacketBody *body) + DeviceMessage *body) { pb_istream_t stream; @@ -133,10 +131,10 @@ static int decode_body(const uint8_t *payload, size_t payload_len, return -EINVAL; } - *body = (CdcPacketBody)CdcPacketBody_init_zero; + *body = (DeviceMessage)DeviceMessage_init_zero; stream = pb_istream_from_buffer(payload, payload_len); - if (!pb_decode(&stream, CdcPacketBody_fields, body)) { + if (!pb_decode(&stream, DeviceMessage_fields, body)) { LOG_WRN("pb_decode failed: %s", PB_GET_ERROR(&stream)); return -EBADMSG; } @@ -144,7 +142,37 @@ static int decode_body(const uint8_t *payload, size_t payload_len, return 0; } -static int encode_body(const CdcPacketBody *body, uint8_t *payload, +static int decode_frame(const uint8_t *frame, size_t frame_len, + const uint8_t **payload, size_t *payload_len) +{ + uint16_t magic; + uint8_t len; + + if ((frame == NULL) || (payload == NULL) || (payload_len == NULL)) { + return -EINVAL; + } + + if (frame_len < PROTO_FRAME_HEADER_SIZE) { + return -EBADMSG; + } + + magic = (uint16_t)frame[0] | ((uint16_t)frame[1] << 8); + if (magic != PROTO_FRAME_MAGIC) { + return -EBADMSG; + } + + len = frame[2]; + if ((len > PROTO_MAX_PAYLOAD_LEN) || + (frame_len != (PROTO_FRAME_HEADER_SIZE + len))) { + return -EBADMSG; + } + + *payload = &frame[PROTO_FRAME_HEADER_SIZE]; + *payload_len = len; + return 0; +} + +static int encode_body(const DeviceMessage *body, uint8_t *payload, size_t payload_buf_size, size_t *payload_len) { pb_ostream_t stream; @@ -154,7 +182,7 @@ static int encode_body(const CdcPacketBody *body, uint8_t *payload, } stream = pb_ostream_from_buffer(payload, payload_buf_size); - if (!pb_encode(&stream, CdcPacketBody_fields, body)) { + if (!pb_encode(&stream, DeviceMessage_fields, body)) { LOG_WRN("pb_encode failed: %s", PB_GET_ERROR(&stream)); return -EIO; } @@ -163,12 +191,45 @@ static int encode_body(const CdcPacketBody *body, uint8_t *payload, return 0; } -static int encode_hello_rsp(uint8_t *rsp_payload, size_t rsp_payload_buf_size, +static int encode_frame(const DeviceMessage *body, uint8_t *frame, + size_t frame_buf_size, size_t *frame_len) +{ + size_t payload_len; + int err; + + if ((frame == NULL) || (frame_len == NULL)) { + return -EINVAL; + } + + if (frame_buf_size < PROTO_FRAME_HEADER_SIZE) { + return -ENOSPC; + } + + err = encode_body(body, &frame[PROTO_FRAME_HEADER_SIZE], + frame_buf_size - PROTO_FRAME_HEADER_SIZE, &payload_len); + if (err) { + return err; + } + + if (payload_len > PROTO_MAX_PAYLOAD_LEN) { + return -EMSGSIZE; + } + + frame[0] = (uint8_t)(PROTO_FRAME_MAGIC & 0xFFU); + frame[1] = (uint8_t)(PROTO_FRAME_MAGIC >> 8); + frame[2] = (uint8_t)payload_len; + *frame_len = PROTO_FRAME_HEADER_SIZE + payload_len; + return 0; +} + +static int encode_hello_rsp(uint32_t reply_to, uint8_t *rsp_payload, + size_t rsp_payload_buf_size, size_t *rsp_payload_len) { - CdcPacketBody body = CdcPacketBody_init_zero; + DeviceMessage body = DeviceMessage_init_zero; - body.which_body = CdcPacketBody_hello_rsp_tag; + body.reply_to = reply_to; + body.which_body = DeviceMessage_hello_rsp_tag; body.body.hello_rsp.protocol_version = PROTOCOL_VERSION; body.body.hello_rsp.vendor_id = PROTOCOL_VENDOR_ID; body.body.hello_rsp.product_id = PROTOCOL_PRODUCT_ID; @@ -176,66 +237,55 @@ static int encode_hello_rsp(uint8_t *rsp_payload, size_t rsp_payload_buf_size, body.body.hello_rsp.firmware_minor = PROTOCOL_FIRMWARE_MINOR; body.body.hello_rsp.capability_flags = PROTOCOL_CAPABILITY_FLAGS; - return encode_body(&body, rsp_payload, rsp_payload_buf_size, rsp_payload_len); + return encode_frame(&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) +static int encode_response(uint32_t reply_to, ResponseCode error_code, + uint8_t *rsp_payload, size_t rsp_payload_buf_size, + size_t *rsp_payload_len) { - CdcPacketBody body = CdcPacketBody_init_zero; + DeviceMessage body = DeviceMessage_init_zero; - body.which_body = CdcPacketBody_ack_tag; - body.body.ack.acked_type = acked_type; + body.reply_to = reply_to; + body.which_body = DeviceMessage_response_tag; + body.body.response.error_code = error_code; - 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); + return encode_frame(&body, rsp_payload, rsp_payload_buf_size, rsp_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; + DeviceMessage body = DeviceMessage_init_zero; - body.which_body = CdcPacketBody_led_state_tag; + body.which_body = DeviceMessage_led_state_tag; body.body.led_state.led_mask = led_mask; - return encode_body(&body, payload, payload_buf_size, payload_len); + return encode_frame(&body, payload, payload_buf_size, payload_len); } static int encode_function_bitmap_state(const uint8_t *bitmap, uint8_t *payload, size_t payload_buf_size, size_t *payload_len) { - CdcPacketBody body = CdcPacketBody_init_zero; + DeviceMessage body = DeviceMessage_init_zero; if (bitmap == NULL) { return -EINVAL; } - body.which_body = CdcPacketBody_function_key_event_tag; + body.which_body = DeviceMessage_function_key_event_tag; body.body.function_key_event.usage_bitmap.size = KEYBOARD_PROTOCOL_BITMAP_BYTES; memcpy(body.body.function_key_event.usage_bitmap.bytes, bitmap, KEYBOARD_PROTOCOL_BITMAP_BYTES); - return encode_body(&body, payload, payload_buf_size, payload_len); + return encode_frame(&body, payload, payload_buf_size, payload_len); } static int do_init(void) { - for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) { - session_state[i] = PROTO_SESSION_DOWN; + for (size_t i = 0; i < ARRAY_SIZE(ctx.session_state); i++) { + ctx.session_state[i] = PROTO_SESSION_DOWN; } LOG_INF("Protocol init done"); @@ -258,7 +308,7 @@ static int do_stop(void) return 0; } - for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) { + for (size_t i = 0; i < ARRAY_SIZE(ctx.session_state); i++) { proto_session_set((enum proto_transport)i, PROTO_SESSION_DOWN, "module_stop"); } @@ -273,7 +323,9 @@ int protocol_module_process_message(enum proto_transport transport, size_t rsp_payload_buf_size, size_t *rsp_payload_len) { - CdcPacketBody body; + DeviceMessage body; + const uint8_t *payload; + size_t payload_len; int err; if ((transport >= PROTO_TRANSPORT_COUNT) || @@ -288,17 +340,22 @@ int protocol_module_process_message(enum proto_transport transport, return -EAGAIN; } - err = decode_body(req_payload, req_payload_len, &body); + err = decode_frame(req_payload, req_payload_len, &payload, &payload_len); + if (err) { + return err; + } + + err = decode_body(payload, payload_len, &body); if (err) { return err; } switch (body.which_body) { - case CdcPacketBody_hello_req_tag: - if (session_state[transport] == PROTO_SESSION_DOWN) { + case DeviceMessage_hello_req_tag: + if (ctx.session_state[transport] == PROTO_SESSION_DOWN) { LOG_WRN("Reject HelloReq transport:%s session:%s lc:%s", proto_transport_name(transport), - proto_session_name(session_state[transport]), + proto_session_name(ctx.session_state[transport]), module_lifecycle_name(ctx.lc.state)); return -EAGAIN; } @@ -312,48 +369,47 @@ int protocol_module_process_message(enum proto_transport transport, } proto_session_set(transport, PROTO_SESSION_ACTIVE, "hello_req"); - return encode_hello_rsp(rsp_payload, rsp_payload_buf_size, rsp_payload_len); + return encode_hello_rsp(body.msg_id, rsp_payload, rsp_payload_buf_size, + rsp_payload_len); - case CdcPacketBody_bitmap_tag: - if (session_state[transport] != PROTO_SESSION_ACTIVE) { - return encode_error(CdcPacketBody_bitmap_tag, - ErrorCode_ERROR_CODE_NOT_READY, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + case DeviceMessage_bitmap_tag: + if (ctx.session_state[transport] != PROTO_SESSION_ACTIVE) { + return encode_response(body.msg_id, + ResponseCode_RESPONSE_CODE_NOT_READY, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); } if (body.body.bitmap.usage_bitmap.size != KEYBOARD_PROTOCOL_BITMAP_BYTES) { - return encode_error(CdcPacketBody_bitmap_tag, - ErrorCode_ERROR_CODE_INVALID_LENGTH, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + return encode_response( + body.msg_id, ResponseCode_RESPONSE_CODE_INVALID_LENGTH, + 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(CdcPacketBody_bitmap_tag, - ErrorCode_ERROR_CODE_INVALID_PARAM, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + return encode_response( + body.msg_id, ResponseCode_RESPONSE_CODE_INVALID_PARAM, + rsp_payload, rsp_payload_buf_size, rsp_payload_len); } - return encode_ack(CdcPacketBody_bitmap_tag, rsp_payload, - rsp_payload_buf_size, rsp_payload_len); + return encode_response(body.msg_id, ResponseCode_RESPONSE_CODE_OK, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); - case CdcPacketBody_time_sync_tag: - if (session_state[transport] != PROTO_SESSION_ACTIVE) { - return encode_error(CdcPacketBody_time_sync_tag, - ErrorCode_ERROR_CODE_NOT_READY, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + case DeviceMessage_time_sync_tag: + if (ctx.session_state[transport] != PROTO_SESSION_ACTIVE) { + return encode_response(body.msg_id, + ResponseCode_RESPONSE_CODE_NOT_READY, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); } if (body.body.time_sync.version != 1U) { - return encode_error(CdcPacketBody_time_sync_tag, - ErrorCode_ERROR_CODE_INVALID_PARAM, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + return encode_response( + body.msg_id, ResponseCode_RESPONSE_CODE_INVALID_PARAM, + rsp_payload, rsp_payload_buf_size, rsp_payload_len); } submit_time_sync_event(body.body.time_sync.version, @@ -361,24 +417,24 @@ int protocol_module_process_message(enum proto_transport transport, body.body.time_sync.timezone_min, body.body.time_sync.utc_ms, body.body.time_sync.accuracy_ms); - return encode_ack(CdcPacketBody_time_sync_tag, rsp_payload, - rsp_payload_buf_size, rsp_payload_len); + return encode_response(body.msg_id, ResponseCode_RESPONSE_CODE_OK, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); - case CdcPacketBody_theme_rgb_tag: - if (session_state[transport] != PROTO_SESSION_ACTIVE) { - return encode_error(CdcPacketBody_theme_rgb_tag, - ErrorCode_ERROR_CODE_NOT_READY, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + case DeviceMessage_theme_rgb_tag: + if (ctx.session_state[transport] != PROTO_SESSION_ACTIVE) { + return encode_response(body.msg_id, + ResponseCode_RESPONSE_CODE_NOT_READY, + 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(CdcPacketBody_theme_rgb_tag, - ErrorCode_ERROR_CODE_INVALID_PARAM, - rsp_payload, rsp_payload_buf_size, - rsp_payload_len); + return encode_response( + body.msg_id, ResponseCode_RESPONSE_CODE_INVALID_PARAM, + rsp_payload, rsp_payload_buf_size, rsp_payload_len); } submit_theme_rgb_update_event((struct theme_rgb) { @@ -386,12 +442,16 @@ int protocol_module_process_message(enum proto_transport transport, .g = (uint8_t)body.body.theme_rgb.green, .b = (uint8_t)body.body.theme_rgb.blue, }); - return encode_ack(CdcPacketBody_theme_rgb_tag, rsp_payload, - rsp_payload_buf_size, rsp_payload_len); + return encode_response(body.msg_id, ResponseCode_RESPONSE_CODE_OK, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); default: LOG_WRN("Unsupported protobuf body case %d", body.which_body); - return -ENOTSUP; + return encode_response(body.msg_id, + ResponseCode_RESPONSE_CODE_UNKNOWN_TYPE, + rsp_payload, rsp_payload_buf_size, + rsp_payload_len); } } @@ -415,7 +475,7 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event) if (err != -ENOTSUP) { LOG_WRN("Protocol processing failed (%d) transport:%s session:%s lc:%s len:%u", err, proto_transport_name(event->transport), - proto_session_name(session_state[event->transport]), + proto_session_name(ctx.session_state[event->transport]), module_lifecycle_name(ctx.lc.state), (uint32_t)event->dyndata.size); } @@ -425,7 +485,7 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event) LOG_INF("Protocol response ready transport:%s rsp_len:%u session:%s", proto_transport_name(event->transport), (unsigned int)rsp_payload_len, - proto_session_name(session_state[event->transport])); + proto_session_name(ctx.session_state[event->transport])); err = submit_proto_tx_event(event->transport, rsp_payload, rsp_payload_len); if (err) { LOG_WRN("Proto TX submit failed (%d)", err); @@ -472,10 +532,10 @@ static bool handle_function_bitmap_state_event( for (enum proto_transport transport = 0; transport < PROTO_TRANSPORT_COUNT; transport++) { - if (session_state[transport] != PROTO_SESSION_ACTIVE) { + if (ctx.session_state[transport] != PROTO_SESSION_ACTIVE) { LOG_INF("FunctionKeyEvent skip transport:%s session:%s", proto_transport_name(transport), - proto_session_name(session_state[transport])); + proto_session_name(ctx.session_state[transport])); continue; } @@ -514,10 +574,10 @@ static bool handle_hid_led_event(const struct hid_led_event *event) PROTO_TRANSPORT_USB_CDC : PROTO_TRANSPORT_BLE_NUS; - if (session_state[transport] != PROTO_SESSION_ACTIVE) { + if (ctx.session_state[transport] != PROTO_SESSION_ACTIVE) { LOG_INF("LedState skip transport:%s session:%s led_mask:0x%02x", proto_transport_name(transport), - proto_session_name(session_state[transport]), event->led_bm); + proto_session_name(ctx.session_state[transport]), event->led_bm); return false; } diff --git a/src/usb_cdc_module.c b/src/usb_cdc_module.c index 018bc5c..74f586c 100644 --- a/src/usb_cdc_module.c +++ b/src/usb_cdc_module.c @@ -28,8 +28,7 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define USB_CDC_RX_RING_BUF_SIZE 256 #define USB_CDC_TX_RING_BUF_SIZE 256 #define USB_CDC_RX_CHUNK_SIZE 32 -#define USB_CDC_PROTO_RX_BUF_SIZE 128 -#define USB_CDC_PROTO_RX_FLUSH_DELAY K_MSEC(3) +#define USB_CDC_PROTO_RX_BUF_SIZE PROTO_MAX_FRAME_LEN #define USB_CDC_EXPECTED_BAUDRATE 115200U enum usb_cdc_business_state { @@ -47,7 +46,6 @@ struct usb_cdc_ctx { struct ring_buf rx_ringbuf; struct ring_buf tx_ringbuf; struct k_work rx_work; - struct k_work_delayable rx_flush_work; uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE]; bool usb_active; size_t proto_rx_len; @@ -79,8 +77,6 @@ static struct usb_cdc_ctx ctx = { .proto_rx_len = 0U, }; -#define proto_rx_buf ctx.proto_rx_buf - static void validate_line_coding(void); static const char *usb_cdc_business_state_name(enum usb_cdc_business_state state) @@ -137,7 +133,6 @@ static void disable_uart_io(void) uart_irq_rx_disable(ctx.cdc_dev); uart_irq_tx_disable(ctx.cdc_dev); ctx.proto_rx_len = 0U; - k_work_cancel_delayable(&ctx.rx_flush_work); reset_ring_buffers(); } @@ -280,6 +275,46 @@ static void validate_line_coding(void) #endif } +static bool try_extract_frame(void) +{ + uint8_t *buf = ctx.proto_rx_buf; + size_t frame_len; + uint16_t magic; + + if (ctx.proto_rx_len < PROTO_FRAME_HEADER_SIZE) { + return false; + } + + magic = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8); + if (magic != PROTO_FRAME_MAGIC) { + LOG_WRN("CDC invalid frame magic 0x%04x", magic); + memmove(buf, &buf[1], ctx.proto_rx_len - 1U); + ctx.proto_rx_len--; + return true; + } + + if (buf[2] > PROTO_MAX_PAYLOAD_LEN) { + LOG_WRN("CDC invalid frame len:%u", buf[2]); + memmove(buf, &buf[1], ctx.proto_rx_len - 1U); + ctx.proto_rx_len--; + return true; + } + + frame_len = PROTO_FRAME_HEADER_SIZE + buf[2]; + if (ctx.proto_rx_len < frame_len) { + return false; + } + + LOG_INF("CDC submit framed proto_rx len:%u", (unsigned int)frame_len); + (void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, buf, frame_len); + + if (ctx.proto_rx_len > frame_len) { + memmove(buf, &buf[frame_len], ctx.proto_rx_len - frame_len); + } + ctx.proto_rx_len -= frame_len; + return true; +} + static void rx_work_handler(struct k_work *work) { uint8_t buffer[USB_CDC_RX_CHUNK_SIZE]; @@ -290,47 +325,30 @@ static void rx_work_handler(struct k_work *work) uint32_t len; unsigned int key = irq_lock(); - len = ring_buf_get(&ctx.rx_ringbuf, buffer, sizeof(buffer)); + len = ring_buf_get(&ctx.rx_ringbuf, buffer, sizeof(buffer)); irq_unlock(key); if (len == 0U) { - return; + break; } LOG_INF("CDC rx_work pulled %u bytes from RX ring (proto_rx_len:%u)", (unsigned int)len, (unsigned int)ctx.proto_rx_len); - if ((ctx.proto_rx_len + len) > sizeof(proto_rx_buf)) { - LOG_WRN("Drop oversized CDC protobuf message len:%u", + if ((ctx.proto_rx_len + len) > sizeof(ctx.proto_rx_buf)) { + LOG_WRN("Drop oversized CDC framed data len:%u", (uint32_t)(ctx.proto_rx_len + len)); ctx.proto_rx_len = 0U; } if (len > 0U) { - memcpy(&proto_rx_buf[ctx.proto_rx_len], buffer, len); + memcpy(&ctx.proto_rx_buf[ctx.proto_rx_len], buffer, len); ctx.proto_rx_len += len; - k_work_reschedule(&ctx.rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY); } } -} -static void rx_flush_work_handler(struct k_work *work) -{ - ARG_UNUSED(work); - - if ((transport_link_state_get() != PROTO_TRANSPORT_LINK_READY) || - (ctx.proto_rx_len == 0U)) { - LOG_INF("CDC rx_flush skipped: link=%s proto_rx_len:%u business:%s", - proto_link_state_name(transport_link_state_get()), - (unsigned int)ctx.proto_rx_len, - usb_cdc_business_state_name(ctx.business)); - return; + while (try_extract_frame()) { } - - LOG_INF("CDC rx_flush submit proto_rx len:%u", (unsigned int)ctx.proto_rx_len); - (void)submit_proto_rx_event(PROTO_TRANSPORT_USB_CDC, proto_rx_buf, - ctx.proto_rx_len); - ctx.proto_rx_len = 0U; } static void cdc_interrupt_handler(const struct device *dev, void *user_data) @@ -408,7 +426,6 @@ static int do_init(void) reset_ring_buffers(); k_work_init(&ctx.rx_work, rx_work_handler); - k_work_init_delayable(&ctx.rx_flush_work, rx_flush_work_handler); uart_irq_callback_set(ctx.cdc_dev, cdc_interrupt_handler); ctx.business = USB_CDC_BUS_OFFLINE; ctx.usb_active = false;