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.
This commit is contained in:
Joel Spadin 2020-10-11 17:03:21 -05:00
parent dfb69d8727
commit 1d369ffa73
7 changed files with 190 additions and 45 deletions

View file

@ -37,6 +37,7 @@ target_sources(app PRIVATE src/events/keycode_state_changed.c)
target_sources(app PRIVATE src/events/modifiers_state_changed.c) target_sources(app PRIVATE src/events/modifiers_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.c) target_sources(app PRIVATE src/events/sensor_event.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c)

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr.h>
#include <usb/usb_device.h>
#include <zmk/event-manager.h>
#include <zmk/usb.h>
struct usb_conn_state_changed {
struct zmk_event_header header;
enum zmk_usb_conn_state conn_state;
};
ZMK_EVENT_DECLARE(usb_conn_state_changed);

View file

@ -12,8 +12,18 @@
#include <zmk/keys.h> #include <zmk/keys.h>
#include <zmk/hid.h> #include <zmk/hid.h>
enum zmk_usb_conn_state {
ZMK_USB_CONN_NONE,
ZMK_USB_CONN_POWERED,
ZMK_USB_CONN_HID,
};
enum usb_dc_status_code zmk_usb_get_status(); enum usb_dc_status_code zmk_usb_get_status();
enum zmk_usb_conn_state zmk_usb_get_conn_state();
static inline bool zmk_usb_is_powered() { return zmk_usb_get_conn_state() != ZMK_USB_CONN_NONE; }
static inline bool zmk_usb_is_hid_ready() { return zmk_usb_get_conn_state() == ZMK_USB_CONN_HID; }
#ifdef CONFIG_ZMK_USB #ifdef CONFIG_ZMK_USB
int zmk_usb_hid_send_report(u8_t *report, size_t len); int zmk_usb_hid_send_report(const u8_t *report, size_t len);
#endif /* CONFIG_ZMK_USB */ #endif /* CONFIG_ZMK_USB */

View file

@ -8,54 +8,138 @@
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/usb.h> #include <zmk/usb.h>
#include <zmk/hog.h> #include <zmk/hog.h>
#include <zmk/event-manager.h>
#include <zmk/events/usb-conn-state-changed.h>
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
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);
}
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) { 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); LOG_DBG("usage page 0x%02X", usage_page);
switch (usage_page) { switch (usage_page) {
case USAGE_KEYPAD: case USAGE_KEYPAD:
keypad_report = zmk_hid_get_keypad_report(); return send_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");
}
#endif /* CONFIG_ZMK_USB */
#ifdef CONFIG_ZMK_BLE
err = zmk_hog_send_keypad_report(&keypad_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
#endif /* CONFIG_ZMK_BLE */
break;
case USAGE_CONSUMER: case USAGE_CONSUMER:
consumer_report = zmk_hid_get_consumer_report(); return send_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");
}
#endif /* CONFIG_ZMK_USB */
#ifdef CONFIG_ZMK_BLE
err = zmk_hog_send_consumer_report(&consumer_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
#endif /* CONFIG_ZMK_BLE */
break;
default: default:
LOG_ERR("Unsupported usage page %d", usage_page); LOG_ERR("Unsupported usage page %d", usage_page);
return -ENOTSUP; 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; 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

View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <kernel.h>
#include <zmk/events/usb-conn-state-changed.h>
ZMK_EVENT_IMPL(usb_conn_state_changed);

View file

@ -23,14 +23,7 @@ static u32_t power_last_uptime;
bool is_usb_power_present() { bool is_usb_power_present() {
#ifdef CONFIG_USB #ifdef CONFIG_USB
enum usb_dc_status_code usb_status = zmk_usb_get_status(); return zmk_usb_is_powered();
switch (usb_status) {
case USB_DC_DISCONNECTED:
case USB_DC_UNKNOWN:
return false;
default:
return true;
}
#else #else
return false; return false;
#endif /* CONFIG_USB */ #endif /* CONFIG_USB */

View file

@ -13,6 +13,8 @@
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/keymap.h> #include <zmk/keymap.h>
#include <zmk/event-manager.h>
#include <zmk/events/usb-conn-state-changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -53,9 +55,34 @@ int zmk_usb_hid_send_report(const u8_t *report, size_t len) {
#endif /* CONFIG_ZMK_USB */ #endif /* CONFIG_ZMK_USB */
static void raise_usb_status_changed_event() {
struct usb_conn_state_changed *ev = new_usb_conn_state_changed();
ev->conn_state = zmk_usb_get_conn_state();
ZMK_EVENT_RAISE(ev);
}
enum usb_dc_status_code zmk_usb_get_status() { return usb_status; } enum usb_dc_status_code zmk_usb_get_status() { return usb_status; }
void usb_status_cb(enum usb_dc_status_code status, const u8_t *params) { usb_status = status; }; enum zmk_usb_conn_state zmk_usb_get_conn_state() {
switch (usb_status) {
case USB_DC_DISCONNECTED:
case USB_DC_UNKNOWN:
return ZMK_USB_CONN_NONE;
case USB_DC_ERROR:
case USB_DC_RESET:
return ZMK_USB_CONN_POWERED;
default:
return ZMK_USB_CONN_HID;
}
}
void usb_status_cb(enum usb_dc_status_code status, const u8_t *params) {
usb_status = status;
raise_usb_status_changed_event();
};
static int zmk_usb_init(struct device *_arg) { static int zmk_usb_init(struct device *_arg) {
int usb_enable_ret; int usb_enable_ret;