Merge pull request #269 from joelspadin/endpoint-selection
feat: only send HID reports to one endpoint
This commit is contained in:
commit
2d31e1d05f
19 changed files with 458 additions and 48 deletions
|
@ -37,11 +37,13 @@ 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)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
|
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
|
||||||
|
target_sources(app PRIVATE src/behaviors/behavior_outputs.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
|
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||||
|
|
|
@ -10,3 +10,4 @@
|
||||||
#include <behaviors/rgb_underglow.dtsi>
|
#include <behaviors/rgb_underglow.dtsi>
|
||||||
#include <behaviors/bluetooth.dtsi>
|
#include <behaviors/bluetooth.dtsi>
|
||||||
#include <behaviors/ext_power.dtsi>
|
#include <behaviors/ext_power.dtsi>
|
||||||
|
#include <behaviors/outputs.dtsi>
|
||||||
|
|
9
app/dts/behaviors/outputs.dtsi
Normal file
9
app/dts/behaviors/outputs.dtsi
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
out: behavior_outputs {
|
||||||
|
compatible = "zmk,behavior-outputs";
|
||||||
|
label = "OUTPUTS";
|
||||||
|
#binding-cells = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
10
app/dts/bindings/behaviors/zmk,behavior-outputs.yaml
Normal file
10
app/dts/bindings/behaviors/zmk,behavior-outputs.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2020, The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
|
||||||
|
description: Output Selection Behavior
|
||||||
|
|
||||||
|
compatible: "zmk,behavior-outputs"
|
||||||
|
|
||||||
|
include: one_param.yaml
|
9
app/include/dt-bindings/zmk/outputs.h
Normal file
9
app/include/dt-bindings/zmk/outputs.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define OUT_TOG 0
|
||||||
|
#define OUT_USB 1
|
||||||
|
#define OUT_BLE 2
|
|
@ -15,6 +15,7 @@ int zmk_ble_prof_prev();
|
||||||
int zmk_ble_prof_select(u8_t index);
|
int zmk_ble_prof_select(u8_t index);
|
||||||
|
|
||||||
bt_addr_le_t *zmk_ble_active_profile_addr();
|
bt_addr_le_t *zmk_ble_active_profile_addr();
|
||||||
|
bool zmk_ble_active_profile_is_connected();
|
||||||
char *zmk_ble_active_profile_name();
|
char *zmk_ble_active_profile_name();
|
||||||
|
|
||||||
int zmk_ble_unpair_all();
|
int zmk_ble_unpair_all();
|
||||||
|
|
|
@ -9,4 +9,12 @@
|
||||||
#include <zmk/keys.h>
|
#include <zmk/keys.h>
|
||||||
#include <zmk/hid.h>
|
#include <zmk/hid.h>
|
||||||
|
|
||||||
|
enum zmk_endpoint {
|
||||||
|
ZMK_ENDPOINT_USB,
|
||||||
|
ZMK_ENDPOINT_BLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
int zmk_endpoints_select(enum zmk_endpoint endpoint);
|
||||||
|
int zmk_endpoints_toggle();
|
||||||
|
|
||||||
int zmk_endpoints_send_report(u8_t usage_report);
|
int zmk_endpoints_send_report(u8_t usage_report);
|
||||||
|
|
20
app/include/zmk/events/usb-conn-state-changed.h
Normal file
20
app/include/zmk/events/usb-conn-state-changed.h
Normal 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);
|
|
@ -168,9 +168,11 @@ int zmk_hid_register_mods(zmk_mod_flags modifiers);
|
||||||
int zmk_hid_unregister_mods(zmk_mod_flags modifiers);
|
int zmk_hid_unregister_mods(zmk_mod_flags modifiers);
|
||||||
int zmk_hid_keypad_press(zmk_key key);
|
int zmk_hid_keypad_press(zmk_key key);
|
||||||
int zmk_hid_keypad_release(zmk_key key);
|
int zmk_hid_keypad_release(zmk_key key);
|
||||||
|
void zmk_hid_keypad_clear();
|
||||||
|
|
||||||
int zmk_hid_consumer_press(zmk_key key);
|
int zmk_hid_consumer_press(zmk_key key);
|
||||||
int zmk_hid_consumer_release(zmk_key key);
|
int zmk_hid_consumer_release(zmk_key key);
|
||||||
|
void zmk_hid_consumer_clear();
|
||||||
|
|
||||||
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report();
|
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report();
|
||||||
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
|
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
|
||||||
|
|
|
@ -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 */
|
44
app/src/behaviors/behavior_outputs.c
Normal file
44
app/src/behaviors/behavior_outputs.c
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_behavior_outputs
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <devicetree.h>
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
|
||||||
|
#include <dt-bindings/zmk/outputs.h>
|
||||||
|
|
||||||
|
#include <zmk/behavior.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
switch (binding->param1) {
|
||||||
|
case OUT_TOG:
|
||||||
|
return zmk_endpoints_toggle();
|
||||||
|
case OUT_USB:
|
||||||
|
return zmk_endpoints_select(ZMK_ENDPOINT_USB);
|
||||||
|
case OUT_BLE:
|
||||||
|
return zmk_endpoints_select(ZMK_ENDPOINT_BLE);
|
||||||
|
default:
|
||||||
|
LOG_ERR("Unknown output command: %d", binding->param1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int behavior_out_init(struct device *dev) { return 0; }
|
||||||
|
|
||||||
|
static const struct behavior_driver_api behavior_outputs_driver_api = {
|
||||||
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(behavior_out, DT_INST_LABEL(0), behavior_out_init, NULL, NULL, APPLICATION,
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
|
|
@ -94,6 +94,12 @@ static void raise_profile_changed_event() {
|
||||||
ZMK_EVENT_RAISE(ev);
|
ZMK_EVENT_RAISE(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void raise_profile_changed_event_callback(struct k_work *work) {
|
||||||
|
raise_profile_changed_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
K_WORK_DEFINE(raise_profile_changed_event_work, raise_profile_changed_event_callback);
|
||||||
|
|
||||||
static bool active_profile_is_open() {
|
static bool active_profile_is_open() {
|
||||||
return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY);
|
return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +117,7 @@ void set_profile_address(u8_t index, const bt_addr_le_t *addr) {
|
||||||
raise_profile_changed_event();
|
raise_profile_changed_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool active_profile_is_connected() {
|
bool zmk_ble_active_profile_is_connected() {
|
||||||
struct bt_conn *conn;
|
struct bt_conn *conn;
|
||||||
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
|
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
|
||||||
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
|
||||||
|
@ -163,9 +169,9 @@ int update_advertising() {
|
||||||
struct bt_conn *conn;
|
struct bt_conn *conn;
|
||||||
enum advertising_type desired_adv = ZMK_ADV_NONE;
|
enum advertising_type desired_adv = ZMK_ADV_NONE;
|
||||||
|
|
||||||
if (active_profile_is_open() || !active_profile_is_connected()) {
|
if (active_profile_is_open()) {
|
||||||
desired_adv = ZMK_ADV_CONN;
|
desired_adv = ZMK_ADV_CONN;
|
||||||
} else if (!active_profile_is_connected()) {
|
} else if (!zmk_ble_active_profile_is_connected()) {
|
||||||
desired_adv = ZMK_ADV_CONN;
|
desired_adv = ZMK_ADV_CONN;
|
||||||
// Need to fix directed advertising for privacy centrals. See
|
// Need to fix directed advertising for privacy centrals. See
|
||||||
// https://github.com/zephyrproject-rtos/zephyr/pull/14984 char
|
// https://github.com/zephyrproject-rtos/zephyr/pull/14984 char
|
||||||
|
@ -327,6 +333,10 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
|
||||||
struct settings_handler profiles_handler = {.name = "ble", .h_set = ble_profiles_handle_set};
|
struct settings_handler profiles_handler = {.name = "ble", .h_set = ble_profiles_handle_set};
|
||||||
#endif /* IS_ENABLED(CONFIG_SETTINGS) */
|
#endif /* IS_ENABLED(CONFIG_SETTINGS) */
|
||||||
|
|
||||||
|
static bool is_conn_active_profile(const struct bt_conn *conn) {
|
||||||
|
return bt_addr_le_cmp(bt_conn_get_dst(conn), &profiles[active_profile].peer) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void connected(struct bt_conn *conn, u8_t err) {
|
static void connected(struct bt_conn *conn, u8_t err) {
|
||||||
char addr[BT_ADDR_LE_STR_LEN];
|
char addr[BT_ADDR_LE_STR_LEN];
|
||||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||||
|
@ -352,6 +362,11 @@ static void connected(struct bt_conn *conn, u8_t err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
update_advertising();
|
update_advertising();
|
||||||
|
|
||||||
|
if (is_conn_active_profile(conn)) {
|
||||||
|
LOG_DBG("Active profile connected");
|
||||||
|
raise_profile_changed_event();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disconnected(struct bt_conn *conn, u8_t reason) {
|
static void disconnected(struct bt_conn *conn, u8_t reason) {
|
||||||
|
@ -364,6 +379,11 @@ static void disconnected(struct bt_conn *conn, u8_t reason) {
|
||||||
// We need to do this in a work callback, otherwise the advertising update will still see the
|
// We need to do this in a work callback, otherwise the advertising update will still see the
|
||||||
// connection for a profile as active, and not start advertising yet.
|
// connection for a profile as active, and not start advertising yet.
|
||||||
k_work_submit(&update_advertising_work);
|
k_work_submit(&update_advertising_work);
|
||||||
|
|
||||||
|
if (is_conn_active_profile(conn)) {
|
||||||
|
LOG_DBG("Active profile disconnected");
|
||||||
|
k_work_submit(&raise_profile_changed_event_work);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
|
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
|
||||||
|
|
|
@ -4,58 +4,238 @@
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <init.h>
|
||||||
|
#include <settings/settings.h>
|
||||||
|
|
||||||
|
#include <zmk/ble.h>
|
||||||
#include <zmk/endpoints.h>
|
#include <zmk/endpoints.h>
|
||||||
#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/ble-active-profile-changed.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);
|
||||||
|
|
||||||
|
#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();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_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);
|
||||||
|
}
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
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();
|
||||||
|
#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 zmk_endpoint get_selected_endpoint() {
|
||||||
|
if (is_ble_ready()) {
|
||||||
|
if (is_usb_ready()) {
|
||||||
|
LOG_DBG("Both endpoints are ready. Using %d", preferred_endpoint);
|
||||||
|
return preferred_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Only BLE is ready.");
|
||||||
|
return ZMK_ENDPOINT_BLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_usb_ready()) {
|
||||||
|
LOG_DBG("Only USB is ready.");
|
||||||
|
return ZMK_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 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. */
|
||||||
|
disconnect_current_endpoint();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
SYS_INIT(zmk_endpoints_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||||
|
|
10
app/src/events/usb_conn_state_changed.c
Normal file
10
app/src/events/usb_conn_state_changed.c
Normal 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);
|
|
@ -68,6 +68,8 @@ int zmk_hid_keypad_release(zmk_key code) {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void zmk_hid_keypad_clear() { memset(&kp_report.body, 0, sizeof(kp_report.body)); }
|
||||||
|
|
||||||
int zmk_hid_consumer_press(zmk_key code) {
|
int zmk_hid_consumer_press(zmk_key code) {
|
||||||
TOGGLE_CONSUMER(0U, code);
|
TOGGLE_CONSUMER(0U, code);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -78,6 +80,8 @@ int zmk_hid_consumer_release(zmk_key code) {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); }
|
||||||
|
|
||||||
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report() {
|
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report() {
|
||||||
return &kp_report;
|
return &kp_report;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
59
docs/docs/behavior/outputs.md
Normal file
59
docs/docs/behavior/outputs.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
title: Output Selection Behavior
|
||||||
|
sidebar_label: Output Selection
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The output behavior allows selecting whether keyboard output is sent to the
|
||||||
|
USB or bluetooth connection when both are connected. This allows connecting a
|
||||||
|
keyboard to USB for power but outputting to a different device over bluetooth.
|
||||||
|
|
||||||
|
By default, output is sent to USB when both USB and BLE are connected.
|
||||||
|
Once you select a different output, it will be remembered until you change it again.
|
||||||
|
|
||||||
|
## Output Command Defines
|
||||||
|
|
||||||
|
Output command defines are provided through the [`dt-bindings/zmk/outputs.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/outputs.h)
|
||||||
|
header, which is added at the top of the keymap file:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <dt-bindings/zmk/outputs.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows you to reference the actions defined in this header:
|
||||||
|
|
||||||
|
| Define | Action | Alias |
|
||||||
|
| ------------------- | ----------------------------------------------- | --------- |
|
||||||
|
| `OUTPUT_USB_CMD` | Prefer sending to USB | `OUT_USB` |
|
||||||
|
| `OUTPUT_BLE_CMD` | Prefer sending to the current bluetooth profile | `OUT_BLE` |
|
||||||
|
| `OUTPUT_TOGGLE_CMD` | Toggle between USB and BLE | `OUT_TOG` |
|
||||||
|
|
||||||
|
## Output Selection Behavior
|
||||||
|
|
||||||
|
The output selection behavior changes the preferred output on press.
|
||||||
|
|
||||||
|
### Behavior Binding
|
||||||
|
|
||||||
|
- Reference: `&out`
|
||||||
|
- Parameter #1: Command, e.g. `OUT_BLE`
|
||||||
|
|
||||||
|
### Example:
|
||||||
|
|
||||||
|
1. Behavior binding to prefer sending keyboard output to USB
|
||||||
|
|
||||||
|
```
|
||||||
|
&out OUT_USB
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Behavior binding to prefer sending keyboard output to the current bluetooth profile
|
||||||
|
|
||||||
|
```
|
||||||
|
&out OUT_BLE
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Behavior binding to toggle between preferring USB and BLE
|
||||||
|
|
||||||
|
```
|
||||||
|
&out OUT_TOG
|
||||||
|
```
|
|
@ -22,6 +22,7 @@ module.exports = {
|
||||||
"behavior/mod-tap",
|
"behavior/mod-tap",
|
||||||
"behavior/reset",
|
"behavior/reset",
|
||||||
"behavior/bluetooth",
|
"behavior/bluetooth",
|
||||||
|
"behavior/outputs",
|
||||||
"behavior/lighting",
|
"behavior/lighting",
|
||||||
"behavior/power",
|
"behavior/power",
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue