feat(proto): 添加设备通信协议v1修订版及统一帧格式
- 新增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前缀
This commit is contained in:
302
docs/device_communication_protocol_v1.md
Normal file
302
docs/device_communication_protocol_v1.md
Normal file
@@ -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 }`
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user