refactor: Split endpoint to transport and instance

Changed the endpoints code to rename the existing endpoint types to
"transport" and add the concept of "endpoint instances". A transport is
the method by which data is sent, while instances allow describing
multiple endpoints that use the same transport (e.g. bluetooth profiles)

Also added new APIs to get the total number of possible endpoint
instances and assign each instance a unique index, which can be used
for tracking separate state for each endpoint in other code files.
This commit is contained in:
Joel Spadin 2023-05-14 11:55:21 -05:00 committed by Pete Johanson
parent 2f05ad55ca
commit 651ed05e9a
8 changed files with 278 additions and 134 deletions

View file

@ -14,9 +14,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h> #include <zmk/display.h>
#include "output_status.h" #include "output_status.h"
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/usb_conn_state_changed.h>
#include <zmk/events/ble_active_profile_changed.h> #include <zmk/events/ble_active_profile_changed.h>
#include <zmk/events/endpoint_selection_changed.h> #include <zmk/events/endpoint_changed.h>
#include <zmk/usb.h> #include <zmk/usb.h>
#include <zmk/ble.h> #include <zmk/ble.h>
#include <zmk/endpoints.h> #include <zmk/endpoints.h>
@ -39,31 +38,31 @@ LV_IMG_DECLARE(USB_connected);
static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets);
struct output_status_state { struct output_status_state {
enum zmk_endpoint selected_endpoint; struct zmk_endpoint_instance selected_endpoint;
bool active_profile_connected; bool active_profile_connected;
bool active_profile_bonded; bool active_profile_bonded;
uint8_t active_profile_index; uint8_t active_profile_index;
}; };
static struct output_status_state get_state(const zmk_event_t *_eh) { static struct output_status_state get_state(const zmk_event_t *_eh) {
return (struct output_status_state){.selected_endpoint = zmk_endpoints_selected(), return (struct output_status_state){
.active_profile_connected = .selected_endpoint = zmk_endpoints_selected(),
zmk_ble_active_profile_is_connected(), .active_profile_connected = zmk_ble_active_profile_is_connected(),
.active_profile_bonded = !zmk_ble_active_profile_is_open(), .active_profile_bonded = !zmk_ble_active_profile_is_open(),
.active_profile_index = zmk_ble_active_profile_index()}; };
; ;
} }
static void set_status_symbol(lv_obj_t *icon, struct output_status_state state) { static void set_status_symbol(lv_obj_t *icon, struct output_status_state state) {
switch (state.selected_endpoint) { switch (state.selected_endpoint.transport) {
case ZMK_ENDPOINT_USB: case ZMK_TRANSPORT_USB:
lv_img_set_src(icon, &USB_connected); lv_img_set_src(icon, &USB_connected);
break; break;
case ZMK_ENDPOINT_BLE: case ZMK_TRANSPORT_BLE:
if (state.active_profile_bonded) { if (state.active_profile_bonded) {
if (state.active_profile_connected) { if (state.active_profile_connected) {
// sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_OK, active_profile_index); // sprintf(text, LV_SYMBOL_BLUETOOTH "%i " LV_SYMBOL_OK, active_profile_index);
switch (state.active_profile_index) { switch (state.selected_endpoint.ble.profile_index) {
case 0: case 0:
lv_img_set_src(icon, &bluetooth_connected_1); lv_img_set_src(icon, &bluetooth_connected_1);
break; break;
@ -84,7 +83,7 @@ static void set_status_symbol(lv_obj_t *icon, struct output_status_state state)
lv_img_set_src(icon, &bluetooth_disconnected_right); lv_img_set_src(icon, &bluetooth_disconnected_right);
} }
} else { } else {
switch (state.active_profile_index) { switch (state.selected_endpoint.ble.profile_index) {
case 0: case 0:
lv_img_set_src(icon, &bluetooth_advertising_1); lv_img_set_src(icon, &bluetooth_advertising_1);
break; break;
@ -113,11 +112,9 @@ static void output_status_update_cb(struct output_status_state state) {
ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state, ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state,
output_status_update_cb, get_state) output_status_update_cb, get_state)
ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_selection_changed); ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_changed);
// We don't get an endpoint changed event when the active profile connects/disconnects
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) // but there wasn't another endpoint to switch from/to, so update on BLE events too.
ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed);
#endif
#if defined(CONFIG_ZMK_BLE) #if defined(CONFIG_ZMK_BLE)
ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed);
#endif #endif

View file

@ -6,10 +6,66 @@
#pragma once #pragma once
#include <zmk/ble.h>
#include <zmk/endpoints_types.h> #include <zmk/endpoints_types.h>
int zmk_endpoints_select(enum zmk_endpoint endpoint); /**
int zmk_endpoints_toggle(); * Recommended length of string buffer for printing endpoint identifiers.
enum zmk_endpoint zmk_endpoints_selected(); */
#define ZMK_ENDPOINT_STR_LEN 10
#ifdef CONFIG_ZMK_USB
#define ZMK_ENDPOINT_USB_COUNT 1
#else
#define ZMK_ENDPOINT_USB_COUNT 0
#endif
#ifdef CONFIG_ZMK_BLE
#define ZMK_ENDPOINT_BLE_COUNT ZMK_BLE_PROFILE_COUNT
#else
#define ZMK_ENDPOINT_BLE_COUNT 0
#endif
/**
* The total number of different (struct zmk_endpoint_instance) values that can
* be selected.
*
* Note that this value may change between firmware versions, so it should not
* be used in any persistent storage.
*/
#define ZMK_ENDPOINT_COUNT (ZMK_ENDPOINT_USB_COUNT + ZMK_ENDPOINT_BLE_COUNT)
bool zmk_endpoint_instance_eq(struct zmk_endpoint_instance a, struct zmk_endpoint_instance b);
/**
* Writes a string identifying an endpoint instance.
*
* @param str Address of output string buffer
* @param len Length of string buffer. See ZMK_ENDPOINT_STR_LEN for recommended length.
*
* @returns Number of characters written.
*/
int zmk_endpoint_instance_to_str(struct zmk_endpoint_instance endpoint, char *str, size_t len);
/**
* Gets a unique index for an endpoint instance. This can be used together with
* ZMK_ENDPOINT_COUNT to manage separate state for each endpoint instance.
*
* Note that the index for a specific instance may change between firmware versions,
* so it should not be used in any persistent storage.
*/
int zmk_endpoint_instance_to_index(struct zmk_endpoint_instance endpoint);
/**
* Sets the preferred endpoint transport to use. (If the preferred endpoint is
* not available, a different one may automatically be selected.)
*/
int zmk_endpoints_select_transport(enum zmk_transport transport);
int zmk_endpoints_toggle_transport(void);
/**
* Gets the currently-selected endpoint.
*/
struct zmk_endpoint_instance zmk_endpoints_selected(void);
int zmk_endpoints_send_report(uint16_t usage_page); int zmk_endpoints_send_report(uint16_t usage_page);

View file

@ -6,7 +6,33 @@
#pragma once #pragma once
enum zmk_endpoint { /**
ZMK_ENDPOINT_USB, * The method by which data is sent.
ZMK_ENDPOINT_BLE, */
enum zmk_transport {
ZMK_TRANSPORT_USB,
ZMK_TRANSPORT_BLE,
};
/**
* Configuration to select an endpoint on ZMK_TRANSPORT_USB.
*/
struct zmk_transport_usb_data {};
/**
* Configuration to select an endpoint on ZMK_TRANSPORT_BLE.
*/
struct zmk_transport_ble_data {
int profile_index;
};
/**
* A specific endpoint to which data may be sent.
*/
struct zmk_endpoint_instance {
enum zmk_transport transport;
union {
struct zmk_transport_usb_data usb; // ZMK_TRANSPORT_USB
struct zmk_transport_ble_data ble; // ZMK_TRANSPORT_BLE
};
}; };

View file

@ -11,8 +11,8 @@
#include <zmk/endpoints_types.h> #include <zmk/endpoints_types.h>
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
struct zmk_endpoint_selection_changed { struct zmk_endpoint_changed {
enum zmk_endpoint endpoint; struct zmk_endpoint_instance endpoint;
}; };
ZMK_EVENT_DECLARE(zmk_endpoint_selection_changed); ZMK_EVENT_DECLARE(zmk_endpoint_changed);

View file

@ -24,11 +24,11 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { switch (binding->param1) {
case OUT_TOG: case OUT_TOG:
return zmk_endpoints_toggle(); return zmk_endpoints_toggle_transport();
case OUT_USB: case OUT_USB:
return zmk_endpoints_select(ZMK_ENDPOINT_USB); return zmk_endpoints_select_transport(ZMK_TRANSPORT_USB);
case OUT_BLE: case OUT_BLE:
return zmk_endpoints_select(ZMK_ENDPOINT_BLE); return zmk_endpoints_select_transport(ZMK_TRANSPORT_BLE);
default: default:
LOG_ERR("Unknown output command: %d", binding->param1); LOG_ERR("Unknown output command: %d", binding->param1);
} }

View file

@ -12,9 +12,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h> #include <zmk/display.h>
#include <zmk/display/widgets/output_status.h> #include <zmk/display/widgets/output_status.h>
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/usb_conn_state_changed.h>
#include <zmk/events/ble_active_profile_changed.h> #include <zmk/events/ble_active_profile_changed.h>
#include <zmk/events/endpoint_selection_changed.h> #include <zmk/events/endpoint_changed.h>
#include <zmk/usb.h> #include <zmk/usb.h>
#include <zmk/ble.h> #include <zmk/ble.h>
#include <zmk/endpoints.h> #include <zmk/endpoints.h>
@ -22,40 +21,38 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets);
struct output_status_state { struct output_status_state {
enum zmk_endpoint selected_endpoint; struct zmk_endpoint_instance selected_endpoint;
bool active_profile_connected; bool active_profile_connected;
bool active_profile_bonded; bool active_profile_bonded;
uint8_t active_profile_index;
}; };
static struct output_status_state get_state(const zmk_event_t *_eh) { static struct output_status_state get_state(const zmk_event_t *_eh) {
return (struct output_status_state){.selected_endpoint = zmk_endpoints_selected(), return (struct output_status_state){.selected_endpoint = zmk_endpoints_selected(),
.active_profile_connected = .active_profile_connected =
zmk_ble_active_profile_is_connected(), zmk_ble_active_profile_is_connected(),
.active_profile_bonded = !zmk_ble_active_profile_is_open(), .active_profile_bonded = !zmk_ble_active_profile_is_open()};
.active_profile_index = zmk_ble_active_profile_index()};
; ;
} }
static void set_status_symbol(lv_obj_t *label, struct output_status_state state) { static void set_status_symbol(lv_obj_t *label, struct output_status_state state) {
char text[10] = {}; char text[10] = {};
switch (state.selected_endpoint) { switch (state.selected_endpoint.transport) {
case ZMK_ENDPOINT_USB: case ZMK_TRANSPORT_USB:
strcat(text, LV_SYMBOL_USB); strcat(text, LV_SYMBOL_USB);
break; break;
case ZMK_ENDPOINT_BLE: case ZMK_TRANSPORT_BLE:
if (state.active_profile_bonded) { if (state.active_profile_bonded) {
if (state.active_profile_connected) { if (state.active_profile_connected) {
snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_OK, snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_OK,
state.active_profile_index + 1); state.selected_endpoint.ble.profile_index + 1);
} else { } else {
snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_CLOSE, snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_CLOSE,
state.active_profile_index + 1); state.selected_endpoint.ble.profile_index + 1);
} }
} else { } else {
snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_SETTINGS, snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_SETTINGS,
state.active_profile_index + 1); state.selected_endpoint.ble.profile_index + 1);
} }
break; break;
} }
@ -70,11 +67,9 @@ static void output_status_update_cb(struct output_status_state state) {
ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state, ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state,
output_status_update_cb, get_state) output_status_update_cb, get_state)
ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_selection_changed); ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_changed);
// We don't get an endpoint changed event when the active profile connects/disconnects
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) // but there wasn't another endpoint to switch from/to, so update on BLE events too.
ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed);
#endif
#if defined(CONFIG_ZMK_BLE) #if defined(CONFIG_ZMK_BLE)
ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed);
#endif #endif

