diff --git a/CMakeLists.txt b/CMakeLists.txt index 83ef765..17fddfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,15 +39,16 @@ target_sources(app PRIVATE src/protocol_module.c src/usb_cdc_module.c src/usb_device_module.c - src/usb_hid_module.c + src/usb_hid_consumer_module.c + src/usb_hid_keyboard_module.c src/events/bat_state_event.c src/events/datetime_event.c src/events/encoder_event.c src/events/function_bitmap_state_event.c src/events/function_bitmap_update_event.c + src/events/hid_channel_state_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/events/led_strip_en_event.c src/mode_switch_module.c diff --git a/inc/events/hid_channel_state_event.h b/inc/events/hid_channel_state_event.h new file mode 100644 index 0000000..932de9f --- /dev/null +++ b/inc/events/hid_channel_state_event.h @@ -0,0 +1,38 @@ +#ifndef BLINKY_HID_CHANNEL_STATE_EVENT_H_ +#define BLINKY_HID_CHANNEL_STATE_EVENT_H_ + +#include +#include + +#include "keyboard_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hid_channel_state_event { + struct app_event_header header; + enum hid_send_channel channel; + uint8_t report_ready_bm; + enum keyboard_protocol_mode protocol_mode; +}; + +APP_EVENT_TYPE_DECLARE(hid_channel_state_event); + +static inline void submit_hid_channel_state_event( + enum hid_send_channel channel, uint8_t report_ready_bm, + enum keyboard_protocol_mode protocol_mode) +{ + struct hid_channel_state_event *event = new_hid_channel_state_event(); + + event->channel = channel; + event->report_ready_bm = report_ready_bm; + event->protocol_mode = protocol_mode; + APP_EVENT_SUBMIT(event); +} + +#ifdef __cplusplus +} +#endif + +#endif /* BLINKY_HID_CHANNEL_STATE_EVENT_H_ */ diff --git a/inc/events/hid_report_sent_event.h b/inc/events/hid_report_sent_event.h index 208b984..a35fe0e 100644 --- a/inc/events/hid_report_sent_event.h +++ b/inc/events/hid_report_sent_event.h @@ -12,7 +12,7 @@ extern "C" { struct hid_report_sent_event { struct app_event_header header; - enum hid_transport transport; + enum hid_send_channel channel; enum keyboard_report_type report_type; uint16_t sequence; bool error; @@ -20,13 +20,13 @@ struct hid_report_sent_event { APP_EVENT_TYPE_DECLARE(hid_report_sent_event); -static inline void submit_hid_report_sent_event(enum hid_transport transport, +static inline void submit_hid_report_sent_event(enum hid_send_channel channel, enum keyboard_report_type report_type, uint16_t sequence, bool error) { struct hid_report_sent_event *event = new_hid_report_sent_event(); - event->transport = transport; + event->channel = channel; event->report_type = report_type; event->sequence = sequence; event->error = error; diff --git a/inc/events/hid_transport_state_event.h b/inc/events/hid_transport_state_event.h deleted file mode 100644 index c1e1149..0000000 --- a/inc/events/hid_transport_state_event.h +++ /dev/null @@ -1,42 +0,0 @@ -#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); - -static inline void submit_hid_transport_state_event( - enum hid_transport transport, bool ready, bool keys_ready, - bool consumer_ready, enum keyboard_protocol_mode protocol_mode) -{ - struct hid_transport_state_event *event = new_hid_transport_state_event(); - - event->transport = transport; - event->ready = ready; - event->keys_ready = keys_ready; - event->consumer_ready = consumer_ready; - event->protocol_mode = protocol_mode; - APP_EVENT_SUBMIT(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 index 063732b..05b4efe 100644 --- a/inc/events/hid_tx_report_event.h +++ b/inc/events/hid_tx_report_event.h @@ -16,7 +16,7 @@ extern "C" { struct hid_tx_report_event { struct app_event_header header; - enum hid_transport transport; + enum hid_send_channel channel; enum keyboard_report_type report_type; enum keyboard_protocol_mode protocol_mode; uint16_t sequence; @@ -25,7 +25,7 @@ struct hid_tx_report_event { APP_EVENT_TYPE_DYNDATA_DECLARE(hid_tx_report_event); -static inline int submit_hid_tx_report_event(enum hid_transport transport, +static inline int submit_hid_tx_report_event(enum hid_send_channel channel, enum keyboard_report_type report_type, enum keyboard_protocol_mode protocol_mode, uint16_t sequence, @@ -38,7 +38,7 @@ static inline int submit_hid_tx_report_event(enum hid_transport transport, } event = new_hid_tx_report_event(size); - event->transport = transport; + event->channel = channel; event->report_type = report_type; event->protocol_mode = protocol_mode; event->sequence = sequence; diff --git a/inc/keyboard_core.h b/inc/keyboard_core.h index 571932f..588f000 100644 --- a/inc/keyboard_core.h +++ b/inc/keyboard_core.h @@ -36,6 +36,13 @@ enum hid_transport { HID_TRANSPORT_COUNT, }; +enum hid_send_channel { + HID_SEND_CH_USB_KEYS, + HID_SEND_CH_USB_CONSUMER, + HID_SEND_CH_BLE_SHARED, + HID_SEND_CH_COUNT, +}; + enum keyboard_consumer_control { KEYBOARD_CONSUMER_CTRL_MUTE, KEYBOARD_CONSUMER_CTRL_VOLUME_UP, diff --git a/src/ble_hid_module.c b/src/ble_hid_module.c index b17d652..402cd6b 100644 --- a/src/ble_hid_module.c +++ b/src/ble_hid_module.c @@ -14,8 +14,8 @@ #include #include "hid_led_event.h" +#include "hid_channel_state_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 "set_protocol_event.h" @@ -101,15 +101,22 @@ static const uint8_t hid_report_desc[] = { static void submit_ble_transport_state_event(void) { bool ready = running && secured && (active_conn != NULL); + uint8_t report_ready_bm = 0U; - submit_hid_transport_state_event( - HID_TRANSPORT_BLE, - ready, - ready && ((protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? - boot_keyboard_notify_enabled : - keyboard_report_notify_enabled), - ready && (protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT) && - consumer_report_notify_enabled, + if (ready && ((protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? + boot_keyboard_notify_enabled : + keyboard_report_notify_enabled)) { + report_ready_bm |= BIT(KEYBOARD_REPORT_TYPE_KEYS); + } + + if (ready && (protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT) && + consumer_report_notify_enabled) { + report_ready_bm |= BIT(KEYBOARD_REPORT_TYPE_CONSUMER); + } + + submit_hid_channel_state_event( + HID_SEND_CH_BLE_SHARED, + report_ready_bm, protocol_mode); } @@ -141,7 +148,8 @@ static void hid_report_complete_cb(struct bt_conn *conn, void *user_data) return; } - submit_hid_report_sent_event(HID_TRANSPORT_BLE, in_flight.report_type, + submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED, + in_flight.report_type, in_flight.sequence, false); in_flight.active = false; } @@ -317,7 +325,8 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) { int err; - if (!running || (event->transport != HID_TRANSPORT_BLE) || in_flight.active) { + if (!running || (event->channel != HID_SEND_CH_BLE_SHARED) || + in_flight.active) { return false; } @@ -353,7 +362,7 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) if (err) { in_flight.active = false; LOG_WRN("BLE keyboard report submit failed (%d)", err); - submit_hid_report_sent_event(HID_TRANSPORT_BLE, + submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED, KEYBOARD_REPORT_TYPE_KEYS, event->sequence, true); } @@ -380,7 +389,7 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) if (err) { in_flight.active = false; LOG_WRN("BLE consumer report submit failed (%d)", err); - submit_hid_report_sent_event(HID_TRANSPORT_BLE, + submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED, KEYBOARD_REPORT_TYPE_CONSUMER, event->sequence, true); } diff --git a/src/events/hid_channel_state_event.c b/src/events/hid_channel_state_event.c new file mode 100644 index 0000000..9b4499c --- /dev/null +++ b/src/events/hid_channel_state_event.c @@ -0,0 +1,51 @@ +#include "hid_channel_state_event.h" + +static const char *channel_name(enum hid_send_channel channel) +{ + switch (channel) { + case HID_SEND_CH_USB_KEYS: + return "usb_keys"; + case HID_SEND_CH_USB_CONSUMER: + return "usb_consumer"; + case HID_SEND_CH_BLE_SHARED: + return "ble_shared"; + default: + return "?"; + } +} + +static void log_hid_channel_state_event(const struct app_event_header *aeh) +{ + const struct hid_channel_state_event *event = + cast_hid_channel_state_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, + "channel:%s ready_bm:0x%02x protocol:%d", + channel_name(event->channel), + event->report_ready_bm, + event->protocol_mode); +} + +static void profile_hid_channel_state_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_channel_state_event *event = + cast_hid_channel_state_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->channel); + nrf_profiler_log_encode_uint8(buf, event->report_ready_bm); + nrf_profiler_log_encode_uint8(buf, event->protocol_mode); +} + +APP_EVENT_INFO_DEFINE(hid_channel_state_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8), + ENCODE("channel", "ready_bm", "protocol_mode"), + profile_hid_channel_state_event); + +APP_EVENT_TYPE_DEFINE(hid_channel_state_event, + log_hid_channel_state_event, + &hid_channel_state_event_info, + APP_EVENT_FLAGS_CREATE( + APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/hid_report_sent_event.c b/src/events/hid_report_sent_event.c index c908f5a..d880ef0 100644 --- a/src/events/hid_report_sent_event.c +++ b/src/events/hid_report_sent_event.c @@ -1,12 +1,14 @@ #include "hid_report_sent_event.h" -static const char *transport_name(enum hid_transport transport) +static const char *channel_name(enum hid_send_channel channel) { - switch (transport) { - case HID_TRANSPORT_USB: - return "USB"; - case HID_TRANSPORT_BLE: - return "BLE"; + switch (channel) { + case HID_SEND_CH_USB_KEYS: + return "usb_keys"; + case HID_SEND_CH_USB_CONSUMER: + return "usb_consumer"; + case HID_SEND_CH_BLE_SHARED: + return "ble_shared"; default: return "?"; } @@ -28,8 +30,8 @@ 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), + APP_EVENT_MANAGER_LOG(aeh, "channel:%s type:%s seq:%u error:%u", + channel_name(event->channel), report_type_name(event->report_type), event->sequence, event->error); @@ -40,7 +42,7 @@ static void profile_hid_report_sent_event(struct log_event_buf *buf, { 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->channel); 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); @@ -51,7 +53,7 @@ APP_EVENT_INFO_DEFINE(hid_report_sent_event, NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U16, NRF_PROFILER_ARG_U8), - ENCODE("transport", "report_type", "sequence", "error"), + ENCODE("channel", "report_type", "sequence", "error"), profile_hid_report_sent_event); APP_EVENT_TYPE_DEFINE(hid_report_sent_event, diff --git a/src/events/hid_transport_state_event.c b/src/events/hid_transport_state_event.c deleted file mode 100644 index ce794eb..0000000 --- a/src/events/hid_transport_state_event.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "hid_transport_state_event.h" - -static const char *transport_name(enum hid_transport transport) -{ - switch (transport) { - case HID_TRANSPORT_USB: - return "USB"; - case HID_TRANSPORT_BLE: - return "BLE"; - 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 index 06f8551..037f510 100644 --- a/src/events/hid_tx_report_event.c +++ b/src/events/hid_tx_report_event.c @@ -4,13 +4,15 @@ #define HID_TX_REPORT_EVENT_LOG_BUF_LEN 192 -static const char *transport_name(enum hid_transport transport) +static const char *channel_name(enum hid_send_channel channel) { - switch (transport) { - case HID_TRANSPORT_USB: - return "USB"; - case HID_TRANSPORT_BLE: - return "BLE"; + switch (channel) { + case HID_SEND_CH_USB_KEYS: + return "usb_keys"; + case HID_SEND_CH_USB_CONSUMER: + return "usb_consumer"; + case HID_SEND_CH_BLE_SHARED: + return "ble_shared"; default: return "?"; } @@ -47,8 +49,8 @@ static void log_hid_tx_report_event(const struct app_event_header *aeh) int pos; pos = snprintf(log_buf, sizeof(log_buf), - "transport:%s type:%s protocol:%s seq:%u len:%zu", - transport_name(event->transport), + "channel:%s type:%s protocol:%s seq:%u len:%zu", + channel_name(event->channel), report_type_name(event->report_type), protocol_mode_name(event->protocol_mode), event->sequence, @@ -84,7 +86,7 @@ static void profile_hid_tx_report_event(struct log_event_buf *buf, { 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->channel); 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); @@ -97,7 +99,7 @@ APP_EVENT_INFO_DEFINE(hid_tx_report_event, NRF_PROFILER_ARG_U8, NRF_PROFILER_ARG_U16, NRF_PROFILER_ARG_U8), - ENCODE("transport", "report_type", "protocol_mode", "sequence", "len"), + ENCODE("channel", "report_type", "protocol_mode", "sequence", "len"), profile_hid_tx_report_event); APP_EVENT_TYPE_DEFINE(hid_tx_report_event, diff --git a/src/hid_flowctrl_module.c b/src/hid_flowctrl_module.c index a234050..2436853 100644 --- a/src/hid_flowctrl_module.c +++ b/src/hid_flowctrl_module.c @@ -10,8 +10,8 @@ #include +#include "hid_channel_state_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" @@ -37,25 +37,26 @@ struct queued_report { uint8_t data[HID_FLOWCTRL_REPORT_DATA_MAX]; }; -struct hid_transport_state_data { - bool ready; - bool keys_ready; - bool consumer_ready; +struct hid_channel_state_data { + uint8_t report_ready_bm; enum keyboard_protocol_mode protocol_mode; }; struct in_flight_report { bool active; - enum hid_transport transport; + enum hid_send_channel channel; enum keyboard_report_type report_type; uint16_t sequence; }; -static struct hid_transport_state_data transport_state[HID_TRANSPORT_COUNT] = { - [HID_TRANSPORT_USB] = { +static struct hid_channel_state_data channel_state[HID_SEND_CH_COUNT] = { + [HID_SEND_CH_USB_KEYS] = { .protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT, }, - [HID_TRANSPORT_BLE] = { + [HID_SEND_CH_USB_CONSUMER] = { + .protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT, + }, + [HID_SEND_CH_BLE_SHARED] = { .protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT, }, }; @@ -65,21 +66,28 @@ 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 struct in_flight_report in_flight[HID_SEND_CH_COUNT]; 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) +static bool current_mode_to_channel(enum keyboard_report_type report_type, + enum hid_send_channel *channel) { - switch (mode) { + if (channel == NULL) { + return false; + } + + switch (current_mode) { case MODE_SWITCH_USB: - *transport = HID_TRANSPORT_USB; + *channel = (report_type == KEYBOARD_REPORT_TYPE_KEYS) ? + HID_SEND_CH_USB_KEYS : + HID_SEND_CH_USB_CONSUMER; return true; case MODE_SWITCH_BLE: - *transport = HID_TRANSPORT_BLE; + *channel = HID_SEND_CH_BLE_SHARED; return true; default: @@ -131,102 +139,137 @@ static bool consumer_fifo_pop(struct queued_report *entry) return true; } -static bool transport_can_send_report(enum keyboard_report_type report_type) +static bool channel_can_send_report(enum hid_send_channel channel, + enum keyboard_report_type report_type, + enum keyboard_protocol_mode protocol_mode) { - enum hid_transport transport; - struct hid_transport_state_data *state; + const struct hid_channel_state_data *state = &channel_state[channel]; - if (!mode_to_transport(current_mode, &transport) || in_flight.active) { - return false; - } - - state = &transport_state[transport]; - - if (!state->ready) { + if (in_flight[channel].active) { return false; } if (report_type == KEYBOARD_REPORT_TYPE_KEYS) { - return state->keys_ready; + return (state->report_ready_bm & BIT(KEYBOARD_REPORT_TYPE_KEYS)) && + (state->protocol_mode == protocol_mode); } - return state->consumer_ready; + if (channel == HID_SEND_CH_BLE_SHARED) { + return (state->report_ready_bm & BIT(KEYBOARD_REPORT_TYPE_CONSUMER)) && + (state->protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT); + } + + return (state->report_ready_bm & BIT(KEYBOARD_REPORT_TYPE_CONSUMER)) != 0U; +} + +static void try_send_keys(void) +{ + enum hid_send_channel channel; + + if (!pending_keys.valid) { + return; + } + + if (!current_mode_to_channel(KEYBOARD_REPORT_TYPE_KEYS, &channel)) { + return; + } + + if (!channel_can_send_report(channel, pending_keys.report_type, + 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; +} + +static void try_send_consumer_fifo(void) +{ + struct queued_report queued; + enum hid_send_channel channel; + + if (consumer_fifo_count == 0U) { + return; + } + + if (!current_mode_to_channel(KEYBOARD_REPORT_TYPE_CONSUMER, &channel)) { + return; + } + + if (!consumer_fifo_pop(&queued)) { + return; + } + + if (!channel_can_send_report(channel, queued.report_type, + queued.protocol_mode)) { + if (queued.protocol_mode == 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++; + (void)submit_hid_tx_report_event(channel, queued.report_type, + queued.protocol_mode, + in_flight[channel].sequence, + queued.data, queued.size); +} + +static void try_send_consumer_latest(void) +{ + enum hid_send_channel channel; + + if (!pending_consumer_latest.valid) { + return; + } + + if (!current_mode_to_channel(KEYBOARD_REPORT_TYPE_CONSUMER, &channel)) { + return; + } + + if (!channel_can_send_report(channel, + pending_consumer_latest.report_type, + 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++; + (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; } 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)) { + if (!running) { 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 { - 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 { - 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 { - 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; - } - } + try_send_keys(); + try_send_consumer_fifo(); + try_send_consumer_latest(); } -static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_event *event) +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))) { @@ -257,41 +300,18 @@ static bool handle_keyboard_hid_report_event(const struct keyboard_hid_report_ev return false; } -static bool handle_hid_transport_state_event(const struct hid_transport_state_event *event) +static bool handle_hid_channel_state_event( + const struct hid_channel_state_event *event) { - enum hid_transport active_transport; - struct hid_transport_state_data *state; - - if (event->transport >= HID_TRANSPORT_COUNT) { + if (event->channel >= HID_SEND_CH_COUNT) { return false; } - state = &transport_state[event->transport]; + channel_state[event->channel].report_ready_bm = event->report_ready_bm; + channel_state[event->channel].protocol_mode = event->protocol_mode; - 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; + if (event->report_ready_bm == 0U) { + in_flight[event->channel].active = false; } try_send_next(); @@ -300,17 +320,21 @@ static bool handle_hid_transport_state_event(const struct hid_transport_state_ev static bool handle_hid_report_sent_event(const struct hid_report_sent_event *event) { - if (!in_flight.active || (event->transport != in_flight.transport)) { + if (event->channel >= HID_SEND_CH_COUNT) { return false; } - if (event->sequence != in_flight.sequence) { + if (!in_flight[event->channel].active) { + return false; + } + + if (event->sequence != in_flight[event->channel].sequence) { LOG_WRN("Unexpected HID sent sequence %u (expected %u)", - event->sequence, in_flight.sequence); + event->sequence, in_flight[event->channel].sequence); return false; } - in_flight.active = false; + in_flight[event->channel].active = false; if (event->error) { LOG_WRN("HID report send failed for seq %u", event->sequence); @@ -339,18 +363,14 @@ 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 = + memset(channel_state, 0, sizeof(channel_state)); + channel_state[HID_SEND_CH_USB_KEYS].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 = + channel_state[HID_SEND_CH_USB_CONSUMER].protocol_mode = + KEYBOARD_PROTOCOL_MODE_REPORT; + channel_state[HID_SEND_CH_BLE_SHARED].protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; next_sequence = 1U; - return 0; } @@ -362,7 +382,6 @@ static int module_start(void) running = true; try_send_next(); - return 0; } @@ -379,15 +398,18 @@ static void module_pause(void) 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)); + 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_channel_state_event(aeh)) { + return handle_hid_channel_state_event( + cast_hid_channel_state_event(aeh)); } if (is_hid_report_sent_event(aeh)) { - return handle_hid_report_sent_event(cast_hid_report_sent_event(aeh)); + return handle_hid_report_sent_event( + cast_hid_report_sent_event(aeh)); } if (is_mode_switch_event(aeh)) { @@ -395,7 +417,8 @@ static bool app_event_handler(const struct app_event_header *aeh) } if (is_module_state_event(aeh)) { - const struct module_state_event *event = cast_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; @@ -450,7 +473,7 @@ static bool app_event_handler(const struct app_event_header *aeh) 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_channel_state_event); APP_EVENT_SUBSCRIBE(MODULE, hid_report_sent_event); APP_EVENT_SUBSCRIBE(MODULE, mode_switch_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); diff --git a/src/usb_cdc_module.c b/src/usb_cdc_module.c index a61424a..378e434 100644 --- a/src/usb_cdc_module.c +++ b/src/usb_cdc_module.c @@ -43,7 +43,6 @@ static struct k_work_delayable rx_flush_work; static bool initialized; static bool running; static bool usb_active; -static bool usb_function_prepared; static bool dtr_ready; static bool rx_enabled; static uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE]; @@ -275,7 +274,6 @@ static int module_init(void) k_work_init_delayable(&control_work, control_work_handler); k_work_init_delayable(&rx_flush_work, rx_flush_work_handler); uart_irq_callback_set(cdc_dev, cdc_interrupt_handler); - usb_function_prepared = false; proto_rx_len = 0U; return 0; } diff --git a/src/usb_hid_consumer_module.c b/src/usb_hid_consumer_module.c new file mode 100644 index 0000000..0f95847 --- /dev/null +++ b/src/usb_hid_consumer_module.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include + +#include + +#define MODULE usb_hid_consumer_module +#include +#include + +#include +#include +#include +#include + +#include "hid_channel_state_event.h" +#include "hid_report_sent_event.h" +#include "hid_tx_report_event.h" +#include "keyboard_core.h" +#include "usb_function_hook.h" +#include "usb_state_event.h" + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +static const struct device *const hid_dev = + DEVICE_DT_GET(DT_NODELABEL(hid_consumer)); + +static const uint8_t consumer_report_desc[] = { + 0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x15, 0x00, + 0x26, 0xFF, 0x03, 0x19, 0x00, 0x2A, 0xFF, 0x03, + 0x75, 0x10, 0x95, 0x01, 0x81, 0x00, 0xC0 +}; + +static bool initialized; +static bool running; +static bool usb_active; +static bool iface_ready; +static bool report_in_flight; +static uint16_t in_flight_sequence; + +UDC_STATIC_BUF_DEFINE(consumer_tx_buf, KEYBOARD_CONSUMER_REPORT_SIZE); + +static void publish_consumer_state(void) +{ + bool ready = running && usb_active && iface_ready; + + submit_hid_channel_state_event(HID_SEND_CH_USB_CONSUMER, + ready ? BIT(KEYBOARD_REPORT_TYPE_CONSUMER) : 0U, + KEYBOARD_PROTOCOL_MODE_REPORT); +} + +static void consumer_iface_ready(const struct device *dev, const bool ready) +{ + ARG_UNUSED(dev); + + iface_ready = ready; + if (!ready) { + report_in_flight = false; + } + + LOG_INF("%s interface %s", hid_dev->name, ready ? "ready" : "not ready"); + publish_consumer_state(); +} + +static int consumer_get_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, uint8_t *const buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + + return -ENOTSUP; +} + +static int consumer_set_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, const uint8_t *const buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + + return -ENOTSUP; +} + +static void consumer_set_idle(const struct device *dev, + const uint8_t id, const uint32_t duration) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + ARG_UNUSED(duration); +} + +static uint32_t consumer_get_idle(const struct device *dev, const uint8_t id) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + + return 0U; +} + +static void consumer_set_protocol(const struct device *dev, const uint8_t proto) +{ + ARG_UNUSED(dev); + ARG_UNUSED(proto); +} + +static void consumer_input_report_done(const struct device *dev, + const uint8_t *const report) +{ + ARG_UNUSED(dev); + ARG_UNUSED(report); + + report_in_flight = false; + submit_hid_report_sent_event(HID_SEND_CH_USB_CONSUMER, + KEYBOARD_REPORT_TYPE_CONSUMER, + in_flight_sequence, false); +} + +static void consumer_output_report(const struct device *dev, + const uint16_t len, + const uint8_t *const buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(len); + ARG_UNUSED(buf); +} + +static const struct hid_device_ops consumer_ops = { + .iface_ready = consumer_iface_ready, + .get_report = consumer_get_report, + .set_report = consumer_set_report, + .set_idle = consumer_set_idle, + .get_idle = consumer_get_idle, + .set_protocol = consumer_set_protocol, + .input_report_done = consumer_input_report_done, + .output_report = consumer_output_report, +}; + +static int usb_hid_consumer_register_device(void) +{ + if (!device_is_ready(hid_dev)) { + LOG_ERR("HID device %s not ready", hid_dev->name); + return -ENODEV; + } + + return hid_device_register(hid_dev, consumer_report_desc, + sizeof(consumer_report_desc), &consumer_ops); +} + +USB_FUNCTION_HOOK_DEFINE(usb_hid_consumer_hook, usb_hid_consumer_register_device); + +static int module_init(void) +{ + usb_active = false; + iface_ready = false; + report_in_flight = false; + return 0; +} + +static int module_start(void) +{ + if (running) { + return 0; + } + + running = true; + publish_consumer_state(); + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + running = false; + report_in_flight = false; + publish_consumer_state(); +} + +static bool handle_usb_state_event(const struct usb_state_event *event) +{ + bool new_usb_active = (event->state == USB_STATE_ACTIVE); + + if (new_usb_active == usb_active) { + return false; + } + + usb_active = new_usb_active; + if (!usb_active) { + iface_ready = false; + report_in_flight = false; + } + + publish_consumer_state(); + return false; +} + +static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) +{ + int err; + + if (!running || !usb_active || + (event->channel != HID_SEND_CH_USB_CONSUMER)) { + return false; + } + + if (event->report_type != KEYBOARD_REPORT_TYPE_CONSUMER) { + return false; + } + + if (!iface_ready) { + return false; + } + + if (report_in_flight) { + LOG_WRN("Drop USB consumer report while previous report is in flight"); + return false; + } + + memcpy(consumer_tx_buf, event->dyndata.data, event->dyndata.size); + err = hid_device_submit_report(hid_dev, (uint16_t)event->dyndata.size, + consumer_tx_buf); + if (err) { + LOG_WRN("USB consumer report submit failed (%d)", err); + submit_hid_report_sent_event(HID_SEND_CH_USB_CONSUMER, + KEYBOARD_REPORT_TYPE_CONSUMER, + event->sequence, true); + } else { + report_in_flight = true; + in_flight_sequence = event->sequence; + } + + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_hid_tx_report_event(aeh)) { + return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh)); + } + + if (is_usb_state_event(aeh)) { + return handle_usb_state_event(cast_usb_state_event(aeh)); + } + + if (is_module_state_event(aeh)) { + const struct module_state_event *event = cast_module_state_event(aeh); + int err; + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + 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; + } + + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); diff --git a/src/usb_hid_keyboard_module.c b/src/usb_hid_keyboard_module.c new file mode 100644 index 0000000..1186bfe --- /dev/null +++ b/src/usb_hid_keyboard_module.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include + +#include + +#define MODULE usb_hid_keyboard_module +#include +#include + +#include +#include +#include +#include + +#include "hid_channel_state_event.h" +#include "hid_led_event.h" +#include "hid_report_sent_event.h" +#include "hid_tx_report_event.h" +#include "keyboard_core.h" +#include "set_protocol_event.h" +#include "usb_function_hook.h" +#include "usb_state_event.h" + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define KBD_LED_REPORT_SIZE 1U + +static const struct device *const hid_dev = DEVICE_DT_GET(DT_NODELABEL(hid_kbd)); + +static const uint8_t keyboard_report_desc[] = { + 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, + 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, + 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x05, 0x07, + 0x19, 0x00, 0x2A, 0xDF, 0x00, 0x15, 0x00, 0x25, + 0x01, 0x75, 0x01, 0x96, 0xE0, 0x00, 0x81, 0x02, + 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x15, 0x00, + 0x25, 0x01, 0x75, 0x01, 0x95, 0x05, 0x91, 0x02, + 0x75, 0x03, 0x95, 0x01, 0x91, 0x01, 0xC0 +}; + +static enum keyboard_protocol_mode keyboard_protocol_mode = + KEYBOARD_PROTOCOL_MODE_REPORT; +static bool initialized; +static bool running; +static bool usb_active; +static bool iface_ready; +static bool report_in_flight; +static uint16_t in_flight_sequence; + +UDC_STATIC_BUF_DEFINE(keyboard_tx_buf, KEYBOARD_NKRO_REPORT_SIZE); + +static void publish_keyboard_state(void) +{ + bool ready = running && usb_active && iface_ready; + + submit_hid_channel_state_event(HID_SEND_CH_USB_KEYS, + ready ? BIT(KEYBOARD_REPORT_TYPE_KEYS) : 0U, + keyboard_protocol_mode); +} + +static void keyboard_iface_ready(const struct device *dev, const bool ready) +{ + ARG_UNUSED(dev); + + iface_ready = ready; + if (!ready) { + report_in_flight = false; + } + + LOG_INF("%s interface %s", hid_dev->name, ready ? "ready" : "not ready"); + publish_keyboard_state(); +} + +static int keyboard_get_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, uint8_t *const buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + + return -ENOTSUP; +} + +static int keyboard_set_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, const uint8_t *const buf) +{ + ARG_UNUSED(dev); + ARG_UNUSED(type); + ARG_UNUSED(id); + ARG_UNUSED(len); + ARG_UNUSED(buf); + + return -ENOTSUP; +} + +static void keyboard_set_idle(const struct device *dev, + const uint8_t id, const uint32_t duration) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + ARG_UNUSED(duration); +} + +static uint32_t keyboard_get_idle(const struct device *dev, const uint8_t id) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + + return 0U; +} + +static enum keyboard_protocol_mode usb_proto_to_keyboard_proto(uint8_t proto) +{ + return (proto == HID_PROTOCOL_BOOT) ? KEYBOARD_PROTOCOL_MODE_BOOT + : KEYBOARD_PROTOCOL_MODE_REPORT; +} + +static void keyboard_set_protocol(const struct device *dev, const uint8_t proto) +{ + ARG_UNUSED(dev); + + enum keyboard_protocol_mode new_mode = usb_proto_to_keyboard_proto(proto); + + if (keyboard_protocol_mode == new_mode) { + return; + } + + keyboard_protocol_mode = new_mode; + LOG_INF("USB keyboard protocol -> %s", + (new_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? "boot" : "report"); + submit_set_protocol_event(HID_TRANSPORT_USB, new_mode); + publish_keyboard_state(); +} + +static void keyboard_input_report_done(const struct device *dev, + const uint8_t *const report) +{ + ARG_UNUSED(dev); + ARG_UNUSED(report); + + report_in_flight = false; + submit_hid_report_sent_event(HID_SEND_CH_USB_KEYS, + KEYBOARD_REPORT_TYPE_KEYS, + in_flight_sequence, false); +} + +static void keyboard_output_report(const struct device *dev, + const uint16_t len, + const uint8_t *const buf) +{ + ARG_UNUSED(dev); + + if ((len < KBD_LED_REPORT_SIZE) || (buf == NULL)) { + LOG_WRN("Invalid keyboard output report"); + return; + } + + submit_hid_led_event(HID_TRANSPORT_USB, buf[0]); +} + +static const struct hid_device_ops keyboard_ops = { + .iface_ready = keyboard_iface_ready, + .get_report = keyboard_get_report, + .set_report = keyboard_set_report, + .set_idle = keyboard_set_idle, + .get_idle = keyboard_get_idle, + .set_protocol = keyboard_set_protocol, + .input_report_done = keyboard_input_report_done, + .output_report = keyboard_output_report, +}; + +static int usb_hid_keyboard_register_device(void) +{ + if (!device_is_ready(hid_dev)) { + LOG_ERR("HID device %s not ready", hid_dev->name); + return -ENODEV; + } + + return hid_device_register(hid_dev, keyboard_report_desc, + sizeof(keyboard_report_desc), &keyboard_ops); +} + +USB_FUNCTION_HOOK_DEFINE(usb_hid_keyboard_hook, usb_hid_keyboard_register_device); + +static int module_init(void) +{ + keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; + usb_active = false; + iface_ready = false; + report_in_flight = false; + return 0; +} + +static int module_start(void) +{ + if (running) { + return 0; + } + + running = true; + publish_keyboard_state(); + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + running = false; + report_in_flight = false; + publish_keyboard_state(); +} + +static bool handle_usb_state_event(const struct usb_state_event *event) +{ + bool new_usb_active = (event->state == USB_STATE_ACTIVE); + + if (new_usb_active == usb_active) { + return false; + } + + usb_active = new_usb_active; + if (!usb_active) { + iface_ready = false; + report_in_flight = false; + } + + publish_keyboard_state(); + return false; +} + +static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) +{ + int err; + + if (!running || !usb_active || + (event->channel != HID_SEND_CH_USB_KEYS)) { + return false; + } + + if (event->report_type != KEYBOARD_REPORT_TYPE_KEYS) { + return false; + } + + if (event->protocol_mode != keyboard_protocol_mode) { + LOG_WRN("Drop USB keys report due to protocol mismatch"); + return false; + } + + if (!iface_ready) { + return false; + } + + if (report_in_flight) { + LOG_WRN("Drop USB keyboard report while previous report is in flight"); + return false; + } + + memcpy(keyboard_tx_buf, event->dyndata.data, event->dyndata.size); + err = hid_device_submit_report(hid_dev, (uint16_t)event->dyndata.size, + keyboard_tx_buf); + if (err) { + LOG_WRN("USB keyboard report submit failed (%d)", err); + submit_hid_report_sent_event(HID_SEND_CH_USB_KEYS, + KEYBOARD_REPORT_TYPE_KEYS, + event->sequence, true); + } else { + report_in_flight = true; + in_flight_sequence = event->sequence; + } + + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_hid_tx_report_event(aeh)) { + return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh)); + } + + if (is_usb_state_event(aeh)) { + return handle_usb_state_event(cast_usb_state_event(aeh)); + } + + if (is_module_state_event(aeh)) { + const struct module_state_event *event = cast_module_state_event(aeh); + int err; + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + 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; + } + + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event); +APP_EVENT_SUBSCRIBE(MODULE, usb_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); diff --git a/src/usb_hid_module.c b/src/usb_hid_module.c deleted file mode 100644 index 61b4da8..0000000 --- a/src/usb_hid_module.c +++ /dev/null @@ -1,586 +0,0 @@ -#include -#include -#include -#include - -#include - -#define MODULE usb_hid_module -#include -#include - -#include -#include -#include -#include -#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 "set_protocol_event.h" -#include "usb_function_hook.h" -#include "usb_state_event.h" - -LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); - -#define KBD_LED_REPORT_SIZE 1U - -enum usb_hid_interface { - USB_HID_INTERFACE_KEYBOARD, - USB_HID_INTERFACE_CONSUMER, - USB_HID_INTERFACE_COUNT, -}; - -struct usb_hid_interface_state { - const struct device *dev; - bool ready; -}; - -static const uint8_t keyboard_report_desc[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xA1, 0x01, /* Collection (Application) */ - 0x05, 0x07, /* Usage Page (Keyboard/Keypad) */ - 0x19, 0xE0, /* Usage Minimum (0xE0) */ - 0x29, 0xE7, /* Usage Maximum (0xE7) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - 0x05, 0x07, /* Usage Page (Keyboard/Keypad) */ - 0x19, 0x00, /* Usage Minimum (0x00) */ - 0x2A, 0xDF, 0x00, /* Usage Maximum (0x00DF) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x75, 0x01, /* Report Size (1) */ - 0x96, 0xE0, 0x00, /* Report Count (224) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x05, /* Report Count (5) */ - 0x91, 0x02, /* Output (Data,Var,Abs) */ - 0x75, 0x03, /* Report Size (3) */ - 0x95, 0x01, /* Report Count (1) */ - 0x91, 0x01, /* Output (Const,Array,Abs) */ - 0xC0 /* End Collection */ -}; - -static const uint8_t consumer_report_desc[] = { - 0x05, 0x0C, /* Usage Page (Consumer) */ - 0x09, 0x01, /* Usage (Consumer Control) */ - 0xA1, 0x01, /* Collection (Application) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x26, 0xFF, 0x03, /* Logical Maximum (1023) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x2A, 0xFF, 0x03, /* Usage Maximum (1023) */ - 0x75, 0x10, /* Report Size (16) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x00, /* Input (Data,Array,Abs) */ - 0xC0 /* End Collection */ -}; - -static struct usb_hid_interface_state hid_ifaces[USB_HID_INTERFACE_COUNT] = { - [USB_HID_INTERFACE_KEYBOARD] = { - .dev = DEVICE_DT_GET(DT_NODELABEL(hid_kbd)), - }, - [USB_HID_INTERFACE_CONSUMER] = { - .dev = DEVICE_DT_GET(DT_NODELABEL(hid_consumer)), - }, -}; - -static enum keyboard_protocol_mode keyboard_protocol_mode = - KEYBOARD_PROTOCOL_MODE_REPORT; -static bool initialized; -static bool running; -static bool usb_active; -static bool usb_function_prepared; -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); - -static struct usb_hid_interface_state *iface_from_dev(const struct device *dev) -{ - for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { - if (hid_ifaces[i].dev == dev) { - return &hid_ifaces[i]; - } - } - - return NULL; -} - -static enum keyboard_protocol_mode usb_proto_to_keyboard_proto(uint8_t proto) -{ - return (proto == HID_PROTOCOL_BOOT) ? KEYBOARD_PROTOCOL_MODE_BOOT - : KEYBOARD_PROTOCOL_MODE_REPORT; -} - -static void reset_usb_runtime_state(void) -{ - for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { - hid_ifaces[i].ready = false; - } - - usb_active = false; - keyboard_report_in_flight = false; - consumer_report_in_flight = false; -} - -static void submit_usb_transport_state_event(void) -{ - bool ready = running && usb_active; - - submit_hid_transport_state_event( - HID_TRANSPORT_USB, - ready, - ready && hid_ifaces[USB_HID_INTERFACE_KEYBOARD].ready, - ready && hid_ifaces[USB_HID_INTERFACE_CONSUMER].ready, - keyboard_protocol_mode); -} - -static void keyboard_iface_ready(const struct device *dev, const bool ready) -{ - struct usb_hid_interface_state *iface = iface_from_dev(dev); - - if (!iface) { - return; - } - - iface->ready = ready; - if (!ready) { - keyboard_report_in_flight = false; - } - - LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); - submit_usb_transport_state_event(); -} - -static int keyboard_get_report(const struct device *dev, - const uint8_t type, const uint8_t id, - const uint16_t len, uint8_t *const buf) -{ - ARG_UNUSED(dev); - ARG_UNUSED(type); - ARG_UNUSED(id); - ARG_UNUSED(len); - ARG_UNUSED(buf); - - return -ENOTSUP; -} - -static int keyboard_set_report(const struct device *dev, - const uint8_t type, const uint8_t id, - const uint16_t len, const uint8_t *const buf) -{ - ARG_UNUSED(dev); - ARG_UNUSED(type); - ARG_UNUSED(id); - ARG_UNUSED(len); - ARG_UNUSED(buf); - - return -ENOTSUP; -} - -static void keyboard_set_idle(const struct device *dev, - const uint8_t id, const uint32_t duration) -{ - ARG_UNUSED(dev); - ARG_UNUSED(id); - ARG_UNUSED(duration); -} - -static uint32_t keyboard_get_idle(const struct device *dev, const uint8_t id) -{ - ARG_UNUSED(dev); - ARG_UNUSED(id); - - return 0U; -} - -static void keyboard_set_protocol(const struct device *dev, const uint8_t proto) -{ - ARG_UNUSED(dev); - - enum keyboard_protocol_mode new_mode = usb_proto_to_keyboard_proto(proto); - - if (keyboard_protocol_mode == new_mode) { - return; - } - - keyboard_protocol_mode = new_mode; - LOG_INF("USB keyboard protocol -> %s", - (new_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? "boot" : "report"); - submit_set_protocol_event(HID_TRANSPORT_USB, new_mode); - submit_usb_transport_state_event(); -} - -static void keyboard_input_report_done(const struct device *dev, - const uint8_t *const report) -{ - ARG_UNUSED(report); - - keyboard_report_in_flight = false; - LOG_DBG("USB keyboard report sent by %s", dev->name); - submit_hid_report_sent_event(HID_TRANSPORT_USB, - KEYBOARD_REPORT_TYPE_KEYS, - keyboard_in_flight_sequence, false); -} - -static void keyboard_output_report(const struct device *dev, - const uint16_t len, - const uint8_t *const buf) -{ - ARG_UNUSED(dev); - - if ((len < KBD_LED_REPORT_SIZE) || (buf == NULL)) { - LOG_WRN("Invalid keyboard output report"); - return; - } - - submit_hid_led_event(HID_TRANSPORT_USB, buf[0]); -} - -static const struct hid_device_ops keyboard_ops = { - .iface_ready = keyboard_iface_ready, - .get_report = keyboard_get_report, - .set_report = keyboard_set_report, - .set_idle = keyboard_set_idle, - .get_idle = keyboard_get_idle, - .set_protocol = keyboard_set_protocol, - .input_report_done = keyboard_input_report_done, - .output_report = keyboard_output_report, -}; - -static void consumer_iface_ready(const struct device *dev, const bool ready) -{ - struct usb_hid_interface_state *iface = iface_from_dev(dev); - - if (!iface) { - return; - } - - iface->ready = ready; - if (!ready) { - consumer_report_in_flight = false; - } - - LOG_INF("%s interface %s", dev->name, ready ? "ready" : "not ready"); - submit_usb_transport_state_event(); -} - -static int consumer_get_report(const struct device *dev, - const uint8_t type, const uint8_t id, - const uint16_t len, uint8_t *const buf) -{ - ARG_UNUSED(dev); - ARG_UNUSED(type); - ARG_UNUSED(id); - ARG_UNUSED(len); - ARG_UNUSED(buf); - - return -ENOTSUP; -} - -static int consumer_set_report(const struct device *dev, - const uint8_t type, const uint8_t id, - const uint16_t len, const uint8_t *const buf) -{ - ARG_UNUSED(dev); - ARG_UNUSED(type); - ARG_UNUSED(id); - ARG_UNUSED(len); - ARG_UNUSED(buf); - - return -ENOTSUP; -} - -static void consumer_set_idle(const struct device *dev, - const uint8_t id, const uint32_t duration) -{ - ARG_UNUSED(dev); - ARG_UNUSED(id); - ARG_UNUSED(duration); -} - -static uint32_t consumer_get_idle(const struct device *dev, const uint8_t id) -{ - ARG_UNUSED(dev); - ARG_UNUSED(id); - - return 0U; -} - -static void consumer_set_protocol(const struct device *dev, const uint8_t proto) -{ - ARG_UNUSED(dev); - ARG_UNUSED(proto); -} - -static void consumer_input_report_done(const struct device *dev, - const uint8_t *const report) -{ - ARG_UNUSED(report); - - consumer_report_in_flight = false; - LOG_DBG("USB consumer report sent by %s", dev->name); - submit_hid_report_sent_event(HID_TRANSPORT_USB, - KEYBOARD_REPORT_TYPE_CONSUMER, - consumer_in_flight_sequence, false); -} - -static void consumer_output_report(const struct device *dev, - const uint16_t len, - const uint8_t *const buf) -{ - ARG_UNUSED(dev); - ARG_UNUSED(len); - ARG_UNUSED(buf); -} - -static const struct hid_device_ops consumer_ops = { - .iface_ready = consumer_iface_ready, - .get_report = consumer_get_report, - .set_report = consumer_set_report, - .set_idle = consumer_set_idle, - .get_idle = consumer_get_idle, - .set_protocol = consumer_set_protocol, - .input_report_done = consumer_input_report_done, - .output_report = consumer_output_report, -}; - -static int usb_hid_register_devices(void) -{ - int err; - - for (size_t i = 0; i < ARRAY_SIZE(hid_ifaces); i++) { - if (!device_is_ready(hid_ifaces[i].dev)) { - LOG_ERR("HID device %s not ready", hid_ifaces[i].dev->name); - return -ENODEV; - } - } - - err = hid_device_register(hid_ifaces[USB_HID_INTERFACE_KEYBOARD].dev, - keyboard_report_desc, - sizeof(keyboard_report_desc), - &keyboard_ops); - if (err) { - return err; - } - - err = hid_device_register(hid_ifaces[USB_HID_INTERFACE_CONSUMER].dev, - consumer_report_desc, - sizeof(consumer_report_desc), - &consumer_ops); - if (err) { - return err; - } - - return 0; -} - -static int usb_hid_pre_stack_init(void) -{ - return usb_hid_register_devices(); -} - -USB_FUNCTION_HOOK_DEFINE(usb_hid_hook, usb_hid_pre_stack_init); - -static int module_init(void) -{ - reset_usb_runtime_state(); - keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT; - usb_function_prepared = true; - - return 0; -} - -static int module_start(void) -{ - if (running) { - return 0; - } - - running = true; - submit_usb_transport_state_event(); - return 0; -} - -static void module_pause(void) -{ - running = false; - submit_usb_transport_state_event(); -} - -static bool handle_usb_state_event(const struct usb_state_event *event) -{ - bool new_usb_active = (event->state == USB_STATE_ACTIVE); - - if (new_usb_active == usb_active) { - return false; - } - - if (!new_usb_active) { - reset_usb_runtime_state(); - } else { - usb_active = true; - } - - submit_usb_transport_state_event(); - return false; -} - -static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event) -{ - int err; - - if (!running || !usb_active || (event->transport != HID_TRANSPORT_USB)) { - return false; - } - - if (event->report_type == KEYBOARD_REPORT_TYPE_KEYS) { - if (event->protocol_mode != keyboard_protocol_mode) { - LOG_WRN("Drop USB keys report due to protocol mismatch"); - return false; - } - - if (!hid_ifaces[USB_HID_INTERFACE_KEYBOARD].ready) { - LOG_DBG("USB keyboard interface not ready"); - return false; - } - - if (keyboard_report_in_flight) { - LOG_WRN("Drop USB keyboard report while previous report is in flight"); - return false; - } - - memcpy(keyboard_tx_buf, event->dyndata.data, event->dyndata.size); - - err = hid_device_submit_report( - hid_ifaces[USB_HID_INTERFACE_KEYBOARD].dev, - (uint16_t)event->dyndata.size, - keyboard_tx_buf); - if (err) { - LOG_WRN("USB keyboard report submit failed (%d)", err); - submit_hid_report_sent_event(HID_TRANSPORT_USB, - KEYBOARD_REPORT_TYPE_KEYS, - event->sequence, true); - } else { - keyboard_report_in_flight = true; - keyboard_in_flight_sequence = event->sequence; - } - - return false; - } - - if (event->report_type == KEYBOARD_REPORT_TYPE_CONSUMER) { - if (!hid_ifaces[USB_HID_INTERFACE_CONSUMER].ready) { - LOG_DBG("USB consumer interface not ready"); - return false; - } - - if (consumer_report_in_flight) { - LOG_WRN("Drop USB consumer report while previous report is in flight"); - return false; - } - - memcpy(consumer_tx_buf, event->dyndata.data, event->dyndata.size); - - err = hid_device_submit_report( - hid_ifaces[USB_HID_INTERFACE_CONSUMER].dev, - (uint16_t)event->dyndata.size, - consumer_tx_buf); - if (err) { - LOG_WRN("USB consumer report submit failed (%d)", err); - submit_hid_report_sent_event(HID_TRANSPORT_USB, - KEYBOARD_REPORT_TYPE_CONSUMER, - event->sequence, true); - } else { - consumer_report_in_flight = true; - consumer_in_flight_sequence = event->sequence; - } - } - - return false; -} - -static bool app_event_handler(const struct app_event_header *aeh) -{ - if (is_hid_tx_report_event(aeh)) { - return handle_hid_tx_report_event(cast_hid_tx_report_event(aeh)); - } - - if (is_usb_state_event(aeh)) { - return handle_usb_state_event(cast_usb_state_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; - } - - return false; -} - -APP_EVENT_LISTENER(MODULE, app_event_handler); -APP_EVENT_SUBSCRIBE(MODULE, module_state_event); -APP_EVENT_SUBSCRIBE(MODULE, hid_tx_report_event); -APP_EVENT_SUBSCRIBE(MODULE, usb_state_event); -APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); -APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);