summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Johanson <peter@peterjohanson.com>2020-08-28 14:15:16 -0400
committerPete Johanson <peter@peterjohanson.com>2020-09-13 22:33:05 -0400
commitcf970efb98c5af97955bfffbcebb3b065b16edc4 (patch)
treea44d5b05e62bf7bdd00d4e4bae38617a411a89f5
parent304603240f7ba16f67912a0031c64fb9ae4e8279 (diff)
feat(bluetooth): Proper bond management, identity support for non-splits
* Add `bt` behavior that can be used to perform certain actions, such as next/prev identity, reset identity, etc. NOTE: Multiple identities is only supported for non-split shields, due to missing Zephyr identity functionality for dual central/peripheral devices. * Proper bond reset tied to action, that honors peripheral bonds, so folks can reset and pair to other hosts, without breaking bonds between splt halves.
-rw-r--r--app/CMakeLists.txt7
-rw-r--r--app/Kconfig29
-rw-r--r--app/dts/behaviors.dtsi3
-rw-r--r--app/dts/behaviors/bluetooth.dtsi9
-rw-r--r--app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml8
-rw-r--r--app/include/dt-bindings/zmk/bt.h17
-rw-r--r--app/include/zmk/ble.h13
-rw-r--r--app/src/behaviors/behavior_bt.c62
-rw-r--r--app/src/ble.c335
-rw-r--r--app/src/split/bluetooth/central.c49
-rw-r--r--docs/docs/behavior/bluetooth.md82
-rw-r--r--docs/sidebars.js1
12 files changed, 563 insertions, 52 deletions
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 2e24fdc..0f2d2ba 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -41,8 +41,11 @@ 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_none.c)
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
-target_sources(app PRIVATE src/keymap.c)
+if(NOT CONFIG_ZMK_BLE_SPLIT_PERIPHERAL)
+ target_sources(app PRIVATE src/keymap.c)
+endif()
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
+target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble_unpair_combo.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split_listener.c)
@@ -57,4 +60,4 @@ target_sources(app PRIVATE src/endpoints.c)
target_sources(app PRIVATE src/hid_listener.c)
target_sources(app PRIVATE src/main.c)
-zephyr_cc_option(-Wfatal-errors) \ No newline at end of file
+zephyr_cc_option(-Wfatal-errors)
diff --git a/app/Kconfig b/app/Kconfig
index 877fce4..b60db05 100644
--- a/app/Kconfig
+++ b/app/Kconfig
@@ -40,8 +40,6 @@ menuconfig ZMK_BLE
select BT_PERIPHERAL
select BT_GATT_DIS
select BT_GATT_BAS
- select SETTINGS
- # select BT_SETTINGS
if ZMK_BLE
@@ -49,6 +47,12 @@ config ZMK_BLE_INIT_PRIORITY
int "Init Priority"
default 50
+config SETTINGS
+ default y
+
+config BT_SETTINGS
+ default y
+
config SYSTEM_WORKQUEUE_STACK_SIZE
default 2048
@@ -103,8 +107,11 @@ config ZMK_SPLIT_BLE_ROLE_CENTRAL
if ZMK_SPLIT_BLE_ROLE_CENTRAL
+config BT_MAX_PAIRED
+ default 2
+
config BT_MAX_CONN
- default 5
+ default 2
config BT_MAX_PAIRED
# Bump this everywhere once we support switching active connections!
@@ -120,15 +127,18 @@ if ZMK_SPLIT_BLE_ROLE_PERIPHERAL
config ZMK_USB
default n
+config BT_MAX_PAIRED
+ default 1
config BT_MAX_CONN
- default 5
+ default 2
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
default n
endif
+
endchoice
endif
@@ -138,6 +148,17 @@ endif
endmenu
+if ZMK_BLE && !ZMK_SPLIT_BLE
+
+config BT_ID_MAX
+ default 5
+
+# Used to update the name to include the identity used
+config BT_DEVICE_NAME_DYNAMIC
+ default y
+
+endif
+
config ZMK_KSCAN_MOCK_DRIVER
bool "Enable mock kscan driver to simulate key presses"
default n
diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
index ab70bcc..202202b 100644
--- a/app/dts/behaviors.dtsi
+++ b/app/dts/behaviors.dtsi
@@ -7,4 +7,5 @@
#include <behaviors/toggle_layer.dtsi>
#include <behaviors/reset.dtsi>
#include <behaviors/sensor_rotate_key_press.dtsi>
-#include <behaviors/rgb_underglow.dtsi> \ No newline at end of file
+#include <behaviors/rgb_underglow.dtsi>
+#include <behaviors/bluetooth.dtsi> \ No newline at end of file
diff --git a/app/dts/behaviors/bluetooth.dtsi b/app/dts/behaviors/bluetooth.dtsi
new file mode 100644
index 0000000..ea09f4a
--- /dev/null
+++ b/app/dts/behaviors/bluetooth.dtsi
@@ -0,0 +1,9 @@
+/ {
+ behaviors {
+ bt: behavior_bluetooth {
+ compatible = "zmk,behavior-bluetooth";
+ label = "BLUETOOTH";
+ #binding-cells = <2>;
+ };
+ };
+};
diff --git a/app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml b/app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml
new file mode 100644
index 0000000..127ebe0
--- /dev/null
+++ b/app/dts/bindings/behaviors/zmk,behavior-bluetooth.yaml
@@ -0,0 +1,8 @@
+# Copyright (c) 2020, Peter Johanson
+# SPDX-License-Identifier: MIT
+
+description: Bluetooth Behavior
+
+compatible: "zmk,behavior-bluetooth"
+
+include: two_param.yaml
diff --git a/app/include/dt-bindings/zmk/bt.h b/app/include/dt-bindings/zmk/bt.h
new file mode 100644
index 0000000..6b05ff0
--- /dev/null
+++ b/app/include/dt-bindings/zmk/bt.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define BT_RST_CMD 0
+#define BT_IDENT_NEXT_CMD 1
+#define BT_IDENT_PREV_CMD 2
+#define BT_IDENT_SEL_CMD 3
+#define BT_IDENT_CLR_CMD 4
+
+#define BT_RST BT_RST_CMD 0
+#define BT_IDENT_NEXT BT_IDENT_NEXT_CMD 0
+#define BT_IDENT_PREV BT_IDENT_PREV_CMD 0
+#define BT_IDENT_SEL BT_IDENT_SEL_CMD
+#define BT_IDENT_CLR BT_IDENT_CLR_CMD 0 \ No newline at end of file
diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h
index d0aaa96..b3762cd 100644
--- a/app/include/zmk/ble.h
+++ b/app/include/zmk/ble.h
@@ -6,7 +6,20 @@
#pragma once
+#include <bluetooth/addr.h>
#include <zmk/keys.h>
+int zmk_ble_adv_pause();
+int zmk_ble_adv_resume();
+
+int zmk_ble_identity_clear();
+int zmk_ble_identity_next();
+int zmk_ble_identity_prev();
+int zmk_ble_identity_select(u8_t index);
+
int zmk_ble_unpair_all();
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event);
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr);
+#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */ \ No newline at end of file
diff --git a/app/src/behaviors/behavior_bt.c b/app/src/behaviors/behavior_bt.c
new file mode 100644
index 0000000..724d245
--- /dev/null
+++ b/app/src/behaviors/behavior_bt.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_behavior_bluetooth
+
+#include <device.h>
+#include <drivers/behavior.h>
+
+#include <dt-bindings/zmk/bt.h>
+
+#include <bluetooth/conn.h>
+
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/ble.h>
+
+static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t command, u32_t arg)
+{
+ switch (command)
+ {
+ case BT_RST_CMD:
+ return zmk_ble_unpair_all();
+ case BT_IDENT_CLR_CMD:
+ return zmk_ble_identity_clear();
+#if CONFIG_BT_ID_MAX != 1
+ case BT_IDENT_NEXT_CMD:
+ return zmk_ble_identity_next();
+ case BT_IDENT_PREV_CMD:
+ return zmk_ble_identity_prev();
+ case BT_IDENT_SEL_CMD:
+ return zmk_ble_identity_select(arg);
+#endif /* BT_ID_MAX != 1 */
+ }
+
+ return -ENOTSUP;
+}
+
+static int behavior_bt_init(struct device *dev)
+{
+ return 0;
+};
+
+static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t command, u32_t arg)
+{
+ return 0;
+}
+
+static const struct behavior_driver_api behavior_bt_driver_api = {
+ .binding_pressed = on_keymap_binding_pressed,
+ .binding_released = on_keymap_binding_released,
+};
+
+DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0),
+ behavior_bt_init,
+ NULL,
+ NULL,
+ APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
+ &behavior_bt_driver_api);
diff --git a/app/src/ble.c b/app/src/ble.c
index c4d3efd..97ff461 100644
--- a/app/src/ble.c
+++ b/app/src/ble.c
@@ -8,6 +8,8 @@
#include <init.h>
#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
#include <settings/settings.h>
#include <bluetooth/bluetooth.h>
@@ -15,6 +17,13 @@
#include <bluetooth/hci.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
+#include <bluetooth/hci_err.h>
+
+#if IS_ENABLED(CONFIG_SETTINGS)
+
+#include <settings/settings.h>
+
+#endif
#include <logging/log.h>
@@ -28,16 +37,249 @@ static struct bt_conn *auth_passkey_entry_conn;
static u8_t passkey_entries[6] = {0, 0, 0, 0, 0, 0};
static u8_t passkey_digit = 0;
-#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
-#define ZMK_ADV_PARAMS BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
- BT_LE_ADV_OPT_USE_NAME | \
- BT_LE_ADV_OPT_ONE_TIME, \
+#define ZMK_BT_LE_ADV_PARAM_INIT(_id, _options, _int_min, _int_max, _peer) \
+{ \
+ .id = _id, \
+ .sid = 0, \
+ .secondary_max_skip = 0, \
+ .options = (_options), \
+ .interval_min = (_int_min), \
+ .interval_max = (_int_max), \
+ .peer = (_peer), \
+}
+
+#define ZMK_BT_LE_ADV_PARAM(_id, _options, _int_min, _int_max, _peer) \
+ ((struct bt_le_adv_param[]) { \
+ ZMK_BT_LE_ADV_PARAM_INIT(_id, _options, _int_min, _int_max, _peer) \
+ })
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+#define ZMK_ADV_PARAMS(_id) ZMK_BT_LE_ADV_PARAM(_id, \
+ BT_LE_ADV_OPT_CONNECTABLE | \
+ BT_LE_ADV_OPT_ONE_TIME | \
+ BT_LE_ADV_OPT_USE_NAME, \
BT_GAP_ADV_FAST_INT_MIN_2, \
BT_GAP_ADV_FAST_INT_MAX_2, NULL)
#else
-#define ZMK_ADV_PARAMS BT_LE_ADV_CONN_NAME
+#define ZMK_ADV_PARAMS(_id) ZMK_BT_LE_ADV_PARAM(_id, \
+ BT_LE_ADV_OPT_CONNECTABLE | \
+ BT_LE_ADV_OPT_USE_NAME, \
+ BT_GAP_ADV_FAST_INT_MIN_2, \
+ BT_GAP_ADV_FAST_INT_MAX_2, NULL)
#endif
+static const struct bt_data zmk_ble_ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_SOME,
+#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
+ 0x12, 0x18, /* HID Service */
+#endif
+ 0x0f, 0x18 /* Battery Service */
+ ),
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
+ BT_DATA_BYTES(BT_DATA_UUID128_ALL,
+ ZMK_SPLIT_BT_SERVICE_UUID)
+#endif
+};
+
+#define IDENTITY_COUNT CONFIG_BT_ID_MAX
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+
+static bt_addr_le_t peripheral_addr;
+
+#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
+
+
+static u8_t active_identity = 0;
+
+int zmk_ble_adv_pause()
+{
+ int err = bt_le_adv_stop();
+ if (err) {
+ LOG_ERR("Failed to stop advertising (err %d)", err);
+ return err;
+ }
+
+ return 0;
+};
+
+int zmk_ble_adv_resume()
+{
+ struct bt_le_adv_param *adv_params = ZMK_ADV_PARAMS(active_identity);
+
+ LOG_DBG("");
+ int err = bt_le_adv_start(adv_params, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
+ if (err)
+ {
+ LOG_ERR("Advertising failed to start (err %d)", err);
+ return err;
+ }
+
+ return 0;
+};
+
+static void disconnect_host_connection(struct bt_conn *conn, void *arg)
+{
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+
+ if (info.role != BT_CONN_ROLE_SLAVE) {
+ return;
+ }
+
+ bt_conn_disconnect(conn, BT_HCI_ERR_LOCALHOST_TERM_CONN);
+};
+
+static int activate_profile(u8_t index)
+{
+ int err;
+
+ if (index >= IDENTITY_COUNT) {
+ return -EINVAL;
+ }
+
+ if (active_identity != index) {
+ LOG_DBG("Persisting new active identity");
+ active_identity = index;
+
+#if IS_ENABLED(CONFIG_SETTINGS)
+ err = settings_save_one("ble/active_identity", &active_identity, sizeof(u8_t));
+ if (err) {
+ LOG_WRN("Failed to persist active_identity (err %d)", err);
+ }
+#endif
+
+#if IS_ENABLED(CONFIG_BT_DEVICE_NAME_DYNAMIC)
+ char name[CONFIG_BT_DEVICE_NAME_MAX];
+ snprintf(name, sizeof(name), "%s (Profile %d)", CONFIG_ZMK_KEYBOARD_NAME, active_identity + 1);
+ bt_set_name(name);
+#endif /* IS_ENABLED(CONFIG_BT_DEVICE_NAME_DYNAMIC) */
+ }
+
+ return zmk_ble_adv_resume();
+};
+
+static int deactivate_profile(u8_t index)
+{
+ int err = zmk_ble_adv_pause();
+ if (err) {
+ LOG_WRN("Failed to pause advertising %d", err);
+ }
+
+ bt_conn_foreach(BT_CONN_TYPE_ALL, disconnect_host_connection, NULL);
+
+ return 0;
+};
+
+int zmk_ble_identity_select(u8_t index)
+{
+ LOG_DBG("index %d", index);
+ if (index >= IDENTITY_COUNT) {
+ return -EINVAL;
+ }
+
+ int err = deactivate_profile(active_identity);
+ if (err) {
+ LOG_WRN("Failed to deactivate profile");
+ return err;
+ }
+
+ return activate_profile(index);
+};
+
+static void unpair_non_peripheral_bonds(const struct bt_bond_info *info, void *user_data) {
+ char addr[BT_ADDR_LE_STR_LEN];
+ bt_addr_le_to_str(&info->addr, addr, sizeof(addr));
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+ if (!bt_addr_le_cmp(&info->addr, &peripheral_addr)) {
+ LOG_DBG("Skipping unpairing peripheral %s", log_strdup(addr));
+ return;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
+
+ LOG_DBG("Unpairing %s", log_strdup(addr));
+ bt_unpair(active_identity, &info->addr);
+}
+
+int zmk_ble_identity_clear()
+{
+ LOG_DBG("");
+ int err = deactivate_profile(active_identity);
+ if (err) {
+ return err;
+ }
+
+ bt_foreach_bond(active_identity, unpair_non_peripheral_bonds, NULL);
+
+ return activate_profile(active_identity);
+};
+
+int zmk_ble_identity_next()
+{
+ LOG_DBG("active_identity %d IDENTITY_COUNT %d", active_identity, IDENTITY_COUNT);
+ return zmk_ble_identity_select((active_identity + 1) % IDENTITY_COUNT);
+}
+
+int zmk_ble_identity_prev()
+{
+ LOG_DBG("");
+ return zmk_ble_identity_select((active_identity + IDENTITY_COUNT - 1) % IDENTITY_COUNT);
+}
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+
+void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr)
+{
+ memcpy(&peripheral_addr, addr, sizeof(bt_addr_le_t));
+ settings_save_one("ble/peripheral_address", addr, sizeof(bt_addr_le_t));
+}
+
+#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) */
+
+#if IS_ENABLED(CONFIG_SETTINGS)
+
+static int ble_profiles_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
+{
+ const char *next;
+
+ LOG_DBG("Setting BLE value %s", log_strdup(name));
+
+ if (settings_name_steq(name, "active_identity", &next) && !next) {
+ if (len != sizeof(active_identity)) {
+ return -EINVAL;
+ }
+
+ int err = read_cb(cb_arg, &active_identity, sizeof(active_identity));
+ if (err <= 0) {
+ LOG_ERR("Failed to handle profile from settings (err %d)", err);
+ return err;
+ }
+ LOG_DBG("Loaded active identity %d", active_identity);
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+ } else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
+ if (len != sizeof(bt_addr_le_t)) {
+ return -EINVAL;
+ }
+
+ int err = read_cb(cb_arg, &peripheral_addr, sizeof(bt_addr_le_t));
+ if (err <= 0) {
+ LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
+ return err;
+ }
+#endif
+ }
+
+ return 0;
+};
+
+struct settings_handler profiles_handler = {
+ .name = "ble",
+ .h_set = ble_profiles_handle_set
+};
+#endif /* IS_ENABLED(CONFIG_SETTINGS) */
+
static void connected(struct bt_conn *conn, u8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
@@ -71,6 +313,12 @@ static void disconnected(struct bt_conn *conn, u8_t reason)
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason);
+
+#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
+ if (bt_addr_le_cmp(&peripheral_addr, BT_ADDR_LE_ANY) && bt_addr_le_cmp(&peripheral_addr, bt_conn_get_dst(conn))) {
+ zmk_ble_adv_resume();
+ }
+#endif
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
@@ -146,19 +394,6 @@ static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
.cancel = auth_cancel,
};
-static const struct bt_data zmk_ble_ad[] = {
- BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
- BT_DATA_BYTES(BT_DATA_UUID16_SOME,
-#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
- 0x12, 0x18, /* HID Service */
-#endif
- 0x0f, 0x18 /* Battery Service */
- ),
-#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
- BT_DATA_BYTES(BT_DATA_UUID128_ALL,
- ZMK_SPLIT_BT_SERVICE_UUID)
-#endif
-};
static void zmk_ble_ready(int err)
{
@@ -169,17 +404,42 @@ static void zmk_ble_ready(int err)
return;
}
- err = bt_le_adv_start(ZMK_ADV_PARAMS, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0);
- if (err)
- {
- LOG_ERR("Advertising failed to start (err %d)", err);
- return;
- }
+ zmk_ble_identity_select(active_identity);
}
+#if CONFIG_BT_ID_MAX != 1
+static int initialize_identities()
+{
+ bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
+ size_t count = CONFIG_BT_ID_MAX;
+
+ LOG_DBG("");
+ bt_id_get(addrs, &count);
+
+ for (int i = 0; i < count; i++) {
+ char addr[BT_ADDR_LE_STR_LEN];
+ bt_addr_le_to_str(&addrs[i], addr, sizeof(addr));
+ LOG_DBG("Existing identity %s", log_strdup(addr));
+ }
+
+ for (int i = count; i < CONFIG_BT_ID_MAX; i++) {
+ LOG_DBG("Initializing identity %d", i);
+ int id = bt_id_create(NULL, NULL);
+ if (id < 0) {
+ LOG_ERR("Failed to create new identity with id %d", i);
+ return id;
+ }
+ }
+
+ return 0;
+};
+#endif /* CONFIG_BT_ID_MAX != 1 */
+
static int zmk_ble_init(struct device *_arg)
{
- int err = bt_enable(NULL);
+ int err;
+
+ err = bt_enable(NULL);
if (err)
{
@@ -187,11 +447,23 @@ static int zmk_ble_init(struct device *_arg)
return err;
}
- if (IS_ENABLED(CONFIG_BT_SETTINGS))
- {
- settings_load();
+#if IS_ENABLED(CONFIG_SETTINGS)
+ settings_subsys_init();
+
+ err = settings_register(&profiles_handler);
+ if (err) {
+ LOG_ERR("Failed to setup the profile settings handler (err %d)", err);
+ return err;
}
+ settings_load();
+
+#endif
+
+#if CONFIG_BT_ID_MAX != 1
+ initialize_identities();
+#endif /* CONFIG_BT_ID_MAX != 1 */
+
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_cb_register(&zmk_ble_auth_cb_display);
@@ -203,7 +475,12 @@ static int zmk_ble_init(struct device *_arg)
int zmk_ble_unpair_all()
{
LOG_DBG("");
- return bt_unpair(BT_ID_DEFAULT, NULL);
+ int err = bt_unpair(BT_ID_DEFAULT, NULL);
+ if (err) {
+ LOG_ERR("Failed to unpair devices (err %d)", err);
+ }
+
+ return err;
};
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event)
diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c
index 9e67228..df8f34e 100644
--- a/app/src/split/bluetooth/central.c
+++ b/app/src/split/bluetooth/central.c
@@ -10,12 +10,14 @@
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
+#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+#include <zmk/ble.h>
#include <zmk/split/bluetooth/uuid.h>
#include <zmk/event-manager.h>
#include <zmk/events/position-state-changed.h>
@@ -71,6 +73,18 @@ static u8_t split_central_notify_func(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE;
}
+static int split_central_subscribe(struct bt_conn *conn)
+{
+ int err = bt_gatt_subscribe(conn, &subscribe_params);
+ if (err && err != -EALREADY) {
+ LOG_ERR("Subscribe failed (err %d)", err);
+ } else {
+ LOG_DBG("[SUBSCRIBED]");
+ }
+
+ return 0;
+}
+
static u8_t split_central_discovery_func(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *params)
@@ -112,12 +126,7 @@ static u8_t split_central_discovery_func(struct bt_conn *conn,
subscribe_params.value = BT_GATT_CCC_NOTIFY;
subscribe_params.ccc_handle = attr->handle;
- err = bt_gatt_subscribe(conn, &subscribe_params);
- if (err && err != -EALREADY) {
- LOG_ERR("Subscribe failed (err %d)", err);
- } else {
- LOG_DBG("[SUBSCRIBED]");
- }
+ split_central_subscribe(conn);
return BT_GATT_ITER_STOP;
}
@@ -137,16 +146,20 @@ static void split_central_process_connection(struct bt_conn *conn) {
}
if (conn == default_conn) {
- discover_params.uuid = &uuid.uuid;
- discover_params.func = split_central_discovery_func;
- discover_params.start_handle = 0x0001;
- discover_params.end_handle = 0xffff;
- discover_params.type = BT_GATT_DISCOVER_PRIMARY;
+ if (subscribe_params.value) {
+ split_central_subscribe(conn);
+ } else {
+ discover_params.uuid = &uuid.uuid;
+ discover_params.func = split_central_discovery_func;
+ discover_params.start_handle = 0x0001;
+ discover_params.end_handle = 0xffff;
+ discover_params.type = BT_GATT_DISCOVER_PRIMARY;
- err = bt_gatt_discover(default_conn, &discover_params);
- if (err) {
- LOG_ERR("Discover failed(err %d)", err);
- return;
+ err = bt_gatt_discover(default_conn, &discover_params);
+ if (err) {
+ LOG_ERR("Discover failed(err %d)", err);
+ return;
+ }
}
}
@@ -194,6 +207,8 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data)
LOG_DBG("Found the split service");
+ zmk_ble_set_peripheral_addr(addr);
+
err = bt_le_scan_stop();
if (err) {
LOG_ERR("Stop LE scan failed (err %d)", err);
@@ -206,10 +221,11 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data)
split_central_process_connection(default_conn);
} else {
param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
+
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
param, &default_conn);
if (err) {
- LOG_ERR("Create conn failed (err %d)", err);
+ LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err, BT_HCI_OP_LE_CREATE_CONN);
start_scan();
}
@@ -263,6 +279,7 @@ static void split_central_connected(struct bt_conn *conn, u8_t conn_err)
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
+
if (conn_err) {
LOG_ERR("Failed to connect to %s (%u)", addr, conn_err);
diff --git a/docs/docs/behavior/bluetooth.md b/docs/docs/behavior/bluetooth.md
new file mode 100644
index 0000000..df8ec26
--- /dev/null
+++ b/docs/docs/behavior/bluetooth.md
@@ -0,0 +1,82 @@
+---
+title: Bluetooth Behavior
+sidebar_label: Bluetooth
+---
+
+## Summary
+
+The bluetooth behavior allows management of various settings and states related to the bluetooth connection(s)
+between the keyboard and the host.
+
+## Bluetooth Command Defines
+
+Bluetooth command defines are provided through the [`dt-bindings/zmk/bt.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/bt.h) header,
+which is added at the top of the keymap file:
+
+```
+#include <dt-bindings/zmk/bt.h>
+```
+
+This will allow you to reference the actions defined in this header such as `BT_IDENT_CLR_CMD`.
+
+Here is a table describing the command for each define:
+
+| Define | Action |
+| ------------------- | ----------------------------------------------------- |
+| `BT_IDENT_CLR_CMD` | Clear paired keyboards (for the current identity)[^1] |
+| `BT_IDENT_NEXT_CMD` | Switch to the next identity[^1] |
+| `BT_IDENT_PREV_CMD` | Switch to the previous identity |
+| `BT_IDENT_SEL_CMD` | Switch to a specific numbered identity |
+| `BT_RST_CMD` | Hard reset of all bluetooth bonds |
+
+Because the `BT_IDENT_SEL_CMD` command takes an additional parameter, the numeric index of the identity to select, _all_ the commands for the bluetooth behavior must take that additional parameter, and ignore the value. To make this easier,
+there are alias defines that add those default parameters for you:
+
+| Define | Action |
+| --------------- | --------------------------------------------------------------------------------------- |
+| `BT_IDENT_CLR` | Alias for `BT_IDENT_CLR_CMD 0` to clear paired keyboards (for the current identity)[^1] |
+| `BT_IDENT_NEXT` | Alias for `BT_IDENT_NEXT_CMD 0` to switch to the next identity[^1] |
+| `BT_IDENT_PREV` | Alias for `BT_IDENT_PREV_CMD 0` to switch to the previous identity |
+| `BT_IDENT_SEL` | Alias for `BT_IDENT_SEL_CMD` to switch to a specific numbered identity |
+| `BT_RST` | Alias for `BT_RST_CMD 0` to reset all bonds[^2] |
+
+[^1]: Multiple keyboard identities/profiles is only support on non-split keyboards as of this time.
+[^2]: This may interrupt pairing between both halves of a split keyboard. For split keyboards, it is preferred to use the [/docs/bond-reset] combo to clear bonds on both devices
+
+## Bluetooth Behavior
+
+The bluetooth behavior completes an bluetooth action given on press.
+
+### Behavior Binding
+
+- Reference: `&bt`
+- Parameter #1: The bluetooth command define, e.g. `BT_IDENT_CLR_CMD` or `BT_RST_CMD`
+- Parameter #2: The parameter for the command, when used to identify an identity/profile to select, e.g. `0`
+
+### Examples
+
+1. Behavior to clear the paired host:
+
+ ```
+ &bt BT_IDENT_CLR
+ ```
+
+1. Behavior to hard reset all bonded devices[^2]:
+
+ ```
+ &bt BT_IDENT_CLR
+ ```
+
+Examples for non-split keyboards with multiple identities:
+
+1. Behavior to switch to the next identity:
+
+ ```
+ &bt BT_IDENT_NEXT
+ ```
+
+1. Behavior to switch to the specific numbered identity:
+
+ ```
+ &bt BT_IDENT_SEL 1
+ ```
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 6bd3aa4..ace7fa1 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -22,6 +22,7 @@ module.exports = {
"behavior/hold-tap",
"behavior/mod-tap",
"behavior/reset",
+ "behavior/bluetooth",
"behavior/lighting",
],
Development: [