forked from kofal.net/zmk
Compare commits
5 Commits
main
...
led-indica
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b23d07954 | ||
|
|
f14ac2b3d6 | ||
|
|
c23cc8ee42 | ||
|
|
6276e973d5 | ||
|
|
84b93350b8 |
@@ -68,6 +68,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/events/layer_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE src/hid_indicators.c)
|
||||
|
||||
if (CONFIG_ZMK_BLE)
|
||||
target_sources(app PRIVATE src/events/ble_active_profile_changed.c)
|
||||
@@ -83,6 +84,8 @@ target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_bac
|
||||
target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/battery.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE src/events/hid_indicators_changed.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
|
||||
add_subdirectory(src/split)
|
||||
|
||||
|
||||
@@ -87,6 +87,12 @@ config ZMK_HID_CONSUMER_REPORT_USAGES_BASIC
|
||||
|
||||
endchoice
|
||||
|
||||
config ZMK_HID_INDICATORS
|
||||
bool "HID Indicators"
|
||||
help
|
||||
Enable HID indicators, used for detecting state of Caps/Scroll/Num Lock,
|
||||
Kata, and Compose.
|
||||
|
||||
menu "Output Types"
|
||||
|
||||
config ZMK_USB
|
||||
|
||||
@@ -27,6 +27,7 @@ int zmk_ble_prof_select(uint8_t index);
|
||||
int zmk_ble_prof_disconnect(uint8_t index);
|
||||
|
||||
int zmk_ble_active_profile_index();
|
||||
int zmk_ble_profile_index(const bt_addr_le_t *addr);
|
||||
bt_addr_le_t *zmk_ble_active_profile_addr();
|
||||
bool zmk_ble_active_profile_is_open();
|
||||
bool zmk_ble_active_profile_is_connected();
|
||||
|
||||
16
app/include/zmk/events/hid_indicators_changed.h
Normal file
16
app/include/zmk/events/hid_indicators_changed.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
#include <zmk/event_manager.h>
|
||||
|
||||
struct zmk_hid_indicators_changed {
|
||||
zmk_hid_indicators_t indicators;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_hid_indicators_changed);
|
||||
@@ -50,6 +50,7 @@
|
||||
#define ZMK_HID_MAIN_VAL_BUFFERED_BYTES (0x01 << 8)
|
||||
|
||||
#define ZMK_HID_REPORT_ID_KEYBOARD 0x01
|
||||
#define ZMK_HID_REPORT_ID_LEDS 0x01
|
||||
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
|
||||
#define ZMK_HID_REPORT_ID_MOUSE 0x03
|
||||
|
||||
@@ -73,6 +74,22 @@ static const uint8_t zmk_hid_report_desc[] = {
|
||||
HID_REPORT_COUNT(0x01),
|
||||
HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
HID_USAGE_PAGE(HID_USAGE_LED),
|
||||
HID_USAGE_MIN8(HID_USAGE_LED_NUM_LOCK),
|
||||
HID_USAGE_MAX8(HID_USAGE_LED_KANA),
|
||||
HID_REPORT_SIZE(0x01),
|
||||
HID_REPORT_COUNT(0x05),
|
||||
HID_OUTPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
|
||||
|
||||
HID_USAGE_PAGE(HID_USAGE_LED),
|
||||
HID_REPORT_SIZE(0x03),
|
||||
HID_REPORT_COUNT(0x01),
|
||||
HID_OUTPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
HID_USAGE_PAGE(HID_USAGE_KEY),
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_REPORT_TYPE_NKRO)
|
||||
@@ -189,6 +206,19 @@ struct zmk_hid_keyboard_report {
|
||||
struct zmk_hid_keyboard_report_body body;
|
||||
} __packed;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
struct zmk_hid_led_report_body {
|
||||
uint8_t leds;
|
||||
} __packed;
|
||||
|
||||
struct zmk_hid_led_report {
|
||||
uint8_t report_id;
|
||||
struct zmk_hid_led_report_body body;
|
||||
} __packed;
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
struct zmk_hid_consumer_report_body {
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC)
|
||||
uint8_t keys[CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE];
|
||||
|
||||
19
app/include/zmk/hid_indicators.h
Normal file
19
app/include/zmk/hid_indicators.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zmk/endpoints_types.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
|
||||
zmk_hid_indicators_t zmk_hid_indicators_get_current_profile(void);
|
||||
zmk_hid_indicators_t zmk_hid_indicators_get_profile(struct zmk_endpoint_instance endpoint);
|
||||
void zmk_hid_indicators_set_profile(zmk_hid_indicators_t indicators,
|
||||
struct zmk_endpoint_instance endpoint);
|
||||
|
||||
void zmk_hid_indicators_process_report(struct zmk_hid_led_report_body *report,
|
||||
struct zmk_endpoint_instance endpoint);
|
||||
9
app/include/zmk/hid_indicators_types.h
Normal file
9
app/include/zmk/hid_indicators_types.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef uint8_t zmk_hid_indicators_t;
|
||||
@@ -4,5 +4,15 @@
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event, bool state);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators);
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001)
|
||||
#define ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002)
|
||||
#define ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000003)
|
||||
#define ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID ZMK_BT_SPLIT_UUID(0x00000004)
|
||||
|
||||
@@ -18,6 +18,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/battery.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
#include <zmk/events/activity_state_changed.h>
|
||||
#include <zmk/activity.h>
|
||||
#include <zmk/workqueue.h>
|
||||
|
||||
static uint8_t last_state_of_charge = 0;
|
||||
@@ -84,6 +86,10 @@ static void zmk_battery_timer(struct k_timer *timer) {
|
||||
|
||||
K_TIMER_DEFINE(battery_timer, zmk_battery_timer, NULL);
|
||||
|
||||
static void zmk_battery_start_reporting() {
|
||||
k_timer_start(&battery_timer, K_NO_WAIT, K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL));
|
||||
}
|
||||
|
||||
static int zmk_battery_init(const struct device *_arg) {
|
||||
#if !DT_HAS_CHOSEN(zmk_battery)
|
||||
battery = device_get_binding("BATTERY");
|
||||
@@ -100,9 +106,30 @@ static int zmk_battery_init(const struct device *_arg) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_timer_start(&battery_timer, K_NO_WAIT, K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL));
|
||||
|
||||
zmk_battery_start_reporting();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int battery_event_listener(const zmk_event_t *eh) {
|
||||
|
||||
if (as_zmk_activity_state_changed(eh)) {
|
||||
switch (zmk_activity_get_state()) {
|
||||
case ZMK_ACTIVITY_ACTIVE:
|
||||
zmk_battery_start_reporting();
|
||||
return 0;
|
||||
case ZMK_ACTIVITY_IDLE:
|
||||
case ZMK_ACTIVITY_SLEEP:
|
||||
k_timer_stop(&battery_timer);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ZMK_LISTENER(battery, battery_event_listener);
|
||||
|
||||
ZMK_SUBSCRIPTION(battery, zmk_activity_state_changed);
|
||||
|
||||
SYS_INIT(zmk_battery_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
||||
@@ -224,6 +224,15 @@ int zmk_ble_clear_bonds() {
|
||||
|
||||
int zmk_ble_active_profile_index() { return active_profile; }
|
||||
|
||||
int zmk_ble_profile_index(const bt_addr_le_t *addr) {
|
||||
for (int i = 0; i < ZMK_BLE_PROFILE_COUNT; i++) {
|
||||
if (bt_addr_le_cmp(addr, &profiles[i].peer) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
static void ble_save_profile_work(struct k_work *work) {
|
||||
settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile));
|
||||
|
||||
10
app/src/events/hid_indicators_changed.c
Normal file
10
app/src/events/hid_indicators_changed.c
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/hid_indicators_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_hid_indicators_changed);
|
||||
69
app/src/hid_indicators.c
Normal file
69
app/src/hid_indicators.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/endpoints.h>
|
||||
#include <zmk/hid_indicators.h>
|
||||
#include <zmk/events/hid_indicators_changed.h>
|
||||
#include <zmk/events/endpoint_changed.h>
|
||||
#include <zmk/split/bluetooth/central.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
static zmk_hid_indicators_t hid_indicators[ZMK_ENDPOINT_COUNT];
|
||||
|
||||
zmk_hid_indicators_t zmk_hid_indicators_get_current_profile(void) {
|
||||
return zmk_hid_indicators_get_profile(zmk_endpoints_selected());
|
||||
}
|
||||
|
||||
zmk_hid_indicators_t zmk_hid_indicators_get_profile(struct zmk_endpoint_instance endpoint) {
|
||||
const int profile = zmk_endpoint_instance_to_index(endpoint);
|
||||
return hid_indicators[profile];
|
||||
}
|
||||
|
||||
static void raise_led_changed_event(struct k_work *_work) {
|
||||
const zmk_hid_indicators_t indicators = zmk_hid_indicators_get_current_profile();
|
||||
|
||||
ZMK_EVENT_RAISE(new_zmk_hid_indicators_changed(
|
||||
(struct zmk_hid_indicators_changed){.indicators = indicators}));
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) && IS_ENABLED(CONFIG_ZMK_SPLIT_BLE)
|
||||
zmk_split_bt_update_hid_indicator(indicators);
|
||||
#endif
|
||||
}
|
||||
|
||||
static K_WORK_DEFINE(led_changed_work, raise_led_changed_event);
|
||||
|
||||
void zmk_hid_indicators_set_profile(zmk_hid_indicators_t indicators,
|
||||
struct zmk_endpoint_instance endpoint) {
|
||||
int profile = zmk_endpoint_instance_to_index(endpoint);
|
||||
|
||||
// This write is not happening on the main thread. To prevent potential data races, every
|
||||
// operation involving hid_indicators must be atomic. Currently, each function either reads
|
||||
// or writes only one entry at a time, so it is safe to do these operations without a lock.
|
||||
hid_indicators[profile] = indicators;
|
||||
|
||||
k_work_submit(&led_changed_work);
|
||||
}
|
||||
|
||||
void zmk_hid_indicators_process_report(struct zmk_hid_led_report_body *report,
|
||||
struct zmk_endpoint_instance endpoint) {
|
||||
const zmk_hid_indicators_t indicators = (zmk_hid_indicators_t)report->leds;
|
||||
zmk_hid_indicators_set_profile(indicators, endpoint);
|
||||
|
||||
LOG_DBG("Update HID indicators: endpoint=%d, indicators=%x", endpoint.transport, indicators);
|
||||
}
|
||||
|
||||
static int profile_listener(const zmk_event_t *eh) {
|
||||
raise_led_changed_event(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ZMK_LISTENER(profile_listener, profile_listener);
|
||||
static ZMK_SUBSCRIPTION(profile_listener, zmk_endpoint_changed);
|
||||
@@ -15,8 +15,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/endpoints_types.h>
|
||||
#include <zmk/hog.h>
|
||||
#include <zmk/hid.h>
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
enum {
|
||||
HIDS_REMOTE_WAKE = BIT(0),
|
||||
@@ -51,6 +55,15 @@ static struct hids_report input = {
|
||||
.type = HIDS_INPUT,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
static struct hids_report led_indicators = {
|
||||
.id = ZMK_HID_REPORT_ID_LEDS,
|
||||
.type = HIDS_OUTPUT,
|
||||
};
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
static struct hids_report consumer_input = {
|
||||
.id = ZMK_HID_REPORT_ID_CONSUMER,
|
||||
.type = HIDS_INPUT,
|
||||
@@ -94,6 +107,34 @@ static ssize_t read_hids_input_report(struct bt_conn *conn, const struct bt_gatt
|
||||
sizeof(struct zmk_hid_keyboard_report_body));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
static ssize_t write_hids_leds_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len, uint16_t offset,
|
||||
uint8_t flags) {
|
||||
if (offset != 0) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
if (len != sizeof(struct zmk_hid_led_report_body)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
}
|
||||
|
||||
struct zmk_hid_led_report_body *report = (struct zmk_hid_led_report_body *)buf;
|
||||
int profile = zmk_ble_profile_index(bt_conn_get_dst(conn));
|
||||
if (profile < 0) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
|
||||
}
|
||||
|
||||
struct zmk_endpoint_instance endpoint = {.transport = ZMK_TRANSPORT_BLE,
|
||||
.ble = {
|
||||
.profile_index = profile,
|
||||
}};
|
||||
zmk_hid_indicators_process_report(report, endpoint);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset) {
|
||||
@@ -152,6 +193,7 @@ BT_GATT_SERVICE_DEFINE(
|
||||
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &input),
|
||||
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_READ_ENCRYPT, read_hids_consumer_input_report, NULL, NULL),
|
||||
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
@@ -166,6 +208,15 @@ BT_GATT_SERVICE_DEFINE(
|
||||
NULL, &mouse_input),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
||||
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
write_hids_leds_report, NULL),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &led_indicators),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
||||
BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point));
|
||||
|
||||
@@ -251,7 +302,7 @@ void send_consumer_report_callback(struct k_work *work) {
|
||||
}
|
||||
|
||||
struct bt_gatt_notify_params notify_params = {
|
||||
.attr = &hog_svc.attrs[10],
|
||||
.attr = &hog_svc.attrs[9],
|
||||
.data = &report,
|
||||
.len = sizeof(report),
|
||||
};
|
||||
|
||||
@@ -20,6 +20,12 @@ config ZMK_SPLIT_BLE
|
||||
|
||||
endchoice
|
||||
|
||||
config ZMK_SPLIT_PERIPHERAL_HID_INDICATORS
|
||||
bool "Peripheral HID Indicators"
|
||||
depends on ZMK_HID_INDICATORS
|
||||
help
|
||||
Enable propogating the HID (LED) Indicator state to the split peripheral(s).
|
||||
|
||||
#ZMK_SPLIT
|
||||
endif
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/sensor_event.h>
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
|
||||
static int start_scanning(void);
|
||||
|
||||
@@ -46,6 +47,9 @@ struct peripheral_slot {
|
||||
struct bt_gatt_subscribe_params sensor_subscribe_params;
|
||||
struct bt_gatt_discover_params sub_discover_params;
|
||||
uint16_t run_behavior_handle;
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
uint16_t update_hid_indicators;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
uint8_t position_state[POSITION_STATE_DATA_LEN];
|
||||
uint8_t changed_positions[POSITION_STATE_DATA_LEN];
|
||||
};
|
||||
@@ -131,6 +135,9 @@ int release_peripheral_slot(int index) {
|
||||
// Clean up previously discovered handles;
|
||||
slot->subscribe_params.value_handle = 0;
|
||||
slot->run_behavior_handle = 0;
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
slot->update_hid_indicators = 0;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -329,13 +336,23 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID))) {
|
||||
LOG_DBG("Found update HID indicators handle");
|
||||
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
}
|
||||
|
||||
bool subscribed = slot->run_behavior_handle && slot->subscribe_params.value_handle;
|
||||
bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle);
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
subscribed = subscribed && slot->sensor_subscribe_params.value_handle;
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
subscribed = subscribed && slot->update_hid_indicators;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -685,6 +702,43 @@ int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *bi
|
||||
return split_bt_invoke_behavior_payload(wrapper);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
static zmk_hid_indicators_t hid_indicators = 0;
|
||||
|
||||
static void split_central_update_indicators_callback(struct k_work *work) {
|
||||
zmk_hid_indicators_t indicators = hid_indicators;
|
||||
for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
|
||||
if (peripherals[i].state != PERIPHERAL_SLOT_STATE_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peripherals[i].update_hid_indicators == 0) {
|
||||
// It appears that sometimes the peripheral is considered connected
|
||||
// before the GATT characteristics have been discovered. If this is
|
||||
// the case, the update_hid_indicators handle will not yet be set.
|
||||
continue;
|
||||
}
|
||||
|
||||
int err = bt_gatt_write_without_response(peripherals[i].conn,
|
||||
peripherals[i].update_hid_indicators, &indicators,
|
||||
sizeof(indicators), true);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write HID indicator characteristic (err %d)", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static K_WORK_DEFINE(split_central_update_indicators, split_central_update_indicators_callback);
|
||||
|
||||
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators) {
|
||||
hid_indicators = indicators;
|
||||
return k_work_submit_to_queue(&split_central_split_run_q, &split_central_update_indicators);
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
int zmk_split_bt_central_init(const struct device *_arg) {
|
||||
k_work_queue_start(&split_central_split_run_q, split_central_split_run_q_stack,
|
||||
K_THREAD_STACK_SIZEOF(split_central_split_run_q_stack),
|
||||
|
||||
@@ -21,6 +21,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/matrix.h>
|
||||
#include <zmk/split/bluetooth/uuid.h>
|
||||
#include <zmk/split/bluetooth/service.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#include <zmk/events/hid_indicators_changed.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
#include <zmk/events/sensor_event.h>
|
||||
#include <zmk/sensors.h>
|
||||
|
||||
@@ -105,6 +110,34 @@ static void split_svc_pos_state_ccc(const struct bt_gatt_attr *attr, uint16_t va
|
||||
LOG_DBG("value %d", value);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
static zmk_hid_indicators_t hid_indicators = 0;
|
||||
|
||||
static void split_svc_update_indicators_callback(struct k_work *work) {
|
||||
LOG_DBG("Raising HID indicators changed event: %x", hid_indicators);
|
||||
ZMK_EVENT_RAISE(new_zmk_hid_indicators_changed(
|
||||
(struct zmk_hid_indicators_changed){.indicators = hid_indicators}));
|
||||
}
|
||||
|
||||
static K_WORK_DEFINE(split_svc_update_indicators_work, split_svc_update_indicators_callback);
|
||||
|
||||
static ssize_t split_svc_update_indicators(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len, uint16_t offset,
|
||||
uint8_t flags) {
|
||||
if (offset + len > sizeof(zmk_hid_indicators_t)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)&hid_indicators + offset, buf, len);
|
||||
|
||||
k_work_submit(&split_svc_update_indicators_work);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(
|
||||
split_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID)),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID),
|
||||
@@ -122,6 +155,11 @@ BT_GATT_SERVICE_DEFINE(
|
||||
split_svc_sensor_state, NULL, &last_sensor_event),
|
||||
BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID),
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
split_svc_update_indicators, NULL),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
);
|
||||
|
||||
K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE);
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include <zmk/usb.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/keymap.h>
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
#include <zmk/event_manager.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
@@ -83,12 +86,44 @@ static int get_report_cb(const struct device *dev, struct usb_setup_packet *setu
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len,
|
||||
uint8_t **data) {
|
||||
if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_OUTPUT) {
|
||||
LOG_ERR("Unsupported report type %d requested",
|
||||
(setup->wValue & HID_GET_REPORT_TYPE_MASK) >> 8);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
case ZMK_HID_REPORT_ID_LEDS:
|
||||
if (*len != sizeof(struct zmk_hid_led_report)) {
|
||||
LOG_ERR("LED set report is malformed: length=%d", *len);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
struct zmk_hid_led_report *report = (struct zmk_hid_led_report *)*data;
|
||||
struct zmk_endpoint_instance endpoint = {
|
||||
.transport = ZMK_TRANSPORT_USB,
|
||||
};
|
||||
zmk_hid_indicators_process_report(&report->body, endpoint);
|
||||
}
|
||||
break;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_ops ops = {
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
.protocol_change = set_proto_cb,
|
||||
#endif
|
||||
.int_in_ready = in_ready_cb,
|
||||
.get_report = get_report_cb,
|
||||
.set_report = set_report_cb,
|
||||
};
|
||||
|
||||
static int zmk_usb_hid_send_report(const uint8_t *report, size_t len) {
|
||||
|
||||
@@ -23,7 +23,8 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/
|
||||
### HID
|
||||
|
||||
| Config | Type | Description | Default |
|
||||
| ------------------------------------- | ---- | ------------------------------------------------- | ------- |
|
||||
| ------------------------------------- | ---- | -------------------------------------------------------------- | ------- |
|
||||
| `CONFIG_ZMK_HID_INDICATORS` | bool | Enable reciept of HID/LED indicator state from connected hosts | n |
|
||||
| `CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE` | int | Number of consumer keys simultaneously reportable | 6 |
|
||||
|
||||
Exactly zero or one of the following options may be set to `y`. The first is used if none are set.
|
||||
@@ -92,8 +93,9 @@ Note that `CONFIG_BT_MAX_CONN` and `CONFIG_BT_MAX_PAIRED` should be set to the s
|
||||
Following split keyboard settings are defined in [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/Kconfig) (generic) and [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/bluetooth/Kconfig) (bluetooth).
|
||||
|
||||
| Config | Type | Description | Default |
|
||||
| ----------------------------------------------------- | ---- | ----------------------------------------------------------------------- | ------- |
|
||||
| ----------------------------------------------------- | ---- | ------------------------------------------------------------------------ | ------- |
|
||||
| `CONFIG_ZMK_SPLIT` | bool | Enable split keyboard support | n |
|
||||
| `CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS` | bool | Enable split keyboard support for passing indicator state to peripherals | n |
|
||||
| `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y |
|
||||
| `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | |
|
||||
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 |
|
||||
|
||||
@@ -83,3 +83,32 @@ You should see tio printing `Disconnected` or `Connected` when you disconnect or
|
||||
</Tabs>
|
||||
|
||||
From there, you should see the various log messages from ZMK and Zephyr, depending on which systems you have set to what log levels.
|
||||
|
||||
## Adding USB Logging to a Board
|
||||
|
||||
Standard boards such as the nice!nano and Seeeduino XIAO family have the necessary configuration for logging already added, however if you are developing your own standalone board you may wish to add the ability to use USB logging in the future.
|
||||
|
||||
To add USB logging to a board you need to define the USB CDC ACM device that the serial output gets piped to, as well as adding the console in the `chosen` node inside `<board>.dts`.
|
||||
|
||||
Inside the USB device (`&usbd`), add the CDC ACM node:
|
||||
|
||||
```dts
|
||||
&usbd {
|
||||
status = "okay";
|
||||
cdc_acm_uart: cdc_acm_uart {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Then you can add the `zephyr,console` binding in the `chosen` node:
|
||||
|
||||
```dts
|
||||
/ {
|
||||
chosen {
|
||||
...
|
||||
zephyr,console = &cdc_acm_uart;
|
||||
};
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user