Merge pull request #133 from petejohanson/bluetooth/ident-management

feat(bluetooth): Proper basic bond management, new `bt` behavior for resetting bond to host.
This commit is contained in:
Pete Johanson 2020-09-13 22:43:45 -04:00 committed by GitHub
commit 160f296bfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 727 additions and 72 deletions

View File

@ -17,6 +17,7 @@ jobs:
- lily58_left
- lily58_right
- romac
- settings_reset
include:
- board: proton_c
shield: clueboard_california

View File

@ -29,22 +29,26 @@ target_sources(app PRIVATE src/hid.c)
target_sources(app PRIVATE src/sensors.c)
target_sources_ifdef(CONFIG_ZMK_DISPLAY app PRIVATE src/display.c)
target_sources(app PRIVATE src/event_manager.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble_unpair_combo.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/keycode_state_changed.c)
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
target_sources(app PRIVATE src/keymap.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_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)
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)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split/bluetooth/service.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL app PRIVATE src/split/bluetooth/central.c)
@ -57,4 +61,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)
zephyr_cc_option(-Wfatal-errors)

View File

@ -37,21 +37,26 @@ menuconfig ZMK_BLE
select BT
select BT_SMP
select BT_SMP_SC_PAIR_ONLY
select BT_SMP_APP_PAIRING_ACCEPT
select BT_PERIPHERAL
select BT_GATT_DIS
select BT_GATT_BAS
select BT_SETTINGS
select SETTINGS
# select BT_SETTINGS
if ZMK_BLE
config ZMK_BLE_INIT_PRIORITY
int "Init Priority"
default 50
config SYSTEM_WORKQUEUE_STACK_SIZE
default 2048
config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup."
default n
# HID GATT notifications sent this way are *not* picked up by Linux, and possibly others.
config BT_GATT_NOTIFY_MULTIPLE
default n
@ -101,28 +106,20 @@ config ZMK_SPLIT_BLE_ROLE_CENTRAL
select BT_CENTRAL
select BT_GATT_CLIENT
if ZMK_SPLIT_BLE_ROLE_CENTRAL
config BT_MAX_CONN
default 5
config BT_MAX_PAIRED
# Bump this everywhere once we support switching active connections!
default 2
endif
config ZMK_SPLIT_BLE_ROLE_PERIPHERAL
bool "Peripheral"
select BT_KEYS_OVERWRITE_OLDEST
if ZMK_SPLIT_BLE_ROLE_PERIPHERAL
config ZMK_USB
default n
config BT_MAX_PAIRED
default 1
config BT_MAX_CONN
default 5
default 1
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
default n
@ -135,8 +132,17 @@ endif
endif
endmenu
if ZMK_BLE && (!ZMK_SPLIT_BLE || ZMK_SPLIT_BLE_ROLE_CENTRAL)
config BT_MAX_CONN
default 6
config BT_MAX_PAIRED
default 5
endif
endmenu
config ZMK_KSCAN_MOCK_DRIVER
bool "Enable mock kscan driver to simulate key presses"

View File

@ -0,0 +1,10 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_SETTINGS_RESET
config ZMK_KEYBOARD_NAME
default "SETTINGS RESET"
endif

View File

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_SETTINGS_RESET
def_bool $(shields_list_contains,settings_reset)

View File

@ -0,0 +1 @@
CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START=y

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&reset
>;
};
};
};

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-direct";
label = "KSCAN";
input-gpios
= <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
;
};
};

View File

@ -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>
#include <behaviors/rgb_underglow.dtsi>
#include <behaviors/bluetooth.dtsi>

View File

@ -0,0 +1,9 @@
/ {
behaviors {
bt: behavior_bluetooth {
compatible = "zmk,behavior-bluetooth";
label = "BLUETOOTH";
#binding-cells = <2>;
};
};
};

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020, Peter Johanson
# SPDX-License-Identifier: MIT
description: Bluetooth Behavior
compatible: "zmk,behavior-bluetooth"
include: two_param.yaml

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#define BT_CLR_CMD 0
#define BT_NXT_CMD 1
#define BT_PRV_CMD 2
#define BT_SEL_CMD 3
// #define BT_FULL_RESET_CMD 4
/*
Note: Some future commands will include additional parameters, so we
defines these aliases up front.
*/
#define BT_CLR BT_CLR_CMD 0
#define BT_NXT BT_NXT_CMD 0
#define BT_PRV BT_PRV_CMD 0
#define BT_SEL BT_SEL_CMD

View File

