feat(hid_tx_manager): 重构HID传输管理器以支持报告状态跟踪
- 引入原子标志位替换布尔变量,提高线程安全性 - 使用消息队列替代循环缓冲区实现传输队列 - 添加boot和NKRO报告状态管理功能 - 实现脏标记机制优化报告发送流程 - 改进传输完成事件处理逻辑
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/sys/atomic.h>
|
||||||
|
|
||||||
#include <app_event_manager.h>
|
#include <app_event_manager.h>
|
||||||
|
|
||||||
#define MODULE hid_tx_manager
|
#define MODULE hid_tx_manager
|
||||||
#include <caf/events/module_state_event.h>
|
#include <caf/events/module_state_event.h>
|
||||||
|
|
||||||
|
#include "hid_report_descriptor.h"
|
||||||
#include "hid_boot_event.h"
|
#include "hid_boot_event.h"
|
||||||
#include "hid_report_event.h"
|
#include "hid_report_event.h"
|
||||||
#include "hid_tx_done_event.h"
|
#include "hid_tx_done_event.h"
|
||||||
@@ -17,6 +21,15 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
|||||||
#define HID_TX_QUEUE_SIZE 16
|
#define HID_TX_QUEUE_SIZE 16
|
||||||
#define HID_TX_MAX_DATA 32
|
#define HID_TX_MAX_DATA 32
|
||||||
|
|
||||||
|
enum hid_tx_flag {
|
||||||
|
HID_TX_FLAG_INITIALIZED = 0,
|
||||||
|
HID_TX_FLAG_IN_FLIGHT,
|
||||||
|
HID_TX_FLAG_BOOT_VALID,
|
||||||
|
HID_TX_FLAG_BOOT_DIRTY,
|
||||||
|
HID_TX_FLAG_NKRO_VALID,
|
||||||
|
HID_TX_FLAG_NKRO_DIRTY,
|
||||||
|
};
|
||||||
|
|
||||||
struct hid_tx_item {
|
struct hid_tx_item {
|
||||||
enum hid_tx_kind kind;
|
enum hid_tx_kind kind;
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -24,86 +37,92 @@ struct hid_tx_item {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct hid_tx_ctx {
|
struct hid_tx_ctx {
|
||||||
struct hid_tx_item queue[HID_TX_QUEUE_SIZE];
|
atomic_t flags;
|
||||||
uint8_t head;
|
struct hid_tx_item boot_state;
|
||||||
uint8_t tail;
|
struct hid_tx_item nkro_state;
|
||||||
uint8_t count;
|
struct hid_tx_item inflight_item;
|
||||||
bool initialized;
|
|
||||||
bool in_flight;
|
|
||||||
mode_type_t active_mode;
|
mode_type_t active_mode;
|
||||||
enum hid_tx_kind inflight_kind;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct hid_tx_ctx tx = {
|
static struct hid_tx_ctx tx = {
|
||||||
.active_mode = MODE_TYPE_COUNT,
|
.active_mode = MODE_TYPE_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool hid_tx_queue_push(enum hid_tx_kind kind, const uint8_t *data, size_t len)
|
K_MSGQ_DEFINE(hid_tx_queue_msgq, sizeof(struct hid_tx_item), HID_TX_QUEUE_SIZE, 4);
|
||||||
{
|
|
||||||
struct hid_tx_item *item;
|
|
||||||
|
|
||||||
|
static bool hid_tx_item_store(struct hid_tx_item *item,
|
||||||
|
enum hid_tx_kind kind,
|
||||||
|
const uint8_t *data,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
if (len > HID_TX_MAX_DATA) {
|
if (len > HID_TX_MAX_DATA) {
|
||||||
LOG_WRN("Drop HID tx kind=%u len=%u: too large", kind, len);
|
LOG_WRN("Drop HID tx kind=%u len=%u: too large", kind, len);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx.count >= HID_TX_QUEUE_SIZE) {
|
|
||||||
LOG_WRN("Drop HID tx kind=%u len=%u: queue full", kind, len);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = &tx.queue[tx.tail];
|
|
||||||
item->kind = kind;
|
item->kind = kind;
|
||||||
item->len = len;
|
item->len = len;
|
||||||
if ((len > 0U) && (data != NULL)) {
|
if ((len > 0U) && (data != NULL)) {
|
||||||
memcpy(item->data, data, len);
|
memcpy(item->data, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.tail = (tx.tail + 1U) % HID_TX_QUEUE_SIZE;
|
|
||||||
tx.count++;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hid_tx_item *hid_tx_queue_front(void)
|
static bool hid_tx_queue_push(enum hid_tx_kind kind, const uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
if (tx.count == 0U) {
|
struct hid_tx_item item;
|
||||||
return NULL;
|
|
||||||
|
if (!hid_tx_item_store(&item, kind, data, len)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tx.queue[tx.head];
|
if (k_msgq_put(&hid_tx_queue_msgq, &item, K_NO_WAIT)) {
|
||||||
|
LOG_WRN("Drop HID tx kind=%u len=%u: queue full", kind, len);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_tx_queue_pop(void)
|
static bool hid_tx_dispatch_item(const struct hid_tx_item *item)
|
||||||
{
|
{
|
||||||
__ASSERT_NO_MSG(tx.count > 0U);
|
tx.inflight_item = *item;
|
||||||
tx.head = (tx.head + 1U) % HID_TX_QUEUE_SIZE;
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_IN_FLIGHT);
|
||||||
tx.count--;
|
hid_tx_event_submit(item->kind, item->data, item->len);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dispatch_next_if_possible(void)
|
static void dispatch_next_if_possible(void)
|
||||||
{
|
{
|
||||||
struct hid_tx_item *item;
|
struct hid_tx_item item;
|
||||||
|
|
||||||
if (!tx.initialized || tx.in_flight) {
|
if (!atomic_test_bit(&tx.flags, HID_TX_FLAG_INITIALIZED) ||
|
||||||
return;
|
atomic_test_bit(&tx.flags, HID_TX_FLAG_IN_FLIGHT)) {
|
||||||
}
|
|
||||||
|
|
||||||
item = hid_tx_queue_front();
|
|
||||||
if (item == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((tx.active_mode != MODE_TYPE_USB) && (tx.active_mode != MODE_TYPE_BLE)) {
|
if ((tx.active_mode != MODE_TYPE_USB) && (tx.active_mode != MODE_TYPE_BLE)) {
|
||||||
LOG_WRN("Drop HID tx kind=%u: unsupported mode=%u",
|
|
||||||
item->kind, tx.active_mode);
|
|
||||||
hid_tx_queue_pop();
|
|
||||||
dispatch_next_if_possible();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.in_flight = true;
|
if (atomic_test_bit(&tx.flags, HID_TX_FLAG_NKRO_DIRTY) &&
|
||||||
tx.inflight_kind = item->kind;
|
atomic_test_bit(&tx.flags, HID_TX_FLAG_NKRO_VALID)) {
|
||||||
hid_tx_event_submit(item->kind, item->data, item->len);
|
atomic_clear_bit(&tx.flags, HID_TX_FLAG_NKRO_DIRTY);
|
||||||
|
(void)hid_tx_dispatch_item(&tx.nkro_state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atomic_test_bit(&tx.flags, HID_TX_FLAG_BOOT_DIRTY) &&
|
||||||
|
atomic_test_bit(&tx.flags, HID_TX_FLAG_BOOT_VALID)) {
|
||||||
|
atomic_clear_bit(&tx.flags, HID_TX_FLAG_BOOT_DIRTY);
|
||||||
|
(void)hid_tx_dispatch_item(&tx.boot_state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!k_msgq_get(&hid_tx_queue_msgq, &item, K_NO_WAIT)) {
|
||||||
|
(void)hid_tx_dispatch_item(&item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_module_state_event(const struct module_state_event *event)
|
static bool handle_module_state_event(const struct module_state_event *event)
|
||||||
@@ -112,8 +131,8 @@ static bool handle_module_state_event(const struct module_state_event *event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
__ASSERT_NO_MSG(!tx.initialized);
|
__ASSERT_NO_MSG(!atomic_test_bit(&tx.flags, HID_TX_FLAG_INITIALIZED));
|
||||||
tx.initialized = true;
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_INITIALIZED);
|
||||||
module_set_state(MODULE_STATE_READY);
|
module_set_state(MODULE_STATE_READY);
|
||||||
dispatch_next_if_possible();
|
dispatch_next_if_possible();
|
||||||
|
|
||||||
@@ -129,42 +148,44 @@ static bool handle_mode_event(const struct mode_event *event)
|
|||||||
|
|
||||||
static bool handle_hid_boot_request_event(const struct hid_boot_event *event)
|
static bool handle_hid_boot_request_event(const struct hid_boot_event *event)
|
||||||
{
|
{
|
||||||
(void)hid_tx_queue_push(HID_TX_KIND_BOOT,
|
(void)hid_tx_item_store(&tx.boot_state,
|
||||||
|
HID_TX_KIND_BOOT,
|
||||||
hid_boot_event_get_data(event),
|
hid_boot_event_get_data(event),
|
||||||
hid_boot_event_get_size(event));
|
hid_boot_event_get_size(event));
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_BOOT_VALID);
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_BOOT_DIRTY);
|
||||||
dispatch_next_if_possible();
|
dispatch_next_if_possible();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_hid_report_request_event(const struct hid_report_event *event)
|
static bool handle_hid_report_request_event(const struct hid_report_event *event)
|
||||||
{
|
{
|
||||||
(void)hid_tx_queue_push(HID_TX_KIND_REPORT,
|
const uint8_t *data = hid_report_event_get_data(event);
|
||||||
hid_report_event_get_data(event),
|
size_t len = hid_report_event_get_size(event);
|
||||||
hid_report_event_get_size(event));
|
|
||||||
|
if ((len > 0U) && (data[0] == REPORT_ID_KEYBOARD)) {
|
||||||
|
(void)hid_tx_item_store(&tx.nkro_state, HID_TX_KIND_REPORT, data, len);
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_VALID);
|
||||||
|
atomic_set_bit(&tx.flags, HID_TX_FLAG_NKRO_DIRTY);
|
||||||
|
} else {
|
||||||
|
(void)hid_tx_queue_push(HID_TX_KIND_REPORT, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch_next_if_possible();
|
dispatch_next_if_possible();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_hid_tx_done_event(const struct hid_tx_done_event *event)
|
static bool handle_hid_tx_done_event(const struct hid_tx_done_event *event)
|
||||||
{
|
{
|
||||||
struct hid_tx_item *item;
|
if (!atomic_test_bit(&tx.flags, HID_TX_FLAG_IN_FLIGHT)) {
|
||||||
|
|
||||||
if (!tx.in_flight) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->kind != tx.inflight_kind) {
|
if (event->kind != tx.inflight_item.kind) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
item = hid_tx_queue_front();
|
atomic_clear_bit(&tx.flags, HID_TX_FLAG_IN_FLIGHT);
|
||||||
if (item == NULL) {
|
|
||||||
tx.in_flight = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hid_tx_queue_pop();
|
|
||||||
tx.in_flight = false;
|
|
||||||
dispatch_next_if_possible();
|
dispatch_next_if_possible();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user