From 1d369ffa73ae63af821d012b1c2ab4a07a5dc9c7 Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sun, 11 Oct 2020 17:03:21 -0500 Subject: feat: only send HID reports to one endpoint Added some utility functions and an event for tracking the state of the USB connection. Updated endpoints.c to select a single endpoint to send HID reports to based on the status of the USB and BLE connections. Partially fixes #206. Future commits will add a user setting to control which endpoint is used if both USB and BLE are ready. --- app/src/endpoints.c | 140 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 112 insertions(+), 28 deletions(-) (limited to 'app/src/endpoints.c') diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 79d294e..4f56aa5 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -8,54 +8,138 @@ #include #include #include +#include +#include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -int zmk_endpoints_send_report(u8_t usage_page) { - int err; - struct zmk_hid_keypad_report *keypad_report; - struct zmk_hid_consumer_report *consumer_report; - LOG_DBG("usage page 0x%02X", usage_page); - switch (usage_page) { - case USAGE_KEYPAD: - keypad_report = zmk_hid_get_keypad_report(); -#ifdef CONFIG_ZMK_USB - if (zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(struct zmk_hid_keypad_report)) != - 0) { - LOG_DBG("USB Send Failed"); +enum endpoint { + ENDPOINT_USB, + ENDPOINT_BLE, +}; + +static enum endpoint current_endpoint = + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ENDPOINT_BLE), (ENDPOINT_USB)); + +static int send_keypad_report() { + struct zmk_hid_keypad_report *keypad_report = zmk_hid_get_keypad_report(); + + switch (current_endpoint) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ENDPOINT_USB: { + int err = zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(*keypad_report)); + if (err) { + LOG_ERR("FAILED TO SEND OVER USB: %d", err); } -#endif /* CONFIG_ZMK_USB */ + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ -#ifdef CONFIG_ZMK_BLE - err = zmk_hog_send_keypad_report(&keypad_report->body); +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ENDPOINT_BLE: { + int err = zmk_hog_send_keypad_report(&keypad_report->body); if (err) { LOG_ERR("FAILED TO SEND OVER HOG: %d", err); } -#endif /* CONFIG_ZMK_BLE */ + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ - break; - case USAGE_CONSUMER: - consumer_report = zmk_hid_get_consumer_report(); -#ifdef CONFIG_ZMK_USB - if (zmk_usb_hid_send_report((u8_t *)consumer_report, - sizeof(struct zmk_hid_consumer_report)) != 0) { - LOG_DBG("USB Send Failed"); + default: + LOG_ERR("Unsupported endpoint %d", current_endpoint); + return -ENOTSUP; + } +} + +static int send_consumer_report() { + struct zmk_hid_consumer_report *consumer_report = zmk_hid_get_consumer_report(); + + switch (current_endpoint) { +#if IS_ENABLED(CONFIG_ZMK_USB) + case ENDPOINT_USB: { + int err = zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(*consumer_report)); + if (err) { + LOG_ERR("FAILED TO SEND OVER USB: %d", err); } -#endif /* CONFIG_ZMK_USB */ + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ -#ifdef CONFIG_ZMK_BLE - err = zmk_hog_send_consumer_report(&consumer_report->body); +#if IS_ENABLED(CONFIG_ZMK_BLE) + case ENDPOINT_BLE: { + int err = zmk_hog_send_consumer_report(&consumer_report->body); if (err) { LOG_ERR("FAILED TO SEND OVER HOG: %d", err); } -#endif /* CONFIG_ZMK_BLE */ + return err; + } +#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ + + default: + LOG_ERR("Unsupported endpoint %d", current_endpoint); + return -ENOTSUP; + } +} + +int zmk_endpoints_send_report(u8_t usage_page) { - break; + LOG_DBG("usage page 0x%02X", usage_page); + switch (usage_page) { + case USAGE_KEYPAD: + return send_keypad_report(); + case USAGE_CONSUMER: + return send_consumer_report(); default: LOG_ERR("Unsupported usage page %d", usage_page); return -ENOTSUP; } +} + +static bool is_usb_ready() { +#if IS_ENABLED(CONFIG_ZMK_USB) + return zmk_usb_is_hid_ready(); +#else + return false; +#endif +} + +static bool is_ble_ready() { +#if IS_ENABLED(CONFIG_ZMK_BLE) + return zmk_ble_active_profile_is_connected(); +#else + return false; +#endif +} + +static enum endpoint get_selected_endpoint() { + if (is_ble_ready()) { + if (is_usb_ready()) { + LOG_DBG("Both endpoints ready. Selecting USB"); + // TODO: add user setting to control this + return ENDPOINT_USB; + } + + return ENDPOINT_BLE; + } + + return ENDPOINT_USB; +} + +static int endpoint_listener(const struct zmk_event_header *eh) { + enum endpoint new_endpoint = get_selected_endpoint(); + + if (new_endpoint != current_endpoint) { + // TODO: send null report on previous endpoint + current_endpoint = new_endpoint; + LOG_INF("Endpoint changed: %d", current_endpoint); + } return 0; } + +ZMK_LISTENER(endpoint_listener, endpoint_listener); +#if IS_ENABLED(CONFIG_USB) +ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed); +#endif +// TODO: add BLE state subscription \ No newline at end of file -- cgit v1.2.3 From 8f666cecc9717b3fbbe2f5f34375b6a45b8965fd Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sun, 11 Oct 2020 18:38:39 -0500 Subject: feat(endpoints): clear HID report on endpoint change This prevents stuck keys when switching endpoints by clearing everything in the HID report and sending one last report before switching to the new endpoint. --- app/src/endpoints.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'app/src/endpoints.c') diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 4f56aa5..3acaba6 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -126,11 +126,21 @@ static enum endpoint get_selected_endpoint() { return ENDPOINT_USB; } +static void disconnect_current_endpoint() { + zmk_hid_keypad_clear(); + zmk_hid_consumer_clear(); + + zmk_endpoints_send_report(USAGE_KEYPAD); + zmk_endpoints_send_report(USAGE_CONSUMER); +} + static int endpoint_listener(const struct zmk_event_header *eh) { enum endpoint new_endpoint = get_selected_endpoint(); if (new_endpoint != current_endpoint) { - // TODO: send null report on previous endpoint + /* Cancel all current keypresses so keys don't stay held on the old endpoint. */ + disconnect_current_endpoint(); + current_endpoint = new_endpoint; LOG_INF("Endpoint changed: %d", current_endpoint); } -- cgit v1.2.3 From b538e605321791568aa25f0bd534ebcb30da92c2 Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sat, 24 Oct 2020 13:39:02 -0500 Subject: feat(endpoints): update on BLE profile change Added zmk_ble_active_profile_is_connected() to allow code outside ble.c to check the status of the active profile, and changed the ble_active_profile_changed event to also notify when the active profile connects or disconnects. Changed endpoint selection to to also update when the active profile changes, connects, or disconnects. --- app/src/endpoints.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'app/src/endpoints.c') diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 3acaba6..56204a4 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -4,11 +4,13 @@ * SPDX-License-Identifier: MIT */ +#include #include #include #include #include #include +#include #include #include @@ -19,8 +21,9 @@ enum endpoint { ENDPOINT_BLE, }; -static enum endpoint current_endpoint = - COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ENDPOINT_BLE), (ENDPOINT_USB)); +#define DEFAULT_ENDPOINT COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ENDPOINT_BLE), (ENDPOINT_USB)) + +static enum endpoint current_endpoint = DEFAULT_ENDPOINT; static int send_keypad_report() { struct zmk_hid_keypad_report *keypad_report = zmk_hid_get_keypad_report(); @@ -115,15 +118,22 @@ static bool is_ble_ready() { static enum endpoint get_selected_endpoint() { if (is_ble_ready()) { if (is_usb_ready()) { - LOG_DBG("Both endpoints ready. Selecting USB"); + LOG_DBG("Both endpoints are ready."); // TODO: add user setting to control this return ENDPOINT_USB; } + LOG_DBG("Only BLE is ready."); return ENDPOINT_BLE; } - return ENDPOINT_USB; + if (is_usb_ready()) { + LOG_DBG("Only USB is ready."); + return ENDPOINT_USB; + } + + LOG_DBG("No endpoints are ready."); + return DEFAULT_ENDPOINT; } static void disconnect_current_endpoint() { @@ -149,7 +159,9 @@ static int endpoint_listener(const struct zmk_event_header *eh) { } ZMK_LISTENER(endpoint_listener, endpoint_listener); -#if IS_ENABLED(CONFIG_USB) +#if IS_ENABLED(CONFIG_ZMK_USB) ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed); #endif -// TODO: add BLE state subscription \ No newline at end of file +#if IS_ENABLED(CONFIG_ZMK_BLE) +ZMK_SUBSCRIPTION(endpoint_listener, ble_active_profile_changed); +#endif \ No newline at end of file -- cgit v1.2.3 From 600bba25f049b1fa161b2372b2a47762164c7fcd Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sat, 24 Oct 2020 16:28:58 -0500 Subject: feat(endpoints): add preferred endpoint setting Added a new setting to remember the user's preferred endpoint. When both USB and BLE are connected, the preferred endpoint will be used. Added a new behavior to control this setting. It supports commands: &end END_USB - Prefer USB output &end END_BLE - Prefer BLE output &end END_TOG - Toggle between USB and BLE --- app/src/endpoints.c | 112 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 19 deletions(-) (limited to 'app/src/endpoints.c') diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 56204a4..0c79589 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -4,6 +4,9 @@ * SPDX-License-Identifier: MIT */ +#include +#include + #include #include #include @@ -16,21 +19,45 @@ #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -enum endpoint { - ENDPOINT_USB, - ENDPOINT_BLE, -}; +#define DEFAULT_ENDPOINT \ + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_ENDPOINT_BLE), (ZMK_ENDPOINT_USB)) + +static enum zmk_endpoint current_endpoint = DEFAULT_ENDPOINT; +static enum zmk_endpoint preferred_endpoint = + ZMK_ENDPOINT_USB; /* Used if multiple endpoints are ready */ + +static void update_current_endpoint(); + +int zmk_endpoints_select(enum zmk_endpoint endpoint) { + LOG_DBG("Selected endpoint %d", endpoint); + + if (preferred_endpoint == endpoint) { + return 0; + } + + preferred_endpoint = endpoint; + +#if IS_ENABLED(CONFIG_SETTINGS) + settings_save_one("endpoints/preferred", &preferred_endpoint, sizeof(preferred_endpoint)); +#endif + + update_current_endpoint(); -#define DEFAULT_ENDPOINT COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ENDPOINT_BLE), (ENDPOINT_USB)) + return 0; +} -static enum endpoint current_endpoint = DEFAULT_ENDPOINT; +int zmk_endpoints_toggle() { + enum zmk_endpoint new_endpoint = + (preferred_endpoint == ZMK_ENDPOINT_USB) ? ZMK_ENDPOINT_BLE : ZMK_ENDPOINT_USB; + return zmk_endpoints_select(new_endpoint); +} static int send_keypad_report() { struct zmk_hid_keypad_report *keypad_report = zmk_hid_get_keypad_report(); switch (current_endpoint) { #if IS_ENABLED(CONFIG_ZMK_USB) - case ENDPOINT_USB: { + case ZMK_ENDPOINT_USB: { int err = zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(*keypad_report)); if (err) { LOG_ERR("FAILED TO SEND OVER USB: %d", err); @@ -40,7 +67,7 @@ static int send_keypad_report() { #endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #if IS_ENABLED(CONFIG_ZMK_BLE) - case ENDPOINT_BLE: { + case ZMK_ENDPOINT_BLE: { int err = zmk_hog_send_keypad_report(&keypad_report->body); if (err) { LOG_ERR("FAILED TO SEND OVER HOG: %d", err); @@ -60,7 +87,7 @@ static int send_consumer_report() { switch (current_endpoint) { #if IS_ENABLED(CONFIG_ZMK_USB) - case ENDPOINT_USB: { + case ZMK_ENDPOINT_USB: { int err = zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(*consumer_report)); if (err) { LOG_ERR("FAILED TO SEND OVER USB: %d", err); @@ -70,7 +97,7 @@ static int send_consumer_report() { #endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #if IS_ENABLED(CONFIG_ZMK_BLE) - case ENDPOINT_BLE: { + case ZMK_ENDPOINT_BLE: { int err = zmk_hog_send_consumer_report(&consumer_report->body); if (err) { LOG_ERR("FAILED TO SEND OVER HOG: %d", err); @@ -99,6 +126,49 @@ int zmk_endpoints_send_report(u8_t usage_page) { } } +#if IS_ENABLED(CONFIG_SETTINGS) + +static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + LOG_DBG("Setting endpoint value %s", log_strdup(name)); + + if (settings_name_steq(name, "preferred", NULL)) { + if (len != sizeof(enum zmk_endpoint)) { + LOG_ERR("Invalid endpoint size (got %d expected %d)", len, sizeof(enum zmk_endpoint)); + return -EINVAL; + } + + int err = read_cb(cb_arg, &preferred_endpoint, sizeof(enum zmk_endpoint)); + if (err <= 0) { + LOG_ERR("Failed to read preferred endpoint from settings (err %d)", err); + return err; + } + + update_current_endpoint(); + } + + return 0; +} + +struct settings_handler endpoints_handler = {.name = "endpoints", .h_set = endpoints_handle_set}; +#endif /* IS_ENABLED(CONFIG_SETTINGS) */ + +static int zmk_endpoints_init(struct device *_arg) { +#if IS_ENABLED(CONFIG_SETTINGS) + settings_subsys_init(); + + int err = settings_register(&endpoints_handler); + if (err) { + LOG_ERR("Failed to register the endpoints settings handler (err %d)", err); + return err; + } + + settings_load(); +#endif + + return 0; +} + static bool is_usb_ready() { #if IS_ENABLED(CONFIG_ZMK_USB) return zmk_usb_is_hid_ready(); @@ -115,21 +185,20 @@ static bool is_ble_ready() { #endif } -static enum endpoint get_selected_endpoint() { +static enum zmk_endpoint get_selected_endpoint() { if (is_ble_ready()) { if (is_usb_ready()) { - LOG_DBG("Both endpoints are ready."); - // TODO: add user setting to control this - return ENDPOINT_USB; + LOG_DBG("Both endpoints are ready. Using %d", preferred_endpoint); + return preferred_endpoint; } LOG_DBG("Only BLE is ready."); - return ENDPOINT_BLE; + return ZMK_ENDPOINT_BLE; } if (is_usb_ready()) { LOG_DBG("Only USB is ready."); - return ENDPOINT_USB; + return ZMK_ENDPOINT_USB; } LOG_DBG("No endpoints are ready."); @@ -144,8 +213,8 @@ static void disconnect_current_endpoint() { zmk_endpoints_send_report(USAGE_CONSUMER); } -static int endpoint_listener(const struct zmk_event_header *eh) { - enum endpoint new_endpoint = get_selected_endpoint(); +static void update_current_endpoint() { + enum zmk_endpoint new_endpoint = get_selected_endpoint(); if (new_endpoint != current_endpoint) { /* Cancel all current keypresses so keys don't stay held on the old endpoint. */ @@ -154,7 +223,10 @@ static int endpoint_listener(const struct zmk_event_header *eh) { current_endpoint = new_endpoint; LOG_INF("Endpoint changed: %d", current_endpoint); } +} +static int endpoint_listener(const struct zmk_event_header *eh) { + update_current_endpoint(); return 0; } @@ -164,4 +236,6 @@ ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed); #endif #if IS_ENABLED(CONFIG_ZMK_BLE) ZMK_SUBSCRIPTION(endpoint_listener, ble_active_profile_changed); -#endif \ No newline at end of file +#endif + +SYS_INIT(zmk_endpoints_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); -- cgit v1.2.3