From 204d1300ba6b13041e9a69cc297c06ac189f1f0d Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Thu, 15 Oct 2020 00:00:37 -0400 Subject: [PATCH 1/4] fix(ble): Only advertise when needed.* Once we have a peer connected to for the active profile, don't continue advertising. --- app/src/ble.c | 135 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 24 deletions(-) diff --git a/app/src/ble.c b/app/src/ble.c index 49e2b3b0..8cc42bcd 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -45,6 +45,18 @@ static u8_t passkey_digit = 0; #define PROFILE_COUNT CONFIG_BT_MAX_PAIRED #endif +enum advertising_type { + ZMK_ADV_NONE, + ZMK_ADV_DIR, + ZMK_ADV_CONN, +} advertising_status; + +#define CURR_ADV(adv) (adv << 4) + +#define ZMK_ADV_CONN_NAME \ + BT_LE_ADV_PARAM(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) + static struct zmk_ble_profile profiles[PROFILE_COUNT]; static u8_t active_profile; @@ -92,29 +104,97 @@ void set_profile_address(u8_t index, const bt_addr_le_t *addr) { 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; +bool active_profile_is_connected() { + struct bt_conn *conn; + bt_addr_le_t *addr = zmk_ble_active_profile_addr(); + if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + return false; + } else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) { + return false; } - return 0; -}; + bt_conn_unref(conn); -int zmk_ble_adv_resume() { - LOG_DBG("active_profile %d, directed? %s", active_profile, - active_profile_is_open() ? "no" : "yes"); + return true; +} - 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; +#define CHECKED_ADV_STOP() \ + err = bt_le_adv_stop(); \ + advertising_status = ZMK_ADV_NONE; \ + if (err) { \ + LOG_ERR("Failed to stop advertising (err %d)", err); \ + return err; \ + } + +#define CHECKED_DIR_ADV() \ + addr = zmk_ble_active_profile_addr(); \ + conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr); \ + if (conn != NULL) { /* TODO: Check status of connection */ \ + LOG_DBG("Skipping advertising, profile host is already connected"); \ + bt_conn_unref(conn); \ + return 0; \ + } \ + err = bt_le_adv_start(BT_LE_ADV_CONN_DIR_LOW_DUTY(addr), zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), \ + NULL, 0); \ + if (err) { \ + LOG_ERR("Advertising failed to start (err %d)", err); \ + return err; \ + } \ + advertising_status = ZMK_ADV_DIR; + +#define CHECKED_OPEN_ADV() \ + err = bt_le_adv_start(ZMK_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; \ + } \ + advertising_status = ZMK_ADV_CONN; + +int update_advertising() { + int err = 0; + bt_addr_le_t *addr; + struct bt_conn *conn; + enum advertising_type desired_adv = ZMK_ADV_NONE; + + if (active_profile_is_open() || !active_profile_is_connected()) { + desired_adv = ZMK_ADV_CONN; + } else if (!active_profile_is_connected()) { + desired_adv = ZMK_ADV_CONN; + // Need to fix directed advertising for privacy centrals. See + // https://github.com/zephyrproject-rtos/zephyr/pull/14984 char + // addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(zmk_ble_active_profile_addr(), addr_str, + // sizeof(addr_str)); + + // LOG_DBG("Directed advertising to %s", log_strdup(addr_str)); + // desired_adv = ZMK_ADV_DIR; + } + LOG_DBG("advertising from %d to %d", advertising_status, desired_adv); + + switch (desired_adv + CURR_ADV(advertising_status)) { + case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_DIR): + case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_CONN): + CHECKED_ADV_STOP(); + CHECKED_DIR_ADV(); + break; + case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_NONE): + CHECKED_DIR_ADV(); + break; + case ZMK_ADV_CONN + CURR_ADV(ZMK_ADV_DIR): + CHECKED_ADV_STOP(); + CHECKED_OPEN_ADV(); + break; + case ZMK_ADV_CONN + CURR_ADV(ZMK_ADV_NONE): + CHECKED_OPEN_ADV(); + break; } return 0; }; +static void update_advertising_callback(struct k_work *work) { update_advertising(); } + +K_WORK_DEFINE(update_advertising_work, update_advertising_callback); + int zmk_ble_clear_bonds() { LOG_DBG(""); @@ -124,6 +204,8 @@ int zmk_ble_clear_bonds() { set_profile_address(active_profile, BT_ADDR_LE_ANY); } + update_advertising(); + return 0; }; @@ -134,9 +216,13 @@ int zmk_ble_prof_select(u8_t index) { } active_profile = index; - return settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile)); + settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile)); + + update_advertising(); raise_profile_changed_event(); + + return 0; }; int zmk_ble_prof_next() { @@ -234,8 +320,11 @@ 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)); + advertising_status = ZMK_ADV_NONE; + if (err) { LOG_WRN("Failed to connect to %s (%u)", log_strdup(addr), err); + update_advertising(); return; } @@ -250,6 +339,8 @@ static void connected(struct bt_conn *conn, u8_t err) { if (bt_conn_set_security(conn, BT_SECURITY_L2)) { LOG_ERR("Failed to set security"); } + + update_advertising(); } static void disconnected(struct bt_conn *conn, u8_t reason) { @@ -259,14 +350,9 @@ static void disconnected(struct bt_conn *conn, u8_t reason) { 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 + // We need to do this in a work callback, otherwise the advertising update will still see the + // connection for a profile as active, and not start advertising yet. + k_work_submit(&update_advertising_work); } static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { @@ -361,6 +447,7 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) { #endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */ set_profile_address(active_profile, dst); + update_advertising(); }; static struct bt_conn_auth_cb zmk_ble_auth_cb_display = { @@ -383,7 +470,7 @@ static void zmk_ble_ready(int err) { return; } - zmk_ble_adv_resume(); + update_advertising(); } static int zmk_ble_init(struct device *_arg) { From 818f0a1f91020f3315ae09f70ddda6f8362fab98 Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Fri, 16 Oct 2020 00:48:53 -0400 Subject: [PATCH 2/4] fix(bluetooth): Advertise name + appearance. * Properly put device name and GAP appearance in advertising packets, for proper display in macOS, Android, etc. * Closes #124 --- app/src/ble.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/ble.c b/app/src/ble.c index 8cc42bcd..ddc92e99 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -54,13 +54,18 @@ enum advertising_type { #define CURR_ADV(adv) (adv << 4) #define ZMK_ADV_CONN_NAME \ - BT_LE_ADV_PARAM(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) + BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME, BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2, NULL) static struct zmk_ble_profile profiles[PROFILE_COUNT]; static u8_t active_profile; +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) + static const struct bt_data zmk_ble_ad[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), + BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0xC1, 0x03), 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) From b07475b7d475a592ad974bb36a5580b1c863344f Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Fri, 16 Oct 2020 00:56:05 -0400 Subject: [PATCH 3/4] fix(bluetooth): Stop adv on connected profile. --- app/src/ble.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/ble.c b/app/src/ble.c index ddc92e99..1b25ca2a 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -176,6 +176,10 @@ int update_advertising() { LOG_DBG("advertising from %d to %d", advertising_status, desired_adv); switch (desired_adv + CURR_ADV(advertising_status)) { + case ZMK_ADV_NONE + CURR_ADV(ZMK_ADV_DIR): + case ZMK_ADV_NONE + CURR_ADV(ZMK_ADV_CONN): + CHECKED_ADV_STOP(); + break; case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_DIR): case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_CONN): CHECKED_ADV_STOP(); From 9d512eaef01d92b930054d6528279944afe221ce Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Fri, 16 Oct 2020 09:49:28 -0400 Subject: [PATCH 4/4] fix(bluetooth): Add adv data in non-peripherals. --- app/src/ble.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/ble.c b/app/src/ble.c index 1b25ca2a..9090582c 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -64,8 +64,10 @@ static u8_t active_profile; #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) static const struct bt_data zmk_ble_ad[] = { +#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0xC1, 0x03), +#endif 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)