@ -7,6 +7,19 @@
#pragma once
#include <zmk/keys.h>
#include <zmk/ble/profile.h>
int zmk_ble_clear_bonds();
int zmk_ble_prof_next();
int zmk_ble_prof_prev();
int zmk_ble_prof_select(u8_t index);
bt_addr_le_t *zmk_ble_active_profile_addr();
char *zmk_ble_active_profile_name();
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) */

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <bluetooth/addr.h>
#define ZMK_BLE_PROFILE_NAME_MAX 15
struct zmk_ble_profile {
char name[ZMK_BLE_PROFILE_NAME_MAX];
bt_addr_le_t peer;
};

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr.h>
#include <zmk/event-manager.h>
#include <device.h>
#include <zmk/ble/profile.h>
struct ble_active_profile_changed {
struct zmk_event_header header;
u8_t index;
struct zmk_ble_profile *profile;
};
ZMK_EVENT_DECLARE(ble_active_profile_changed);

View File

@ -0,0 +1,60 @@
/*
* 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_CLR_CMD:
return zmk_ble_clear_bonds();
case BT_NXT_CMD:
return zmk_ble_prof_next();
case BT_PRV_CMD:
return zmk_ble_prof_prev();
case BT_SEL_CMD:
return zmk_ble_prof_select(arg);
default:
LOG_ERR("Unknown BT command: %d", command);
}
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);

View File

@ -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,33 +17,245 @@
#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>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/ble.h>
#include <zmk/keys.h>
#include <zmk/split/bluetooth/uuid.h>
#include <zmk/event-manager.h>
#include <zmk/events/ble-active-profile-changed.h>
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, \
BT_GAP_ADV_FAST_INT_MIN_2, \
BT_GAP_ADV_FAST_INT_MAX_2, NULL)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
#else
#define ZMK_ADV_PARAMS BT_LE_ADV_CONN_NAME
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
#endif
static struct zmk_ble_profile profiles[PROFILE_COUNT];
static u8_t active_profile;
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
};
#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 void raise_profile_changed_event()
{
struct ble_active_profile_changed *ev = new_ble_active_profile_changed();
ev->index = active_profile;
ev->profile = &profiles[active_profile];
ZMK_EVENT_RAISE(ev);
}
static bool active_profile_is_open()
{
return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY);
}
void set_profile_address(u8_t index, const bt_addr_le_t *addr)
{
char setting_name[15];
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
memcpy(&profiles[index].peer, addr, sizeof(bt_addr_le_t));
sprintf(setting_name, "ble/profiles/%d", index);
LOG_DBG("Setting profile addr for %s to %s", log_strdup(setting_name), log_strdup(addr_str));
settings_save_one(setting_name, &profiles[index], sizeof(struct zmk_ble_profile));
raise_profile_changed_event();
}
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()
{
LOG_DBG("active_profile %d, directed? %s", active_profile, active_profile_is_open() ? "no" : "yes");
int err = bt_le_adv_start(
BT_LE_ADV_CONN_NAME,
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;
};
int zmk_ble_clear_bonds()
{
LOG_DBG("");
if (bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY)) {
LOG_DBG("Unpairing!");
bt_unpair(BT_ID_DEFAULT, &profiles[active_profile].peer);
set_profile_address(active_profile, BT_ADDR_LE_ANY);
}
return 0;
};
int zmk_ble_prof_select(u8_t index)
{
LOG_DBG("profile %d", index);
if (active_profile == index) {
return 0;
}
active_profile = index;
return settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile));
raise_profile_changed_event();
};
int zmk_ble_prof_next()
{
LOG_DBG("");
return zmk_ble_prof_select((active_profile + 1) % PROFILE_COUNT);
};
int zmk_ble_prof_prev()
{
LOG_DBG("");
return zmk_ble_prof_select((active_profile + PROFILE_COUNT - 1) % PROFILE_COUNT);
};
bt_addr_le_t *zmk_ble_active_profile_addr()
{
return &profiles[active_profile].peer;
}
char *zmk_ble_active_profile_name()
{
return profiles[active_profile].name;
}
#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, "profiles", &next) && next) {
char *endptr;
u8_t idx = strtoul(next, &endptr, 10);
if (*endptr != '\0') {
LOG_WRN("Invalid profile index: %s", log_strdup(next));
return -EINVAL;
}
if (len != sizeof(struct zmk_ble_profile)) {
LOG_ERR("Invalid profile size (got %d expected %d)", len, sizeof(struct zmk_ble_profile));
return -EINVAL;
}
if (idx >= PROFILE_COUNT) {
LOG_WRN("Profile address for index %d is larger than max of %d", idx, PROFILE_COUNT);
return -EINVAL;
}
int err = read_cb(cb_arg, &profiles[idx], sizeof(struct zmk_ble_profile));
if (err <= 0) {
LOG_ERR("Failed to handle profile address from settings (err %d)", err);
return err;
}
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&profiles[idx].peer, addr_str, sizeof(addr_str));
LOG_DBG("Loaded %s address for profile %d", log_strdup(addr_str), idx);
} else if (settings_name_steq(name, "active_profile", &next) && !next) {
if (len != sizeof(active_profile)) {
return -EINVAL;
}
int err = read_cb(cb_arg, &active_profile, sizeof(active_profile));
if (err <= 0) {
LOG_ERR("Failed to handle active profile from settings (err %d)", err);
return err;
}
}
#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];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err)
@ -71,6 +285,14 @@ 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();
// }
#else
// zmk_ble_adv_resume();
#endif
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
@ -137,7 +359,52 @@ static void auth_cancel(struct bt_conn *conn)
LOG_DBG("Pairing cancelled: %s", log_strdup(addr));
}
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
static enum bt_security_err auth_pairing_accept(struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat)
{
struct bt_conn_info info;
bt_conn_get_info(conn, &info);
LOG_DBG("role %d, open? %s", info.role, active_profile_is_open() ? "yes" : "no");
if (info.role == BT_CONN_ROLE_SLAVE && !active_profile_is_open()) {
LOG_WRN("Rejecting pairing request to taken profile %d", active_profile);
return BT_SECURITY_ERR_PAIR_NOT_ALLOWED;
}
return BT_SECURITY_ERR_SUCCESS;
};
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
static void auth_pairing_complete(struct bt_conn *conn, bool bonded)
{
struct bt_conn_info info;
char addr[BT_ADDR_LE_STR_LEN];
const bt_addr_le_t *dst = bt_conn_get_dst(conn);
bt_addr_le_to_str(dst, addr, sizeof(addr));
bt_conn_get_info(conn, &info);
if (info.role != BT_CONN_ROLE_SLAVE) {
LOG_DBG("SKIPPING FOR ROLE %d", info.role);
return;
}
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
if (!active_profile_is_open()) {
LOG_ERR("Pairing completed but current profile is not open: %s", log_strdup(addr));
bt_unpair(BT_ID_DEFAULT, dst);
return;
}
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
set_profile_address(active_profile, dst);
};
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
.pairing_accept = auth_pairing_accept,
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
.pairing_complete = auth_pairing_complete,
// .passkey_display = auth_passkey_display,
#ifdef CONFIG_ZMK_BLE_PASSKEY_ENTRY
@ -146,19 +413,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,12 +423,7 @@ 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_adv_resume();
}
static int zmk_ble_init(struct device *_arg)
@ -187,11 +436,37 @@ 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 IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START)
LOG_WRN("Clearing all existing BLE bond information from the keyboard");
for (int i = 0; i < 10; i++) {
bt_unpair(i, NULL);
}
for (int i = 0; i < PROFILE_COUNT; i++) {
char setting_name[15];
sprintf(setting_name, "ble/profiles/%d", i);
err = settings_delete(setting_name);
if (err) {
LOG_ERR("Failed to delete setting: %d", err);
}
}
#endif
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_cb_register(&zmk_ble_auth_cb_display);
@ -202,8 +477,17 @@ static int zmk_ble_init(struct device *_arg)
int zmk_ble_unpair_all()
{
LOG_DBG("");
return bt_unpair(BT_ID_DEFAULT, NULL);
int resp = 0;
for (int i = BT_ID_DEFAULT; i < CONFIG_BT_ID_MAX; i++) {
int err = bt_unpair(BT_ID_DEFAULT, NULL);
if (err) {
resp = err;
LOG_ERR("Failed to unpair devices (err %d)", err);
}
}
return resp;
};
bool zmk_ble_handle_key_user(struct zmk_key_event *key_event)

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com>
*
* SPDX-License-Identifier: MIT
*/
#include <kernel.h>
#include <zmk/events/ble-active-profile-changed.h>
ZMK_EVENT_IMPL(ble_active_profile_changed);

