Files
blinky/src/hid_flowctrl_module.c

459 lines
12 KiB
C
Raw Normal View History

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#define MODULE hid_flowctrl_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <zephyr/logging/log.h>
#include "hid_report_sent_event.h"
#include "hid_transport_state_event.h"
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "keyboard_hid_report_event.h"
#include "mode_switch_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define HID_FLOWCTRL_FIFO_DEPTH 32U
#define HID_FLOWCTRL_REPORT_DATA_MAX KEYBOARD_NKRO_REPORT_SIZE
struct pending_report {
bool valid;
enum keyboard_report_type report_type;
enum keyboard_protocol_mode protocol_mode;
size_t size;
uint8_t data[HID_FLOWCTRL_REPORT_DATA_MAX];
};
struct queued_report {
enum keyboard_report_type report_type;
enum keyboard_protocol_mode protocol_mode;
size_t size;
uint8_t data[HID_FLOWCTRL_REPORT_DATA_MAX];
};
struct hid_transport_state_data {
bool ready;
bool keys_ready;
bool consumer_ready;
enum keyboard_protocol_mode protocol_mode;
};
struct in_flight_report {
bool active;
enum hid_transport transport;
enum keyboard_report_type report_type;
uint16_t sequence;
};
static struct hid_transport_state_data transport_state[HID_TRANSPORT_COUNT] = {
[HID_TRANSPORT_USB] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
[HID_TRANSPORT_BLE] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
};
static struct pending_report pending_keys;
static struct pending_report pending_consumer_latest;
static struct queued_report consumer_fifo[HID_FLOWCTRL_FIFO_DEPTH];
static uint8_t consumer_fifo_head;
static uint8_t consumer_fifo_tail;
static uint8_t consumer_fifo_count;
static struct in_flight_report in_flight;
static enum mode_switch_mode current_mode;
static uint16_t next_sequence;
static bool initialized;
static bool running;
static bool mode_to_transport(enum mode_switch_mode mode, enum hid_transport *transport)
{
switch (mode) {
case MODE_SWITCH_USB:
*transport = HID_TRANSPORT_USB;
return true;
case MODE_SWITCH_BLE:
*transport = HID_TRANSPORT_BLE;
return true;
default:
return false;
}
}
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));
}
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) {
LOG_WRN("Consumer FIFO full, dropping oldest pulse");
consumer_fifo_head = (consumer_fifo_head + 1U) % HID_FLOWCTRL_FIFO_DEPTH;
consumer_fifo_count--;
}
struct queued_report *entry = &consumer_fifo[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++;
}
static bool consumer_fifo_pop(struct queued_report *entry)
{
if (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--;
return true;
}
static bool transport_can_send_report(enum keyboard_report_type report_type)
{
enum hid_transport transport;
struct hid_transport_state_data *state;
if (!mode_to_transport(current_mode, &transport) || in_flight.active) {
return false;
}
state = &transport_state[transport];
if (!state->ready) {
return false;
}
if (report_type == KEYBOARD_REPORT_TYPE_KEYS) {
return state->keys_ready;
}
return state->consumer_ready;
}
static void try_send_next(void)
{
struct queued_report queued;
enum hid_transport transport;
struct hid_transport_state_data *state;
if (!running || in_flight.active || !mode_to_transport(current_mode, &transport)) {
return;
}
state = &transport_state[transport];
if (!state->ready) {
return;
}
if (pending_keys.valid && transport_can_send_report(KEYBOARD_REPORT_TYPE_KEYS)) {
if (pending_keys.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale keys report after protocol change");
pending_keys.valid = false;
} else {
feat(events): 添加事件提交函数到各个头文件中 为多个事件头文件添加了静态内联提交函数,包括: - bat_state_event: 添加submit_bat_state_event函数 - ble_serial_rx_event: 添加submit_ble_serial_rx_event函数 - ble_serial_tx_event: 添加submit_ble_serial_tx_event函数 - cdc_proto_tx_event: 添加submit_cdc_proto_tx_event函数 - datetime_event: 添加submit_datetime_event函数 - encoder_event: 添加submit_encoder_event函数 - function_bitmap_update_event: 添加submit_function_bitmap_update_event函数 - hid_led_event: 添加submit_hid_led_event函数 - hid_report_sent_event: 添加submit_hid_report_sent_event函数 - hid_transport_state_event: 添加submit_hid_transport_state_event函数 - hid_tx_report_event: 添加submit_hid_tx_report_event函数 - key_function_event: 添加submit_key_function_event函数 - keyboard_hid_report_event: 添加submit_keyboard_hid_report_event函数 - led_strip_en_event: 添加submit_led_strip_en_event函数 - mode_switch_event: 添加submit_mode_switch_event函数 - set_protocol_event: 添加submit_set_protocol_event函数 - theme_rgb_update_event: 添加submit_theme_rgb_update_event函数 - time_sync_event: 添加submit_time_sync_event函数 - usb_cdc_rx_event: 添加submit_usb_cdc_rx_event函数 - usb_cdc_tx_event: 添加submit_usb_cdc_tx_event函数 - usb_device_state_event: 添加submit_usb_device_state_event函数 - usb_function_ready_event: 添加submit_usb_function_ready_event函数 - usb_prepare_event: 添加submit_usb_prepare_event函数 这些函数提供了一致的事件提交接口,简化了事件创建和提交过程。
2026-04-14 16:42:04 +08:00
in_flight.active = true;
in_flight.transport = transport;
in_flight.report_type = pending_keys.report_type;
in_flight.sequence = next_sequence++;
(void)submit_hid_tx_report_event(
transport, pending_keys.report_type,
pending_keys.protocol_mode, in_flight.sequence,
pending_keys.data, pending_keys.size);
pending_keys.valid = false;
return;
}
}
if ((consumer_fifo_count > 0U) &&
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER) &&
consumer_fifo_pop(&queued)) {
if (queued.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale consumer report after protocol change");
} else {
feat(events): 添加事件提交函数到各个头文件中 为多个事件头文件添加了静态内联提交函数,包括: - bat_state_event: 添加submit_bat_state_event函数 - ble_serial_rx_event: 添加submit_ble_serial_rx_event函数 - ble_serial_tx_event: 添加submit_ble_serial_tx_event函数 - cdc_proto_tx_event: 添加submit_cdc_proto_tx_event函数 - datetime_event: 添加submit_datetime_event函数 - encoder_event: 添加submit_encoder_event函数 - function_bitmap_update_event: 添加submit_function_bitmap_update_event函数 - hid_led_event: 添加submit_hid_led_event函数 - hid_report_sent_event: 添加submit_hid_report_sent_event函数 - hid_transport_state_event: 添加submit_hid_transport_state_event函数 - hid_tx_report_event: 添加submit_hid_tx_report_event函数 - key_function_event: 添加submit_key_function_event函数 - keyboard_hid_report_event: 添加submit_keyboard_hid_report_event函数 - led_strip_en_event: 添加submit_led_strip_en_event函数 - mode_switch_event: 添加submit_mode_switch_event函数 - set_protocol_event: 添加submit_set_protocol_event函数 - theme_rgb_update_event: 添加submit_theme_rgb_update_event函数 - time_sync_event: 添加submit_time_sync_event函数 - usb_cdc_rx_event: 添加submit_usb_cdc_rx_event函数 - usb_cdc_tx_event: 添加submit_usb_cdc_tx_event函数 - usb_device_state_event: 添加submit_usb_device_state_event函数 - usb_function_ready_event: 添加submit_usb_function_ready_event函数 - usb_prepare_event: 添加submit_usb_prepare_event函数 这些函数提供了一致的事件提交接口,简化了事件创建和提交过程。
2026-04-14 16:42:04 +08:00
in_flight.active = true;
in_flight.transport = transport;
in_flight.report_type = queued.report_type;
in_flight.sequence = next_sequence++;
(void)submit_hid_tx_report_event(
transport, queued.report_type,
queued.protocol_mode, in_flight.sequence,
queued.data, queued.size);
return;
}
}
if (pending_consumer_latest.valid &&
transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER)) {
if (pending_consumer_latest.protocol_mode != state->protocol_mode) {
LOG_WRN("Drop stale latest consumer report after protocol change");
pending_consumer_latest.valid = false;
} else {
feat(events): 添加事件提交函数到各个头文件中 为多个事件头文件添加了静态内联提交函数,包括: - bat_state_event: 添加submit_bat_state_event函数 - ble_serial_rx_event: 添加submit_ble_serial_rx_event函数 - ble_serial_tx_event: 添加submit_ble_serial_tx_event函数 - cdc_proto_tx_event: 添加submit_cdc_proto_tx_event函数 - datetime_event: 添加submit_datetime_event函数 - encoder_event: 添加submit_encoder_event函数 - function_bitmap_update_event: 添加submit_function_bitmap_update_event函数 - hid_led_event: 添加submit_hid_led_event函数 - hid_report_sent_event: 添加submit_hid_report_sent_event函数 - hid_transport_state_event: 添加submit_hid_transport_state_event函数 - hid_tx_report_event: 添加submit_hid_tx_report_event函数 - key_function_event: 添加submit_key_function_event函数 - keyboard_hid_report_event: 添加submit_keyboard_hid_report_event函数 - led_strip_en_event: 添加submit_led_strip_en_event函数 - mode_switch_event: 添加submit_mode_switch_event函数 - set_protocol_event: 添加submit_set_protocol_event函数 - theme_rgb_update_event: 添加submit_theme_rgb_update_event函数 - time_sync_event: 添加submit_time_sync_event函数 - usb_cdc_rx_event: 添加submit_usb_cdc_rx_event函数 - usb_cdc_tx_event: 添加submit_usb_cdc_tx_event函数 - usb_device_state_event: 添加submit_usb_device_state_event函数 - usb_function_ready_event: 添加submit_usb_function_ready_event函数 - usb_prepare_event: 添加submit_usb_prepare_event函数 这些函数提供了一致的事件提交接口,简化了事件创建和提交过程。
2026-04-14 16:42:04 +08:00
in_flight.active = true;
in_flight.transport = transport;
in_flight.report_type = pending_consumer_latest.report_type;
in_flight.sequence = next_sequence++;
(void)submit_hid_tx_report_event(
transport, pending_consumer_latest.report_type,
pending_consumer_latest.protocol_mode,
in_flight.sequence,
pending_consumer_latest.data,
pending_consumer_latest.size);
pending_consumer_latest.valid = false;
}
}
}
static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_event *event)
{
if (!running ||
((event->mode != MODE_SWITCH_USB) && (event->mode != MODE_SWITCH_BLE))) {
return false;
}
if (event->queue_policy == HID_QUEUE_POLICY_FIFO) {
consumer_fifo_push(event->report_type,
event->protocol_mode,
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);
} 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,
event->dyndata.size);
}
try_send_next();
return false;
}
static bool handle_hid_transport_state_event(const struct hid_transport_state_event *event)
{
enum hid_transport active_transport;
struct hid_transport_state_data *state;
if (event->transport >= HID_TRANSPORT_COUNT) {
return false;
}
state = &transport_state[event->transport];
state->ready = event->ready;
state->keys_ready = event->keys_ready;
state->consumer_ready = event->consumer_ready;
if (state->protocol_mode != event->protocol_mode) {
state->protocol_mode = event->protocol_mode;
if (mode_to_transport(current_mode, &active_transport) &&
(active_transport == event->transport)) {
pending_keys.valid = false;
pending_consumer_latest.valid = false;
consumer_fifo_head = 0U;
consumer_fifo_tail = 0U;
consumer_fifo_count = 0U;
}
}
if (!state->ready &&
mode_to_transport(current_mode, &active_transport) &&
(active_transport == event->transport)) {
consumer_fifo_head = 0U;
consumer_fifo_tail = 0U;
consumer_fifo_count = 0U;
in_flight.active = false;
}
try_send_next();
return false;
}
static bool handle_hid_report_sent_event(const struct hid_report_sent_event *event)
{
if (!in_flight.active || (event->transport != in_flight.transport)) {
return false;
}
if (event->sequence != in_flight.sequence) {
LOG_WRN("Unexpected HID sent sequence %u (expected %u)",
event->sequence, in_flight.sequence);
return false;
}
in_flight.active = false;
if (event->error) {
LOG_WRN("HID report send failed for seq %u", event->sequence);
}
try_send_next();
return false;
}
static bool handle_mode_switch_event(const struct mode_switch_event *event)
{
bool mode_changed = (current_mode != event->mode);
current_mode = event->mode;
if (mode_changed || ((current_mode != MODE_SWITCH_USB) &&
(current_mode != MODE_SWITCH_BLE))) {
clear_pending_reports();
}
try_send_next();
return false;
}
static int module_init(void)
{
clear_pending_reports();
current_mode = MODE_SWITCH_USB;
transport_state[HID_TRANSPORT_USB].ready = false;
transport_state[HID_TRANSPORT_USB].keys_ready = false;
transport_state[HID_TRANSPORT_USB].consumer_ready = false;
transport_state[HID_TRANSPORT_USB].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
transport_state[HID_TRANSPORT_BLE].ready = false;
transport_state[HID_TRANSPORT_BLE].keys_ready = false;
transport_state[HID_TRANSPORT_BLE].consumer_ready = false;
transport_state[HID_TRANSPORT_BLE].protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
next_sequence = 1U;
return 0;
}
static int module_start(void)
{
if (running) {
return 0;
}
running = true;
try_send_next();
return 0;
}
static void module_pause(void)
{
if (!running) {
return;
}
clear_pending_reports();
running = false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_keyboard_hid_report_event(aeh)) {
return handle_keyboard_hid_report_event(cast_keyboard_hid_report_event(aeh));
}
if (is_hid_transport_state_event(aeh)) {
return handle_hid_transport_state_event(cast_hid_transport_state_event(aeh));
}
if (is_hid_report_sent_event(aeh)) {
return handle_hid_report_sent_event(cast_hid_report_sent_event(aeh));
}
if (is_mode_switch_event(aeh)) {
return handle_mode_switch_event(cast_mode_switch_event(aeh));
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
int err;
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (initialized) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
__ASSERT_NO_MSG(false);
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, keyboard_hid_report_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_transport_state_event);
APP_EVENT_SUBSCRIBE(MODULE, hid_report_sent_event);
APP_EVENT_SUBSCRIBE(MODULE, mode_switch_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);