From 70381192d9c5093c826c20d805e4be67140f7f12 Mon Sep 17 00:00:00 2001 From: skiinder Date: Fri, 10 Apr 2026 13:46:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0HID=E6=B5=81=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E6=A8=A1=E5=9D=97=E5=92=8C=E7=9B=B8=E5=85=B3=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加hid_flowctrl_module.c实现HID报告流控制功能,包括FIFO队列管理和 报告发送控制 - 新增hid_report_sent_event、hid_transport_state_event和 hid_tx_report_event事件类型及其对应的头文件和实现 - 在CMakeLists.txt中注册新模块和事件源文件 - 修改keyboard_core_module.c以支持队列策略,并添加编码器事件处理逻辑 - 更新usb_hid_module.c将直接的键盘HID报告事件改为通过 hid_tx_report_event进行传输,并添加状态报告事件 - 在keyboard_hid_report_event中增加queue_policy字段以支持不同 队列策略 --- CMakeLists.txt | 4 + inc/events/hid_report_sent_event.h | 27 ++ inc/events/hid_transport_state_event.h | 28 ++ inc/events/hid_tx_report_event.h | 28 ++ inc/events/keyboard_hid_report_event.h | 1 + inc/keyboard_core.h | 9 + src/events/hid_report_sent_event.c | 59 ++++ src/events/hid_transport_state_event.c | 66 +++++ src/events/hid_tx_report_event.c | 105 +++++++ src/events/keyboard_hid_report_event.c | 20 +- src/hid_flowctrl_module.c | 390 +++++++++++++++++++++++++ src/keyboard_core_module.c | 68 ++++- src/usb_hid_module.c | 61 +++- 13 files changed, 856 insertions(+), 10 deletions(-) create mode 100644 inc/events/hid_report_sent_event.h create mode 100644 inc/events/hid_transport_state_event.h create mode 100644 inc/events/hid_tx_report_event.h create mode 100644 src/events/hid_report_sent_event.c create mode 100644 src/events/hid_transport_state_event.c create mode 100644 src/events/hid_tx_report_event.c create mode 100644 src/hid_flowctrl_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c75d401..99ee416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,14 @@ target_sources(app PRIVATE src/main.c src/battery_module.c src/encoder_module.c + src/hid_flowctrl_module.c src/keyboard_core_module.c src/usb_hid_module.c src/events/encoder_event.c src/events/hid_led_event.c + src/events/hid_report_sent_event.c + src/events/hid_transport_state_event.c + src/events/hid_tx_report_event.c src/mode_switch_module.c src/events/keyboard_hid_report_event.c src/events/mode_switch_event.c diff --git a/inc/events/hid_report_sent_event.h b/inc/events/hid_report_sent_event.h new file mode 100644 index 0000000..dc8b572 --- /dev/null +++ b/inc/events/hid_report_sent_event.h @@ -0,0 +1,27 @@ +#ifndef BLINKY_HID_REPORT_SENT_EVENT_H_ +#define BLINKY_HID_REPORT_SENT_EVENT_H_ + +#include +#include + +#include "keyboard_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hid_report_sent_event { + struct app_event_header header; + enum hid_transport transport; + enum keyboard_report_type report_type; + uint16_t sequence; + bool error; +}; + +APP_EVENT_TYPE_DECLARE(hid_report_sent_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_HID_REPORT_SENT_EVENT_H_ */ diff --git a/inc/events/hid_transport_state_event.h b/inc/events/hid_transport_state_event.h new file mode 100644 index 0000000..b7afae0 --- /dev/null +++ b/inc/events/hid_transport_state_event.h @@ -0,0 +1,28 @@ +#ifndef BLINKY_HID_TRANSPORT_STATE_EVENT_H_ +#define BLINKY_HID_TRANSPORT_STATE_EVENT_H_ + +#include +#include + +#include "keyboard_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hid_transport_state_event { + struct app_event_header header; + enum hid_transport transport; + bool ready; + bool keys_ready; + bool consumer_ready; + enum keyboard_protocol_mode protocol_mode; +}; + +APP_EVENT_TYPE_DECLARE(hid_transport_state_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_HID_TRANSPORT_STATE_EVENT_H_ */ diff --git a/inc/events/hid_tx_report_event.h b/inc/events/hid_tx_report_event.h new file mode 100644 index 0000000..202d23d --- /dev/null +++ b/inc/events/hid_tx_report_event.h @@ -0,0 +1,28 @@ +#ifndef BLINKY_HID_TX_REPORT_EVENT_H_ +#define BLINKY_HID_TX_REPORT_EVENT_H_ + +#include +#include + +#include "keyboard_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hid_tx_report_event { + struct app_event_header header; + enum hid_transport transport; + enum keyboard_report_type report_type; + enum keyboard_protocol_mode protocol_mode; + uint16_t sequence; + struct event_dyndata dyndata; +}; + +APP_EVENT_TYPE_DYNDATA_DECLARE(hid_tx_report_event); + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_HID_TX_REPORT_EVENT_H_ */ diff --git a/inc/events/keyboard_hid_report_event.h b/inc/events/keyboard_hid_report_event.h index ae2841b..0abdd88 100644 --- a/inc/events/keyboard_hid_report_event.h +++ b/inc/events/keyboard_hid_report_event.h @@ -16,6 +16,7 @@ struct keyboard_hid_report_event { enum mode_switch_mode mode; enum keyboard_report_type report_type; enum keyboard_protocol_mode protocol_mode; + enum hid_queue_policy queue_policy; struct event_dyndata dyndata; }; diff --git a/inc/keyboard_core.h b/inc/keyboard_core.h index 288df4b..206154a 100644 --- a/inc/keyboard_core.h +++ b/inc/keyboard_core.h @@ -23,6 +23,15 @@ enum keyboard_report_type { KEYBOARD_REPORT_TYPE_CONSUMER, }; +enum hid_queue_policy { + HID_QUEUE_POLICY_LATEST, + HID_QUEUE_POLICY_FIFO, +}; + +enum hid_transport { + HID_TRANSPORT_USB, +}; + enum keyboard_consumer_control { KEYBOARD_CONSUMER_CTRL_MUTE, KEYBOARD_CONSUMER_CTRL_VOLUME_UP, diff --git a/src/events/hid_report_sent_event.c b/src/events/hid_report_sent_event.c new file mode 100644 index 0000000..bd5538a --- /dev/null +++ b/src/events/hid_report_sent_event.c @@ -0,0 +1,59 @@ +#include "hid_report_sent_event.h" + +static const char *transport_name(enum hid_transport transport) +{ + switch (transport) { + case HID_TRANSPORT_USB: + return "USB"; + default: + return "?"; + } +} + +static const char *report_type_name(enum keyboard_report_type report_type) +{ + switch (report_type) { + case KEYBOARD_REPORT_TYPE_KEYS: + return "keys"; + case KEYBOARD_REPORT_TYPE_CONSUMER: + return "consumer"; + default: + return "?"; + } +} + +static void log_hid_report_sent_event(const struct app_event_header *aeh) +{ + const struct hid_report_sent_event *event = cast_hid_report_sent_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "transport:%s type:%s seq:%u error:%u", + transport_name(event->transport), + report_type_name(event->report_type), + event->sequence, + event->error); +} + +static void profile_hid_report_sent_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_report_sent_event *event = cast_hid_report_sent_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->transport); + nrf_profiler_log_encode_uint8(buf, event->report_type); + nrf_profiler_log_encode_uint16(buf, event->sequence); + nrf_profiler_log_encode_uint8(buf, event->error); +} + +APP_EVENT_INFO_DEFINE(hid_report_sent_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U16, + NRF_PROFILER_ARG_U8), + ENCODE("transport", "report_type", "sequence", "error"), + profile_hid_report_sent_event); + +APP_EVENT_TYPE_DEFINE(hid_report_sent_event, + log_hid_report_sent_event, + &hid_report_sent_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/hid_transport_state_event.c b/src/events/hid_transport_state_event.c new file mode 100644 index 0000000..1546271 --- /dev/null +++ b/src/events/hid_transport_state_event.c @@ -0,0 +1,66 @@ +#include "hid_transport_state_event.h" + +static const char *transport_name(enum hid_transport transport) +{ + switch (transport) { + case HID_TRANSPORT_USB: + return "USB"; + default: + return "?"; + } +} + +static const char *protocol_mode_name(enum keyboard_protocol_mode protocol_mode) +{ + switch (protocol_mode) { + case KEYBOARD_PROTOCOL_MODE_BOOT: + return "boot"; + case KEYBOARD_PROTOCOL_MODE_REPORT: + return "report"; + default: + return "?"; + } +} + +static void log_hid_transport_state_event(const struct app_event_header *aeh) +{ + const struct hid_transport_state_event *event = + cast_hid_transport_state_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, + "transport:%s ready:%u keys_ready:%u consumer_ready:%u protocol:%s", + transport_name(event->transport), + event->ready, + event->keys_ready, + event->consumer_ready, + protocol_mode_name(event->protocol_mode)); +} + +static void profile_hid_transport_state_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_transport_state_event *event = + cast_hid_transport_state_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->transport); + nrf_profiler_log_encode_uint8(buf, event->ready); + nrf_profiler_log_encode_uint8(buf, event->keys_ready); + nrf_profiler_log_encode_uint8(buf, event->consumer_ready); + nrf_profiler_log_encode_uint8(buf, event->protocol_mode); +} + +APP_EVENT_INFO_DEFINE(hid_transport_state_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8), + ENCODE("transport", "ready", "keys_ready", "consumer_ready", + "protocol_mode"), + profile_hid_transport_state_event); + +APP_EVENT_TYPE_DEFINE(hid_transport_state_event, + log_hid_transport_state_event, + &hid_transport_state_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/hid_tx_report_event.c b/src/events/hid_tx_report_event.c new file mode 100644 index 0000000..0207e6c --- /dev/null +++ b/src/events/hid_tx_report_event.c @@ -0,0 +1,105 @@ +#include + +#include "hid_tx_report_event.h" + +#define HID_TX_REPORT_EVENT_LOG_BUF_LEN 192 + +static const char *transport_name(enum hid_transport transport) +{ + switch (transport) { + case HID_TRANSPORT_USB: + return "USB"; + default: + return "?"; + } +} + +static const char *report_type_name(enum keyboard_report_type report_type) +{ + switch (report_type) { + case KEYBOARD_REPORT_TYPE_KEYS: + return "keys"; + case KEYBOARD_REPORT_TYPE_CONSUMER: + return "consumer"; + default: + return "?"; + } +} + +static const char *protocol_mode_name(enum keyboard_protocol_mode protocol_mode) +{ + switch (protocol_mode) { + case KEYBOARD_PROTOCOL_MODE_BOOT: + return "boot"; + case KEYBOARD_PROTOCOL_MODE_REPORT: + return "report"; + default: + return "?"; + } +} + +static void log_hid_tx_report_event(const struct app_event_header *aeh) +{ + const struct hid_tx_report_event *event = cast_hid_tx_report_event(aeh); + char log_buf[HID_TX_REPORT_EVENT_LOG_BUF_LEN]; + int pos; + + pos = snprintf(log_buf, sizeof(log_buf), + "transport:%s type:%s protocol:%s seq:%u len:%zu", + transport_name(event->transport), + report_type_name(event->report_type), + protocol_mode_name(event->protocol_mode), + event->sequence, + event->dyndata.size); + if ((pos > 0) && (pos < sizeof(log_buf))) { + for (size_t i = 0; i < event->dyndata.size; i++) { + int tmp = snprintf(&log_buf[pos], sizeof(log_buf) - pos, + " %02x", event->dyndata.data[i]); + + if (tmp < 0) { + log_buf[sizeof(log_buf) - 2] = '~'; + pos = tmp; + break; + } + + pos += tmp; + if (pos >= sizeof(log_buf)) { + break; + } + } + } + + if (pos < 0) { + APP_EVENT_MANAGER_LOG(aeh, "log message preparation failure"); + return; + } + + APP_EVENT_MANAGER_LOG(aeh, "%s", log_buf); +} + +static void profile_hid_tx_report_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_tx_report_event *event = cast_hid_tx_report_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->transport); + nrf_profiler_log_encode_uint8(buf, event->report_type); + nrf_profiler_log_encode_uint8(buf, event->protocol_mode); + nrf_profiler_log_encode_uint16(buf, event->sequence); + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size); +} + +APP_EVENT_INFO_DEFINE(hid_tx_report_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U16, + NRF_PROFILER_ARG_U8), + ENCODE("transport", "report_type", "protocol_mode", "sequence", "len"), + profile_hid_tx_report_event); + +APP_EVENT_TYPE_DEFINE(hid_tx_report_event, + log_hid_tx_report_event, + &hid_tx_report_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/keyboard_hid_report_event.c b/src/events/keyboard_hid_report_event.c index 698075a..2415fec 100644 --- a/src/events/keyboard_hid_report_event.c +++ b/src/events/keyboard_hid_report_event.c @@ -42,6 +42,18 @@ static const char *protocol_mode_name(enum keyboard_protocol_mode protocol_mode) } } +static const char *queue_policy_name(enum hid_queue_policy queue_policy) +{ + switch (queue_policy) { + case HID_QUEUE_POLICY_LATEST: + return "latest"; + case HID_QUEUE_POLICY_FIFO: + return "fifo"; + default: + return "?"; + } +} + static void log_keyboard_hid_report_event(const struct app_event_header *aeh) { const struct keyboard_hid_report_event *event = @@ -49,10 +61,12 @@ static void log_keyboard_hid_report_event(const struct app_event_header *aeh) char log_buf[KEYBOARD_HID_REPORT_EVENT_LOG_BUF_LEN]; int pos; - pos = snprintf(log_buf, sizeof(log_buf), "mode:%s type:%s protocol:%s len:%zu", + pos = snprintf(log_buf, sizeof(log_buf), + "mode:%s type:%s protocol:%s queue:%s len:%zu", mode_name(event->mode), report_type_name(event->report_type), protocol_mode_name(event->protocol_mode), + queue_policy_name(event->queue_policy), event->dyndata.size); if ((pos > 0) && (pos < sizeof(log_buf))) { for (size_t i = 0; i < event->dyndata.size; i++) { @@ -89,15 +103,17 @@ static void profile_keyboard_hid_report_event(struct log_event_buf *buf, nrf_profiler_log_encode_uint8(buf, event->mode); nrf_profiler_log_encode_uint8(buf, event->report_type); nrf_profiler_log_encode_uint8(buf, event->protocol_mode); + nrf_profiler_log_encode_uint8(buf, event->queue_policy); nrf_profiler_log_encode_uint8(buf, (uint8_t)event->dyndata.size); } APP_EVENT_INFO_DEFINE(keyboard_hid_report_event, ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U8), - ENCODE("mode", "report_type", "protocol_mode", "len"), + ENCODE("mode", "report_type", "protocol_mode", "queue_policy", "len"), profile_keyboard_hid_report_event); APP_EVENT_TYPE_DEFINE(keyboard_hid_report_event, diff --git a/src/hid_flowctrl_module.c b/src/hid_flowctrl_module.c new file mode 100644 index 0000000..32f295b --- /dev/null +++ b/src/hid_flowctrl_module.c @@ -0,0 +1,390 @@ +#include +#include +#include + +#include + +#define MODULE hid_flowctrl_module +#include +#include + +#include + +#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 keyboard_report_type report_type; + uint16_t sequence; +}; + +static struct hid_transport_state_data usb_state = { + .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 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) +{ + if ((current_mode != MODE_SWITCH_USB) || !usb_state.ready || in_flight.active) { + return false; + } + + if (report_type == KEYBOARD_REPORT_TYPE_KEYS) { + return usb_state.keys_ready; + } + + return usb_state.consumer_ready; +} + +static void submit_hid_tx_report_event(enum keyboard_report_type report_type, + enum keyboard_protocol_mode protocol_mode, + const uint8_t *data, size_t size) +{ + struct hid_tx_report_event *event = new_hid_tx_report_event(size); + + event->transport = HID_TRANSPORT_USB; + event->report_type = report_type; + event->protocol_mode = protocol_mode; + event->sequence = next_sequence++; + memcpy(event->dyndata.data, data, size); + + in_flight.active = true; + in_flight.report_type = report_type; + in_flight.sequence = event->sequence; + + APP_EVENT_SUBMIT(event); +} + +static void try_send_next(void) +{ + struct queued_report queued; + + if (!running || in_flight.active || (current_mode != MODE_SWITCH_USB) || !usb_state.ready) { + return; + } + + if (pending_keys.valid && transport_can_send_report(KEYBOARD_REPORT_TYPE_KEYS)) { + if (pending_keys.protocol_mode != usb_state.protocol_mode) { + LOG_WRN("Drop stale keys report after protocol change"); + pending_keys.valid = false; + } else { + submit_hid_tx_report_event(pending_keys.report_type, + pending_keys.protocol_mode, + 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)) { + submit_hid_tx_report_event(queued.report_type, + queued.protocol_mode, + queued.data, + queued.size); + return; + } + + if (pending_consumer_latest.valid && + transport_can_send_report(KEYBOARD_REPORT_TYPE_CONSUMER)) { + submit_hid_tx_report_event(pending_consumer_latest.report_type, + pending_consumer_latest.protocol_mode, + 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)) { + 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) +{ + if (event->transport != HID_TRANSPORT_USB) { + return false; + } + + usb_state.ready = event->ready; + usb_state.keys_ready = event->keys_ready; + usb_state.consumer_ready = event->consumer_ready; + + if (usb_state.protocol_mode != event->protocol_mode) { + usb_state.protocol_mode = event->protocol_mode; + pending_keys.valid = false; + } + + if (!usb_state.ready) { + 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 ((event->transport != HID_TRANSPORT_USB) || !in_flight.active) { + 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) +{ + current_mode = event->mode; + + if (current_mode != MODE_SWITCH_USB) { + clear_pending_reports(); + } + + try_send_next(); + return false; +} + +static int module_init(void) +{ + clear_pending_reports(); + current_mode = MODE_SWITCH_USB; + usb_state.ready = false; + usb_state.keys_ready = false; + usb_state.consumer_ready = false; + usb_state.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); diff --git a/src/keyboard_core_module.c b/src/keyboard_core_module.c index 77cdbb1..3987198 100644 --- a/src/keyboard_core_module.c +++ b/src/keyboard_core_module.c @@ -14,6 +14,7 @@ #include #include +#include "encoder_event.h" #include "keyboard_core.h" #include "keyboard_hid_report_event.h" #include "mode_switch_event.h" @@ -228,6 +229,7 @@ static void build_consumer_report(uint8_t report[KEYBOARD_CONSUMER_REPORT_SIZE]) } static void submit_keyboard_report_event(enum keyboard_report_type report_type, + enum hid_queue_policy queue_policy, const uint8_t *data, size_t size) { struct keyboard_hid_report_event *event = @@ -236,11 +238,49 @@ static void submit_keyboard_report_event(enum keyboard_report_type report_type, event->mode = current_mode; event->report_type = report_type; event->protocol_mode = protocol_mode; + event->queue_policy = queue_policy; memcpy(event->dyndata.data, data, size); APP_EVENT_SUBMIT(event); } +static void submit_consumer_fifo_frame(uint16_t usage_id) +{ + uint8_t report_buf[KEYBOARD_CONSUMER_REPORT_SIZE]; + + if (!running || !mode_valid) { + return; + } + + sys_put_le16(usage_id, report_buf); + submit_keyboard_report_event(KEYBOARD_REPORT_TYPE_CONSUMER, + HID_QUEUE_POLICY_FIFO, + report_buf, + KEYBOARD_CONSUMER_REPORT_SIZE); +} + +static void submit_consumer_pulse_frames(enum keyboard_consumer_control control_id, + uint8_t pulse_count) +{ + uint16_t usage_id; + + if (control_id >= KEYBOARD_CONSUMER_CTRL_COUNT) { + LOG_WRN("Unsupported consumer control id %u", control_id); + return; + } + + usage_id = consumer_usage_map[control_id]; + if (usage_id == 0U) { + LOG_WRN("Unmapped consumer control id %u", control_id); + return; + } + + for (uint8_t i = 0; i < pulse_count; i++) { + submit_consumer_fifo_frame(usage_id); + submit_consumer_fifo_frame(0U); + } +} + static void emit_keys_report(bool force) { uint8_t report_buf[KEYBOARD_NKRO_REPORT_SIZE]; @@ -271,7 +311,10 @@ static void emit_keys_report(bool force) memcpy(cache_buf, report_buf, report_size); *cache_valid = true; - submit_keyboard_report_event(KEYBOARD_REPORT_TYPE_KEYS, report_buf, report_size); + submit_keyboard_report_event(KEYBOARD_REPORT_TYPE_KEYS, + HID_QUEUE_POLICY_LATEST, + report_buf, + report_size); } static void emit_consumer_report(bool force) @@ -293,6 +336,7 @@ static void emit_consumer_report(bool force) reports_cache.consumer_valid = true; submit_keyboard_report_event(KEYBOARD_REPORT_TYPE_CONSUMER, + HID_QUEUE_POLICY_LATEST, report_buf, KEYBOARD_CONSUMER_REPORT_SIZE); } @@ -416,12 +460,33 @@ static bool handle_mode_switch_event(const struct mode_switch_event *event) return false; } +static bool handle_encoder_event(const struct encoder_event *event) +{ + if (!running || !mode_valid) { + return false; + } + + if (event->detents > 0) { + submit_consumer_pulse_frames(KEYBOARD_CONSUMER_CTRL_VOLUME_UP, + (uint8_t)event->detents); + } else if (event->detents < 0) { + submit_consumer_pulse_frames(KEYBOARD_CONSUMER_CTRL_VOLUME_DOWN, + (uint8_t)(-event->detents)); + } + + return false; +} + static bool app_event_handler(const struct app_event_header *aeh) { if (is_button_event(aeh)) { return handle_button_event(cast_button_event(aeh)); } + if (is_encoder_event(aeh)) { + return handle_encoder_event(cast_encoder_event(aeh)); + } + if (is_set_protocol_event(aeh)) { const struct set_protocol_event *event = cast_set_protocol_event(aeh); @@ -496,6 +561,7 @@ static bool app_event_handler(const struct app_event_header *aeh) APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, button_event); +APP_EVENT_SUBSCRIBE(MODULE, encoder_event); APP_EVENT_SUBSCRIBE(MODULE, set_protocol_event); APP_EVENT_SUBSCRIBE(MODULE, mode_switch_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); diff --git a/src/usb_hid_module.c b/src/usb_hid_module.c index e9275df..ab06bd5 100644 --- a/src/usb_hid_module.c +++ b/src/usb_hid_module.c @@ -18,8 +18,10 @@ #include #include "hid_led_event.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 "set_protocol_event.h" LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); @@ -107,6 +109,8 @@ static bool running; static bool usb_enabled; static bool keyboard_report_in_flight; static bool consumer_report_in_flight; +static uint16_t keyboard_in_flight_sequence; +static uint16_t consumer_in_flight_sequence; UDC_STATIC_BUF_DEFINE(keyboard_tx_buf, KEYBOARD_NKRO_REPORT_SIZE); UDC_STATIC_BUF_DEFINE(consumer_tx_buf, KEYBOARD_CONSUMER_REPORT_SIZE); @@ -158,6 +162,34 @@ static void submit_hid_led_event(uint8_t led_bm) APP_EVENT_SUBMIT(event); } +static void submit_transport_state_event(void) +{ + struct hid_transport_state_event *event = new_hid_transport_state_event(); + + event->transport = HID_TRANSPORT_USB; + event->ready = usb_enabled; + event->keys_ready = usb_enabled && + hid_ifaces[USB_HID_INTERFACE_KEYBOARD].ready; + event->consumer_ready = usb_enabled && + hid_ifaces[USB_HID_INTERFACE_CONSUMER].ready; + event->protocol_mode = keyboard_protocol_mode; + + APP_EVENT_SUBMIT(event); +} + +static void submit_hid_report_sent_event(enum keyboard_report_type report_type, + uint16_t sequence, bool error) +{ + struct hid_report_sent_event *event = new_hid_report_sent_event(); + + event->transport = HID_TRANSPORT_USB; + event->report_type = report_type; + event->sequence = sequence; + event->error = error; + + APP_EVENT_SUBMIT(event); +} + static void keyboard_iface_ready(const struct device *dev, const bool ready) { struct usb_hid_interface_state *iface = iface_from_dev(dev); @@ -172,6 +204,7 @@ static void keyboard_iface_ready(const struct device *dev, const bool ready) } LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); + submit_transport_state_event(); } static int keyboard_get_report(const struct device *dev, @@ -230,6 +263,7 @@ static void keyboard_set_protocol(const struct device *dev, const uint8_t proto) LOG_INF("USB keyboard protocol -> %s", (new_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? "boot" : "report"); submit_set_protocol_event(new_mode); + submit_transport_state_event(); } static void keyboard_input_report_done(const struct device *dev, @@ -239,6 +273,8 @@ static void keyboard_input_report_done(const struct device *dev, keyboard_report_in_flight = false; LOG_DBG("USB keyboard report sent by %s", dev->name); + submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_KEYS, + keyboard_in_flight_sequence, false); } static void keyboard_output_report(const struct device *dev, @@ -280,6 +316,7 @@ static void consumer_iface_ready(const struct device *dev, const bool ready) } LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); + submit_transport_state_event(); } static int consumer_get_report(const struct device *dev, @@ -337,6 +374,8 @@ static void consumer_input_report_done(const struct device *dev, consumer_report_in_flight = false; LOG_DBG("USB consumer report sent by %s", dev->name); + submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_CONSUMER, + consumer_in_flight_sequence, false); } static void consumer_output_report(const struct device *dev, @@ -372,6 +411,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, LOG_ERR("usbd_enable failed (%d)", err); } else { usb_enabled = true; + submit_transport_state_event(); } } @@ -391,6 +431,7 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, } keyboard_report_in_flight = false; consumer_report_in_flight = false; + submit_transport_state_event(); } } @@ -510,6 +551,7 @@ static int module_start(void) } running = true; + submit_transport_state_event(); return 0; } @@ -518,11 +560,11 @@ static void module_pause(void) running = false; } -static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_event *event) +static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) { int err; - if (!running || (event->mode != MODE_SWITCH_USB)) { + if (!running || (event->transport != HID_TRANSPORT_USB)) { return false; } @@ -550,8 +592,11 @@ static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_ev keyboard_tx_buf); if (err) { LOG_WRN("USB keyboard report submit failed (%d)", err); + submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_KEYS, + event->sequence, true); } else { keyboard_report_in_flight = true; + keyboard_in_flight_sequence = event->sequence; } return false; @@ -576,8 +621,11 @@ static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_ev consumer_tx_buf); if (err) { LOG_WRN("USB consumer report submit failed (%d)", err); + submit_hid_report_sent_event(KEYBOARD_REPORT_TYPE_CONSUMER, + event->sequence, true); } else { consumer_report_in_flight = true; + consumer_in_flight_sequence = event->sequence; } } @@ -586,9 +634,8 @@ static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_ev 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_tx_report_event(aeh)) { + return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh)); } if (is_module_state_event(aeh)) { @@ -647,6 +694,6 @@ static bool app_event_handler(const struct app_event_header *aeh) APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); -APP_EVENT_SUBSCRIBE(MODULE, keyboard_hid_report_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);