View file

@ -16,29 +16,29 @@
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/ble_active_profile_changed.h> #include <zmk/events/ble_active_profile_changed.h>
#include <zmk/events/usb_conn_state_changed.h> #include <zmk/events/usb_conn_state_changed.h>
#include <zmk/events/endpoint_selection_changed.h> #include <zmk/events/endpoint_changed.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define DEFAULT_ENDPOINT \ #define DEFAULT_TRANSPORT \
COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_ENDPOINT_BLE), (ZMK_ENDPOINT_USB)) COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_TRANSPORT_BLE), (ZMK_TRANSPORT_USB))
static enum zmk_endpoint current_endpoint = DEFAULT_ENDPOINT; static struct zmk_endpoint_instance current_instance = {};
static enum zmk_endpoint preferred_endpoint = static enum zmk_transport preferred_transport =
ZMK_ENDPOINT_USB; /* Used if multiple endpoints are ready */ ZMK_TRANSPORT_USB; /* Used if multiple endpoints are ready */
static void update_current_endpoint(); static void update_current_endpoint(void);
#if IS_ENABLED(CONFIG_SETTINGS) #if IS_ENABLED(CONFIG_SETTINGS)
static void endpoints_save_preferred_work(struct k_work *work) { static void endpoints_save_preferred_work(struct k_work *work) {
settings_save_one("endpoints/preferred", &preferred_endpoint, sizeof(preferred_endpoint)); settings_save_one("endpoints/preferred", &preferred_transport, sizeof(preferred_transport));
} }
static struct k_work_delayable endpoints_save_work; static struct k_work_delayable endpoints_save_work;
#endif #endif
static int endpoints_save_preferred() { static int endpoints_save_preferred(void) {
#if IS_ENABLED(CONFIG_SETTINGS) #if IS_ENABLED(CONFIG_SETTINGS)
return k_work_reschedule(&endpoints_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); return k_work_reschedule(&endpoints_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE));
#else #else
@ -46,14 +46,60 @@ static int endpoints_save_preferred() {
#endif #endif
} }
int zmk_endpoints_select(enum zmk_endpoint endpoint) { bool zmk_endpoint_instance_eq(struct zmk_endpoint_instance a, struct zmk_endpoint_instance b) {
LOG_DBG("Selected endpoint %d", endpoint); if (a.transport != b.transport) {
return false;
}
if (preferred_endpoint == endpoint) { switch (a.transport) {
case ZMK_TRANSPORT_USB:
return true;
case ZMK_TRANSPORT_BLE:
return a.ble.profile_index == b.ble.profile_index;
}
LOG_ERR("Invalid transport %d", a.transport);
return false;
}
int zmk_endpoint_instance_to_str(struct zmk_endpoint_instance endpoint, char *str, size_t len) {
switch (endpoint.transport) {
case ZMK_TRANSPORT_USB:
return snprintf(str, len, "USB");
case ZMK_TRANSPORT_BLE:
return snprintf(str, len, "BLE:%d", endpoint.ble.profile_index);
default:
return snprintf(str, len, "Invalid");
}
}
#define INSTANCE_INDEX_OFFSET_USB 0
#define INSTANCE_INDEX_OFFSET_BLE ZMK_ENDPOINT_USB_COUNT
int zmk_endpoint_instance_to_index(struct zmk_endpoint_instance endpoint) {
switch (endpoint.transport) {
case ZMK_TRANSPORT_USB:
return INSTANCE_INDEX_OFFSET_USB;
case ZMK_TRANSPORT_BLE:
return INSTANCE_INDEX_OFFSET_BLE + endpoint.ble.profile_index;
}
LOG_ERR("Invalid transport %d", endpoint.transport);
return 0;
}
int zmk_endpoints_select_transport(enum zmk_transport transport) {
LOG_DBG("Selected endpoint transport %d", transport);
if (preferred_transport == transport) {
return 0; return 0;
} }
preferred_endpoint = endpoint; preferred_transport = transport;
endpoints_save_preferred(); endpoints_save_preferred();
@ -62,20 +108,22 @@ int zmk_endpoints_select(enum zmk_endpoint endpoint) {
return 0; return 0;
} }
enum zmk_endpoint zmk_endpoints_selected() { return current_endpoint; } int zmk_endpoints_toggle_transport(void) {
enum zmk_transport new_transport =
int zmk_endpoints_toggle() { (preferred_transport == ZMK_TRANSPORT_USB) ? ZMK_TRANSPORT_BLE : ZMK_TRANSPORT_USB;
enum zmk_endpoint new_endpoint = return zmk_endpoints_select_transport(new_transport);
(preferred_endpoint == ZMK_ENDPOINT_USB) ? ZMK_ENDPOINT_BLE : ZMK_ENDPOINT_USB;
return zmk_endpoints_select(new_endpoint);
} }
static int send_keyboard_report() { struct zmk_endpoint_instance zmk_endpoints_selected(void) {
return current_instance;
}
static int send_keyboard_report(void) {
struct zmk_hid_keyboard_report *keyboard_report = zmk_hid_get_keyboard_report(); struct zmk_hid_keyboard_report *keyboard_report = zmk_hid_get_keyboard_report();
switch (current_endpoint) { switch (current_instance.transport) {
#if IS_ENABLED(CONFIG_ZMK_USB) #if IS_ENABLED(CONFIG_ZMK_USB)
case ZMK_ENDPOINT_USB: { case ZMK_TRANSPORT_USB: {
int err = zmk_usb_hid_send_report((uint8_t *)keyboard_report, sizeof(*keyboard_report)); int err = zmk_usb_hid_send_report((uint8_t *)keyboard_report, sizeof(*keyboard_report));
if (err) { if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err); LOG_ERR("FAILED TO SEND OVER USB: %d", err);
@ -85,7 +133,7 @@ static int send_keyboard_report() {
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE) #if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_ENDPOINT_BLE: { case ZMK_TRANSPORT_BLE: {
int err = zmk_hog_send_keyboard_report(&keyboard_report->body); int err = zmk_hog_send_keyboard_report(&keyboard_report->body);
if (err) { if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err); LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
@ -93,19 +141,18 @@ static int send_keyboard_report() {
return err; return err;
} }
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ #endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
default:
LOG_ERR("Unsupported endpoint %d", current_endpoint);
return -ENOTSUP;
} }
LOG_ERR("Unsupported endpoint transport %d", current_instance.transport);
return -ENOTSUP;
} }
static int send_consumer_report() { static int send_consumer_report(void) {
struct zmk_hid_consumer_report *consumer_report = zmk_hid_get_consumer_report(); struct zmk_hid_consumer_report *consumer_report = zmk_hid_get_consumer_report();
switch (current_endpoint) { switch (current_instance.transport) {
#if IS_ENABLED(CONFIG_ZMK_USB) #if IS_ENABLED(CONFIG_ZMK_USB)
case ZMK_ENDPOINT_USB: { case ZMK_TRANSPORT_USB: {
int err = zmk_usb_hid_send_report((uint8_t *)consumer_report, sizeof(*consumer_report)); int err = zmk_usb_hid_send_report((uint8_t *)consumer_report, sizeof(*consumer_report));
if (err) { if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err); LOG_ERR("FAILED TO SEND OVER USB: %d", err);
@ -115,7 +162,7 @@ static int send_consumer_report() {
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */ #endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE) #if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_ENDPOINT_BLE: { case ZMK_TRANSPORT_BLE: {
int err = zmk_hog_send_consumer_report(&consumer_report->body); int err = zmk_hog_send_consumer_report(&consumer_report->body);
if (err) { if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err); LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
@ -123,11 +170,10 @@ static int send_consumer_report() {
return err; return err;
} }
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */ #endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
default:
LOG_ERR("Unsupported endpoint %d", current_endpoint);
return -ENOTSUP;
} }
LOG_ERR("Unsupported endpoint transport %d", current_instance.transport);
return -ENOTSUP;
} }
int zmk_endpoints_send_report(uint16_t usage_page) { int zmk_endpoints_send_report(uint16_t usage_page) {
@ -136,12 +182,13 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
switch (usage_page) { switch (usage_page) {
case HID_USAGE_KEY: case HID_USAGE_KEY:
return send_keyboard_report(); return send_keyboard_report();
case HID_USAGE_CONSUMER: case HID_USAGE_CONSUMER:
return send_consumer_report(); return send_consumer_report();
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) #if IS_ENABLED(CONFIG_SETTINGS)
@ -151,12 +198,12 @@ static int endpoints_handle_set(const char *name, size_t len, settings_read_cb r
LOG_DBG("Setting endpoint value %s", name); LOG_DBG("Setting endpoint value %s", name);
if (settings_name_steq(name, "preferred", NULL)) { if (settings_name_steq(name, "preferred", NULL)) {
if (len != sizeof(enum zmk_endpoint)) { if (len != sizeof(enum zmk_transport)) {
LOG_ERR("Invalid endpoint size (got %d expected %d)", len, sizeof(enum zmk_endpoint)); LOG_ERR("Invalid endpoint size (got %d expected %d)", len, sizeof(enum zmk_transport));
return -EINVAL; return -EINVAL;
} }
int err = read_cb(cb_arg, &preferred_endpoint, sizeof(enum zmk_endpoint)); int err = read_cb(cb_arg, &preferred_transport, sizeof(enum zmk_transport));
if (err <= 0) { if (err <= 0) {
LOG_ERR("Failed to read preferred endpoint from settings (err %d)", err); LOG_ERR("Failed to read preferred endpoint from settings (err %d)", err);
return err; return err;
@ -171,6 +218,60 @@ static int endpoints_handle_set(const char *name, size_t len, settings_read_cb r
struct settings_handler endpoints_handler = {.name = "endpoints", .h_set = endpoints_handle_set}; struct settings_handler endpoints_handler = {.name = "endpoints", .h_set = endpoints_handle_set};
#endif /* IS_ENABLED(CONFIG_SETTINGS) */ #endif /* IS_ENABLED(CONFIG_SETTINGS) */
static bool is_usb_ready(void) {
#if IS_ENABLED(CONFIG_ZMK_USB)
return zmk_usb_is_hid_ready();
#else
return false;
#endif
}
static bool is_ble_ready(void) {
#if IS_ENABLED(CONFIG_ZMK_BLE)
return zmk_ble_active_profile_is_connected();
#else
return false;
#endif
}
static enum zmk_transport get_selected_transport(void) {
if (is_ble_ready()) {
if (is_usb_ready()) {
LOG_DBG("Both endpoint transports are ready. Using %d", preferred_transport);
return preferred_transport;
}
LOG_DBG("Only BLE is ready.");
return ZMK_TRANSPORT_BLE;
}
if (is_usb_ready()) {
LOG_DBG("Only USB is ready.");
return ZMK_TRANSPORT_USB;
}
LOG_DBG("No endpoint transports are ready.");
return DEFAULT_TRANSPORT;
}
static struct zmk_endpoint_instance get_selected_instance(void) {
struct zmk_endpoint_instance instance = {.transport = get_selected_transport()};
switch (instance.transport) {
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_TRANSPORT_BLE:
instance.ble.profile_index = zmk_ble_active_profile_index();
break;
#endif // IS_ENABLED(CONFIG_ZMK_BLE)
default:
// No extra data for this transport.
break;
}
return instance;
}
static int zmk_endpoints_init(const struct device *_arg) { static int zmk_endpoints_init(const struct device *_arg) {
#if IS_ENABLED(CONFIG_SETTINGS) #if IS_ENABLED(CONFIG_SETTINGS)
settings_subsys_init(); settings_subsys_init();
@ -186,45 +287,11 @@ static int zmk_endpoints_init(const struct device *_arg) {
settings_load_subtree("endpoints"); settings_load_subtree("endpoints");
#endif #endif
current_instance = get_selected_instance();
return 0; 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() { static void disconnect_current_endpoint() {
zmk_hid_keyboard_clear(); zmk_hid_keyboard_clear();
zmk_hid_consumer_clear(); zmk_hid_consumer_clear();
@ -233,18 +300,21 @@ static void disconnect_current_endpoint() {
zmk_endpoints_send_report(HID_USAGE_CONSUMER); zmk_endpoints_send_report(HID_USAGE_CONSUMER);
} }
static void update_current_endpoint() { static void update_current_endpoint(void) {
enum zmk_endpoint new_endpoint = get_selected_endpoint(); struct zmk_endpoint_instance new_instance = get_selected_instance();
if (new_endpoint != current_endpoint) { if (!zmk_endpoint_instance_eq(new_instance, current_instance)) {
/* Cancel all current keypresses so keys don't stay held on the old endpoint. */ // Cancel all current keypresses so keys don't stay held on the old endpoint.
disconnect_current_endpoint(); disconnect_current_endpoint();
current_endpoint = new_endpoint; current_instance = new_instance;
LOG_INF("Endpoint changed: %d", current_endpoint);
ZMK_EVENT_RAISE(new_zmk_endpoint_selection_changed( char endpoint_str[ZMK_ENDPOINT_STR_LEN];
(struct zmk_endpoint_selection_changed){.endpoint = current_endpoint})); zmk_endpoint_instance_to_str(current_instance, endpoint_str, sizeof(endpoint_str));
LOG_INF("Endpoint changed: %s", endpoint_str);
ZMK_EVENT_RAISE(
new_zmk_endpoint_changed((struct zmk_endpoint_changed){.endpoint = current_instance}));
} }
} }

View file

@ -5,6 +5,6 @@
*/ */
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zmk/events/endpoint_selection_changed.h> #include <zmk/events/endpoint_changed.h>
ZMK_EVENT_IMPL(zmk_endpoint_selection_changed); ZMK_EVENT_IMPL(zmk_endpoint_changed);