/* * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ #include #include #include #include #include #include #include #include #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), (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(); 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); } return err; } #endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #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); } return err; } #endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ 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); } return err; } #endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #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); } 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) { 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 are ready."); // TODO: add user setting to control this return ENDPOINT_USB; } LOG_DBG("Only BLE is ready."); return ENDPOINT_BLE; } 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() { 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) { /* 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); } return 0; } ZMK_LISTENER(endpoint_listener, endpoint_listener); #if IS_ENABLED(CONFIG_ZMK_USB) ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed); #endif #if IS_ENABLED(CONFIG_ZMK_BLE) ZMK_SUBSCRIPTION(endpoint_listener, ble_active_profile_changed); #endif