Compare commits

...

5 Commits

Author SHA1 Message Date
Peter Johanson
0b23d07954 refactor: Move to zmk_hid_indicators_t type. 2023-11-27 15:49:43 -08:00
Alessandro Bortolin
f14ac2b3d6 feat: LED indicators on peripheral side 2023-11-27 15:06:48 -08:00
Alessandro Bortolin
c23cc8ee42 feat: handle LED indicators report 2023-11-27 14:47:45 -08:00
ReFil
6276e973d5 feat(ble): Only update BAS when active
Subscribes to the activity changing event, will stop the battery work timer when in idle or deep sleep, restart when board goes active
2023-11-27 09:58:20 -08:00
ReFil
84b93350b8 feat(docs): Document adding USB logging to a standalone board (#2039)
Currently this is only documented in the zephyr 3.0 upgrade blog. This explicitly documents it as well as when it doesn't need to be applied (i.e. when a mcu board is already in use).
2023-11-27 09:12:33 -08:00
20 changed files with 444 additions and 19 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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();

View 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);

View File

@@ -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];

View 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);

View File

@@ -0,0 +1,9 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
typedef uint8_t zmk_hid_indicators_t;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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));

View 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
View 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);

View File

@@ -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),
};

View File

@@ -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

View File

@@ -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),

View File

@@ -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);

View File

@@ -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) {

View File

@@ -22,9 +22,10 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/
### HID
| Config | Type | Description | Default |
| ------------------------------------- | ---- | ------------------------------------------------- | ------- |
| `CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE` | int | Number of consumer keys simultaneously reportable | 6 |
| 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.
@@ -91,14 +92,15 @@ 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_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 |
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 |
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 |
| 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 |
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 |
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 |

View File

@@ -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;
};
...
};
```