View File

@ -6,6 +6,10 @@
#include <settings/settings.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <bluetooth/bluetooth.h>
#include <bluetooth/gatt.h>
@ -148,12 +152,40 @@ BT_GATT_SERVICE_DEFINE(hog_svc,
BT_GATT_PERM_WRITE,
NULL, write_ctrl_point, &ctrl_point));
struct bt_conn *destination_connection() {
struct bt_conn *conn;
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
LOG_DBG("Address pointer %p", addr);
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
LOG_WRN("Not sending, no active address for current profile");
return NULL;
} else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) {
LOG_WRN("Not sending, not connected to active profile");
return NULL;
}
return conn;
}
int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report)
{
return bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return -ENOTCONN;
}
LOG_DBG("Sending to NULL? %s", conn == NULL ? "yes" : "no");
return bt_gatt_notify(conn, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
};
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report)
{
return bt_gatt_notify(NULL, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body));
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return -ENOTCONN;
}
return bt_gatt_notify(conn, &hog_svc.attrs[10], report, sizeof(struct zmk_hid_consumer_report_body));
};

View File

@ -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,27 @@ 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);
switch (err) {
case -EALREADY:
LOG_DBG("[ALREADY SUBSCRIBED]");
break;
// break;
// bt_gatt_unsubscribe(conn, &subscribe_params);
// return split_central_subscribe(conn);
case 0:
LOG_DBG("[SUBSCRIBED]");
break;
default:
LOG_ERR("Subscribe failed (err %d)", err);
break;
}
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 +135,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;
}
@ -136,7 +154,7 @@ static void split_central_process_connection(struct bt_conn *conn) {
return;
}
if (conn == default_conn) {
if (conn == default_conn && !subscribe_params.value) {
discover_params.uuid = &uuid.uuid;
discover_params.func = split_central_discovery_func;
discover_params.start_handle = 0x0001;
@ -194,6 +212,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 +226,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,8 +284,9 @@ 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);
LOG_ERR("Failed to connect to %s (%u)", log_strdup(addr), conn_err);
bt_conn_unref(default_conn);
default_conn = NULL;

View File

@ -6,6 +6,11 @@
#include <zephyr/types.h>
#include <sys/util.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <bluetooth/gatt.h>
#include <bluetooth/uuid.h>
@ -28,6 +33,7 @@ static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_
static void split_svc_pos_state_ccc(const struct bt_gatt_attr *attr, u16_t value)
{
LOG_DBG("value %d", value);
}

View File

@ -21,12 +21,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
int split_listener(const struct zmk_event_header *eh)
{
LOG_DBG("");
if (is_position_state_changed(eh)) {
const struct position_state_changed *ev = cast_position_state_changed(eh);
if (ev->state) {
zmk_split_bt_position_pressed(ev->position);
return zmk_split_bt_position_pressed(ev->position);
} else {
zmk_split_bt_position_released(ev->position);
return zmk_split_bt_position_released(ev->position);
}
}
return 0;

View File

@ -0,0 +1,76 @@
---
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. By default, ZMK supports five "profiles" for selecting which bonded host
computer/laptop/keyboard should receive the keyboard input; many of the commands here operation on those profiles.
## 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_CLR_CMD`.
Here is a table describing the command for each define:
| Define | Action |
| ------------ | ---------------------------------------------------------------------------------------------- |
| `BT_CLR_CMD` | Clear bond information between the keyboard and host for the selected profile [^1] |
| `BT_NXT_CMD` | Switch to the next profile, cycling through to the first one when the end is reached. |
| `BT_PRV_CMD` | Switch to the previous profile, cycling through to the last one when the beginning is reached. |
| `BT_SEL_CMD` | Select the 0-indexed profile by number. |
Because at least one bluetooth commands takes an additional parameter, it is recommended to use
the following aliases in your keymap to avoid having to specify an ignored second parameter:
| Define | Action |
| -------- | -------------------------------------------------------------------------------- |
| `BT_CLR` | Alias for `BT_CLR_CMD 0` to clear the current profile's bond to the current host |
| `BT_NXT` | Alias for `BT_NXT_CMD 0` to select the next profile |
| `BT_PRV` | Alias for `BT_PRV_CMD 0` to select the previous profile |
| `BT_SEL` | Alias for `BT_SEL_CMD` to select the given profile, e.g. `&bt BT_SEL 1` |
## 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_CLR_CMD`
- Parameter #2: (Reserved for future bluetooth command types)
### Examples
1. Behavior binding to clear the paired host for the selected profile:
```
&bt BT_CLR
```
1. Behavior binding to select the next profile:
```
&bt BT_NXT
```
1. Behavior binding to select the previous profile:
```
&bt BT_NXT
```
1. Behavior binding to select the 2nd profile (passed parameters are [zero based](https://en.wikipedia.org/wiki/Zero-based_numbering)):
```
&bt BT_SEL 1
```

View File

@ -22,6 +22,7 @@ module.exports = {
"behavior/hold-tap",
"behavior/mod-tap",
"behavior/reset",
"behavior/bluetooth",
"behavior/lighting",
],
Development: [