diff --git a/include/bluetooth/bluetooth.h b/include/bluetooth/bluetooth.h index 8028ff6bcb0..10e93676ad4 100644 --- a/include/bluetooth/bluetooth.h +++ b/include/bluetooth/bluetooth.h @@ -321,6 +321,8 @@ enum { * connection happens. If this option is not set the stack will * take care of keeping advertising enabled even as connections * occur. + * If Advertising directed and connectable then this behaviour is + * the default behaviour and this flag has no effect. */ BT_LE_ADV_OPT_ONE_TIME = BIT(1), @@ -337,8 +339,7 @@ enum { BT_LE_ADV_OPT_USE_NAME = BIT(3), /** Use low duty directed advertising mode, otherwise high duty mode - * will be used. This option is only effective when used with - * bt_conn_create_slave_le(). + * will be used. */ BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY = BIT(4), @@ -450,13 +451,27 @@ struct bt_le_adv_param { u8_t secondary_max_skip; /** Bit-field of advertising options */ - u32_t options; + u32_t options; /** Minimum Advertising Interval (N * 0.625) */ u32_t interval_min; /** Maximum Advertising Interval (N * 0.625) */ u32_t interval_max; + + /** Directed advertising to peer + * + * When this parameter is set the advertiser will send directed + * advertising to the remote device. + * + * The advertising type will either be high duty cycle, or low duty + * cycle if the BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY option is enabled. + * + * In case of connectable high duty cycle if the connection could not + * be established within the timeout the connected() callback will be + * called with the status set to @ref BT_HCI_ERR_ADV_TIMEOUT. + */ + const bt_addr_le_t *peer; }; /** Helper to declare advertising parameters inline @@ -464,8 +479,10 @@ struct bt_le_adv_param { * @param _options Advertising Options * @param _int_min Minimum advertising interval * @param _int_max Maximum advertising interval + * @param _peer Peer address, set to NULL for undirected advertising or + * address of peer for directed advertising. */ -#define BT_LE_ADV_PARAM(_options, _int_min, _int_max) \ +#define BT_LE_ADV_PARAM(_options, _int_min, _int_max, _peer) \ ((struct bt_le_adv_param[]) { { \ .id = BT_ID_DEFAULT, \ .sid = 0, \ @@ -473,37 +490,46 @@ struct bt_le_adv_param { .options = (_options), \ .interval_min = (_int_min), \ .interval_max = (_int_max), \ + .peer = (_peer), \ } }) +#define BT_LE_ADV_CONN_DIR(_peer) BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ + BT_LE_ADV_OPT_ONE_TIME, 0, 0,\ + _peer) + + #define BT_LE_ADV_CONN BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE, \ BT_GAP_ADV_FAST_INT_MIN_2, \ - BT_GAP_ADV_FAST_INT_MAX_2) + BT_GAP_ADV_FAST_INT_MAX_2, NULL) #define BT_LE_ADV_CONN_NAME BT_LE_ADV_PARAM(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) + BT_GAP_ADV_FAST_INT_MAX_2, NULL) -#define BT_LE_ADV_CONN_DIR_LOW_DUTY \ +#define BT_LE_ADV_CONN_DIR_LOW_DUTY(_peer) \ BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | \ BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY, \ - BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2) - -#define BT_LE_ADV_CONN_DIR BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ - BT_LE_ADV_OPT_ONE_TIME, 0, 0) + BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, \ + _peer) #define BT_LE_ADV_NCONN BT_LE_ADV_PARAM(0, BT_GAP_ADV_FAST_INT_MIN_2, \ - BT_GAP_ADV_FAST_INT_MAX_2) + BT_GAP_ADV_FAST_INT_MAX_2, NULL) #define BT_LE_ADV_NCONN_NAME BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_NAME, \ BT_GAP_ADV_FAST_INT_MIN_2, \ - BT_GAP_ADV_FAST_INT_MAX_2) + BT_GAP_ADV_FAST_INT_MAX_2, NULL) /** @brief Start advertising * * Set advertisement data, scan response data, advertisement parameters * and start advertising. * + * When the advertisement parameter peer address has been set the advertising + * will be directed to the peer. In this case advertisement data and scan + * response data parameters are ignored. If the mode is high duty cycle + * the timeout will be @ref BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT. + * * @param param Advertising parameters. * @param ad Data to be used in advertisement packets. * @param ad_len Number of elements in ad @@ -569,6 +595,10 @@ struct bt_le_ext_adv_start_param { * Application will be notified by the advertiser sent callback. * Set to zero for no timeout. * + * When using high duty cycle directed connectable advertising then + * this parameters must be set to a non-zero value less than or equal + * to the maximum of @ref BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT. + * * If privacy :option:`CONFIG_BT_PRIVACY` is enabled then the timeout * must be less than :option:`CONFIG_BT_RPA_TIMEOUT`. */ diff --git a/include/bluetooth/conn.h b/include/bluetooth/conn.h index 92b036da01a..d599f4aca7a 100644 --- a/include/bluetooth/conn.h +++ b/include/bluetooth/conn.h @@ -471,27 +471,26 @@ int bt_le_set_auto_conn(const bt_addr_le_t *addr, * The caller gets a new reference to the connection object which must be * released with bt_conn_unref() once done using the object. * - * @param[in] peer Remote address. - * @param[in] param Directed advertising parameters. - * @param[out] conn Valid connection object on success. + * @param peer Remote address. + * @param param Directed advertising parameters. * - * @return Zero on success or (negative) error code on failure. + * @return Valid connection object on success or NULL otherwise. */ -int bt_conn_le_create_slave(const bt_addr_le_t *peer, - const struct bt_le_adv_param *param, - struct bt_conn **conn); - __deprecated static inline struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer, const struct bt_le_adv_param *param) { - struct bt_conn *conn; + struct bt_le_adv_param adv_param = *param; - if (bt_conn_le_create_slave(peer, param, &conn)) { + adv_param.options |= (BT_LE_ADV_OPT_CONNECTABLE | + BT_LE_ADV_OPT_ONE_TIME); + adv_param.peer = peer; + + if (!bt_le_adv_start(&adv_param, NULL, 0, NULL, 0)) { return NULL; } - return conn; + return bt_conn_lookup_addr_le(param->id, peer); } /** Security level. */ @@ -622,9 +621,9 @@ struct bt_conn_cb { * @ref bt_conn_disconnect or by the timeout in the host through * @ref bt_conn_le_create_param timeout parameter, which defaults to * :option:`CONFIG_BT_CREATE_CONN_TIMEOUT` seconds. - * - @p BT_HCI_ERR_ADV_TIMEOUT Directed advertiser started by @ref - * bt_conn_create_slave_le with high duty cycle timed out after 1.28 - * seconds. + * - @p BT_HCI_ERR_ADV_TIMEOUT High duty cycle directed connectable + * advertiser started by @ref bt_le_adv_start or bt_le_ext_adv_start + * failed to be connected within the timeout. */ void (*connected)(struct bt_conn *conn, u8_t err); diff --git a/include/bluetooth/gap.h b/include/bluetooth/gap.h index b4dc5da54a6..2cc4b9d8b56 100644 --- a/include/bluetooth/gap.h +++ b/include/bluetooth/gap.h @@ -123,6 +123,11 @@ enum { #define BT_GAP_SID_INVALID 0xff #define BT_GAP_NO_TIMEOUT 0x0000 +/* The maximum allowed high duty cycle directed advertising timeout, 1.28 + * seconds in 10 ms unit. + */ +#define BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT 128 + #ifdef __cplusplus } #endif diff --git a/include/bluetooth/hci.h b/include/bluetooth/hci.h index b91fd915b46..1fe25b50e12 100644 --- a/include/bluetooth/hci.h +++ b/include/bluetooth/hci.h @@ -1088,12 +1088,6 @@ struct bt_hci_cp_le_set_ext_scan_rsp_data { u8_t data[251]; } __packed; -/* If the advertising is high duty cycle connectable directed advertising, then - * Duration[i] shall be less than or equal to 1.28 seconds and shall not be - * equal to 0. - */ -#define BT_HCI_LE_EXT_ADV_DURATION_HI_DC_MAX 128 - #define BT_HCI_OP_LE_SET_EXT_ADV_ENABLE BT_OP(BT_OGF_LE, 0x0039) struct bt_hci_ext_adv_set { u8_t handle; diff --git a/samples/bluetooth/hci_pwr_ctrl/src/main.c b/samples/bluetooth/hci_pwr_ctrl/src/main.c index 05c74bc9403..4defe4e2b1e 100644 --- a/samples/bluetooth/hci_pwr_ctrl/src/main.c +++ b/samples/bluetooth/hci_pwr_ctrl/src/main.c @@ -38,9 +38,9 @@ static K_THREAD_STACK_DEFINE(pwr_thread_stack, 320); static const s8_t txp[DEVICE_BEACON_TXPOWER_NUM] = {4, 0, -3, -8, -15, -18, -23, -30}; -static const struct bt_le_adv_param *param = BT_LE_ADV_PARAM - (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, - 0x0020, 0x0020); +static const struct bt_le_adv_param *param = + BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, + 0x0020, 0x0020, NULL); static void read_conn_rssi(u16_t handle, s8_t *rssi) { diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index f0a7a3ae89f..405c3715f53 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -1553,6 +1553,27 @@ void bt_conn_process_tx(struct bt_conn *conn) } } +bool bt_conn_exists_le(u8_t id, const bt_addr_le_t *peer) +{ + struct bt_conn *conn = bt_conn_lookup_addr_le(id, peer); + + if (conn) { + /* Connection object already exists. + * If the connection state is not "disconnected",then the + * connection was created but has not yet been disconnected. + * If the connection state is "disconnected" then the connection + * still has valid references. The last reference of the stack + * is released after the disconnected callback. + */ + BT_WARN("Found valid connection in %s state", + state2str(conn->state)); + bt_conn_unref(conn); + return true; + } + + return false; +} + struct bt_conn *bt_conn_add_le(u8_t id, const bt_addr_le_t *peer) { struct bt_conn *conn = conn_new(); @@ -2068,6 +2089,7 @@ int bt_conn_disconnect(struct bt_conn *conn, u8_t reason) } return 0; case BT_CONN_CONNECT_DIR_ADV: + BT_WARN("Deprecated: Use bt_le_adv_stop instead"); conn->err = reason; bt_conn_set_state(conn, BT_CONN_DISCONNECTED); if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { @@ -2273,19 +2295,7 @@ int bt_conn_le_create(const bt_addr_le_t *peer, return -EINVAL; } - conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, peer); - if (conn) { - /* Connection object already exists. - * If the connection state is "connect" or "connected" then - * this connection object was created using this API but has not - * yet been disconnected. - * If the connection state is "disconnected" then the connection - * still has valid references. The last reference of the stack - * is released after the disconnected callback. - */ - BT_WARN("Found valid connection in %s state", - state2str(conn->state)); - bt_conn_unref(conn); + if (bt_conn_exists_le(BT_ID_DEFAULT, peer)) { return -EINVAL; } @@ -2398,53 +2408,6 @@ int bt_le_set_auto_conn(const bt_addr_le_t *addr, #endif /* !defined(CONFIG_BT_WHITELIST) */ #endif /* CONFIG_BT_CENTRAL */ -#if defined(CONFIG_BT_PERIPHERAL) -int bt_conn_le_create_slave(const bt_addr_le_t *peer, - const struct bt_le_adv_param *param, - struct bt_conn **ret_conn) -{ - int err; - struct bt_conn *conn; - struct bt_le_adv_param param_int; - - memcpy(¶m_int, param, sizeof(param_int)); - param_int.options |= (BT_LE_ADV_OPT_CONNECTABLE | - BT_LE_ADV_OPT_ONE_TIME); - - conn = bt_conn_lookup_addr_le(param->id, peer); - if (conn) { - /* Connection object already exists. - * If the connection state is "connect-dir-adv" or "connected" - * then this connection object was created using this API but - * has not yet been disconnected. - * If the connection state is "disconnected" then the connection - * still has valid references. The last reference of the stack - * is released after the disconnected callback. - */ - BT_WARN("Found valid connection in %s state", - state2str(conn->state)); - bt_conn_unref(conn); - return -EINVAL; - } - - conn = bt_conn_add_le(param->id, peer); - if (!conn) { - return -ENOMEM; - } - - bt_conn_set_state(conn, BT_CONN_CONNECT_DIR_ADV); - - err = bt_le_adv_start_internal(¶m_int, NULL, 0, NULL, 0, peer); - if (err) { - bt_conn_unref(conn); - return err; - } - - *ret_conn = conn; - return 0; -} -#endif /* CONFIG_BT_PERIPHERAL */ - int bt_conn_le_conn_update(struct bt_conn *conn, const struct bt_le_conn_param *param) { diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 904fe17de31..4f590ed096f 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -171,6 +171,9 @@ static inline int bt_conn_send(struct bt_conn *conn, struct net_buf *buf) return bt_conn_send_cb(conn, buf, NULL, NULL); } +/* Check if a connection object with the peer already exists */ +bool bt_conn_exists_le(u8_t id, const bt_addr_le_t *peer); + /* Add a new LE connection */ struct bt_conn *bt_conn_add_le(u8_t id, const bt_addr_le_t *peer); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 1d7ea4a4b3d..f7870360039 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1753,7 +1753,7 @@ static struct bt_conn *find_pending_connect(u8_t role, bt_addr_le_t *peer_addr) /* * Make lookup to check if there's a connection object in - * CONNECT or DIR_ADV state associated with passed peer LE address. + * CONNECT or CONNECT_AUTO state associated with passed peer LE address. */ if (IS_ENABLED(CONFIG_BT_CENTRAL) && role == BT_HCI_ROLE_MASTER) { conn = bt_conn_lookup_state_le(BT_ID_DEFAULT, peer_addr, @@ -6934,8 +6934,30 @@ bool bt_addr_le_is_bonded(u8_t id, const bt_addr_le_t *addr) } } -static bool valid_adv_param(const struct bt_le_adv_param *param, bool dir_adv) +static bool valid_adv_ext_param(const struct bt_le_adv_param *param) { + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { + if (param->peer && + !(param->options & BT_LE_ADV_OPT_EXT_ADV) && + !(param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + /* Cannot do directed non-connectable advertising + * without extended advertising. + */ + return false; + } + + if (!(param->options & BT_LE_ADV_OPT_EXT_ADV) && + param->options & (BT_LE_ADV_OPT_EXT_ADV | + BT_LE_ADV_OPT_NO_2M | + BT_LE_ADV_OPT_CODED | + BT_LE_ADV_OPT_ANONYMOUS | + BT_LE_ADV_OPT_USE_TX_POWER)) { + /* Extended options require extended advertising. */ + return false; + } + } + if (param->id >= bt_dev.id_count || !bt_addr_le_cmp(&bt_dev.id_addr[param->id], BT_ADDR_LE_ANY)) { return false; @@ -6954,7 +6976,8 @@ static bool valid_adv_param(const struct bt_le_adv_param *param, bool dir_adv) } } - if ((param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) || !dir_adv) { + if ((param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) || + !param->peer) { if (param->interval_min > param->interval_max || param->interval_min < 0x0020 || param->interval_max > 0x4000) { @@ -6965,6 +6988,19 @@ static bool valid_adv_param(const struct bt_le_adv_param *param, bool dir_adv) return true; } +static bool valid_adv_param(const struct bt_le_adv_param *param) +{ + if (param->options & BT_LE_ADV_OPT_EXT_ADV) { + return false; + } + + if (param->peer && !(param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + return false; + } + + return valid_adv_ext_param(param); +} + static inline bool ad_has_name(const struct bt_data *ad, size_t ad_len) { size_t i; @@ -7077,8 +7113,8 @@ static u8_t get_filter_policy(u8_t options) } } -int le_adv_set_random_addr(struct bt_le_ext_adv *adv, u32_t options, - bool dir_adv, u8_t *own_addr_type) +static int le_adv_set_random_addr(struct bt_le_ext_adv *adv, u32_t options, + bool dir_adv, u8_t *own_addr_type) { const bt_addr_le_t *id_addr; int err = 0; @@ -7173,15 +7209,63 @@ int le_adv_set_random_addr(struct bt_le_ext_adv *adv, u32_t options, return 0; } +static int le_adv_start_add_conn(const struct bt_le_ext_adv *adv, + struct bt_conn **out_conn) +{ + struct bt_conn *conn; + + if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) { + /* Undirected advertising */ + conn = bt_conn_add_le(adv->id, BT_ADDR_LE_NONE); + if (!conn) { + return -ENOMEM; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT_ADV); + *out_conn = conn; + return 0; + } + + if (bt_conn_exists_le(adv->id, &adv->target_addr)) { + return -EINVAL; + } + + conn = bt_conn_add_le(adv->id, &adv->target_addr); + if (!conn) { + return -ENOMEM; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT_DIR_ADV); + *out_conn = conn; + return 0; +} + +static void le_adv_stop_free_conn(const struct bt_le_ext_adv *adv) +{ + struct bt_conn *conn; + + if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) { + conn = bt_conn_lookup_state_le(adv->id, BT_ADDR_LE_NONE, + BT_CONN_CONNECT_ADV); + } else { + conn = bt_conn_lookup_state_le(adv->id, &adv->target_addr, + BT_CONN_CONNECT_DIR_ADV); + } + + if (conn) { + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_conn_unref(conn); + } +} + int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, const struct bt_data *ad, size_t ad_len, - const struct bt_data *sd, size_t sd_len, - const bt_addr_le_t *peer) + const struct bt_data *sd, size_t sd_len) { struct bt_hci_cp_le_set_adv_param set_param; struct bt_conn *conn = NULL; struct net_buf *buf; - bool dir_adv = (peer != NULL); + bool dir_adv = (param->peer != NULL); int err; struct bt_le_ext_adv *adv; @@ -7189,7 +7273,7 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, return -EAGAIN; } - if (!valid_adv_param(param, dir_adv)) { + if (!valid_adv_param(param)) { return -EINVAL; } @@ -7225,6 +7309,12 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, return err; } + if (dir_adv) { + bt_addr_le_copy(&adv->target_addr, param->peer); + } else { + bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY); + } + if (param->options & BT_LE_ADV_OPT_CONNECTABLE) { if (dir_adv) { if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) { @@ -7233,7 +7323,7 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, set_param.type = BT_HCI_ADV_DIRECT_IND; } - bt_addr_le_copy(&set_param.direct_addr, peer); + bt_addr_le_copy(&set_param.direct_addr, param->peer); } else { set_param.type = BT_HCI_ADV_IND; } @@ -7264,15 +7354,13 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, if (err) { return err; } + } - if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && - param->options & BT_LE_ADV_OPT_CONNECTABLE) { - conn = bt_conn_add_le(param->id, BT_ADDR_LE_NONE); - if (!conn) { - return -ENOMEM; - } - - bt_conn_set_state(conn, BT_CONN_CONNECT_ADV); + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + (param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + err = le_adv_start_add_conn(adv, &conn); + if (err) { + return err; } } @@ -7283,6 +7371,7 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, bt_conn_set_state(conn, BT_CONN_DISCONNECTED); bt_conn_unref(conn); } + return err; } @@ -7295,7 +7384,7 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, bt_conn_unref(conn); } - atomic_set_bit_to(bt_dev.flags, BT_DEV_KEEP_ADVERTISING, + atomic_set_bit_to(bt_dev.flags, BT_DEV_KEEP_ADVERTISING, !dir_adv && !(param->options & BT_LE_ADV_OPT_ONE_TIME)); atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING_NAME, @@ -7312,11 +7401,10 @@ int bt_le_adv_start_legacy(const struct bt_le_adv_param *param, static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, const struct bt_le_adv_param *param, - const bt_addr_le_t *peer, bool has_scan_data) { struct bt_hci_cp_le_set_ext_adv_param *cp; - bool dir_adv = peer != NULL; + bool dir_adv = param->peer != NULL; struct net_buf *buf, *rsp; int err; @@ -7334,6 +7422,12 @@ static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, return err; } + if (dir_adv) { + bt_addr_le_copy(&adv->target_addr, param->peer); + } else { + bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY); + } + sys_put_le24(param->interval_min, cp->prim_min_interval); sys_put_le24(param->interval_max, cp->prim_max_interval); cp->prim_channel_map = 0x07; @@ -7372,15 +7466,7 @@ static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, if (param->options & BT_LE_ADV_OPT_CONNECTABLE) { cp->props |= BT_HCI_LE_ADV_PROP_CONN; - if (dir_adv) { - cp->props |= BT_HCI_LE_ADV_PROP_DIRECT; - if (!(param->options & - BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) { - cp->props |= BT_HCI_LE_ADV_PROP_HI_DC_CONN; - } - - bt_addr_le_copy(&cp->peer_addr, peer); - } else if (!(param->options & BT_LE_ADV_OPT_EXT_ADV)) { + if (!dir_adv && !(param->options & BT_LE_ADV_OPT_EXT_ADV)) { /* When using non-extended adv packets then undirected * advertising has to be scannable as well. * We didn't require this option to be set before, so @@ -7394,6 +7480,15 @@ static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, cp->props |= BT_HCI_LE_ADV_PROP_SCAN; } + if (dir_adv) { + cp->props |= BT_HCI_LE_ADV_PROP_DIRECT; + if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) { + cp->props |= BT_HCI_LE_ADV_PROP_HI_DC_CONN; + } + + bt_addr_le_copy(&cp->peer_addr, param->peer); + } + cp->sid = param->sid; err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, buf, &rsp); @@ -7436,14 +7531,13 @@ static int le_ext_adv_param_set(struct bt_le_ext_adv *adv, int bt_le_adv_start_ext(struct bt_le_ext_adv *adv, const struct bt_le_adv_param *param, const struct bt_data *ad, size_t ad_len, - const struct bt_data *sd, size_t sd_len, - const bt_addr_le_t *peer) + const struct bt_data *sd, size_t sd_len) { struct bt_le_ext_adv_start_param start_param = { .timeout = 0, .num_events = 0, }; - bool dir_adv = (peer != NULL); + bool dir_adv = (param->peer != NULL); struct bt_conn *conn = NULL; int err; @@ -7451,7 +7545,7 @@ int bt_le_adv_start_ext(struct bt_le_ext_adv *adv, return -EAGAIN; } - if (!valid_adv_param(param, dir_adv)) { + if (!valid_adv_param(param)) { return -EINVAL; } @@ -7461,7 +7555,7 @@ int bt_le_adv_start_ext(struct bt_le_ext_adv *adv, adv->id = param->id; - err = le_ext_adv_param_set(adv, param, peer, sd || + err = le_ext_adv_param_set(adv, param, sd || (param->options & BT_LE_ADV_OPT_USE_NAME)); if (err) { return err; @@ -7474,19 +7568,20 @@ int bt_le_adv_start_ext(struct bt_le_ext_adv *adv, if (err) { return err; } - - if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && - param->options & BT_LE_ADV_OPT_CONNECTABLE) { - conn = bt_conn_add_le(param->id, BT_ADDR_LE_NONE); - if (!conn) { - return -ENOMEM; - } - - bt_conn_set_state(conn, BT_CONN_CONNECT_ADV); + } else { + if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) { + start_param.timeout = + BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT; + atomic_set_bit(adv->flags, BT_ADV_LIMITED); + } + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + (param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + err = le_adv_start_add_conn(adv, &conn); + if (err) { + return err; } - } else if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) { - start_param.timeout = BT_HCI_LE_EXT_ADV_DURATION_HI_DC_MAX; - atomic_set_bit(adv->flags, BT_ADV_LIMITED); } err = set_le_adv_enable_ext(adv, true, &start_param); @@ -7518,10 +7613,9 @@ int bt_le_adv_start_ext(struct bt_le_ext_adv *adv, return 0; } -int bt_le_adv_start_internal(const struct bt_le_adv_param *param, - const struct bt_data *ad, size_t ad_len, - const struct bt_data *sd, size_t sd_len, - const bt_addr_le_t *peer) +int bt_le_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) { if (IS_ENABLED(CONFIG_BT_EXT_ADV) && BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) { @@ -7532,8 +7626,7 @@ int bt_le_adv_start_internal(const struct bt_le_adv_param *param, return -ENOMEM; } - err = bt_le_adv_start_ext(adv, param, ad, ad_len, sd, sd_len, - peer); + err = bt_le_adv_start_ext(adv, param, ad, ad_len, sd, sd_len); if (err) { adv_delete_legacy(); } @@ -7541,18 +7634,7 @@ int bt_le_adv_start_internal(const struct bt_le_adv_param *param, return err; } - return bt_le_adv_start_legacy(param, ad, ad_len, sd, sd_len, peer); -} - -int bt_le_adv_start(const struct bt_le_adv_param *param, - const struct bt_data *ad, size_t ad_len, - const struct bt_data *sd, size_t sd_len) -{ - if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) { - return -EINVAL; - } - - return bt_le_adv_start_internal(param, ad, ad_len, sd, sd_len, NULL); + return bt_le_adv_start_legacy(param, ad, ad_len, sd, sd_len); } int bt_le_adv_stop(void) @@ -7565,27 +7647,19 @@ int bt_le_adv_stop(void) atomic_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING); if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + /* Legacy advertiser exists, but is not currently advertising. + * This happens when keep advertising behavior is active but + * no conn object is available to do connectable advertising. + */ adv_delete_legacy(); return 0; } - if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE)) { struct bt_le_ext_adv *adv = bt_adv_lookup_legacy(); - struct bt_conn *conn; - conn = bt_conn_lookup_state_le(adv->id, BT_ADDR_LE_NONE, - BT_CONN_CONNECT_ADV); - if (conn) { - bt_conn_set_state(conn, BT_CONN_DISCONNECTED); - bt_conn_unref(conn); - } - - conn = bt_conn_lookup_state_le(adv->id, NULL, - BT_CONN_CONNECT_DIR_ADV); - if (conn) { - bt_conn_set_state(conn, BT_CONN_DISCONNECTED); - bt_conn_unref(conn); - } + le_adv_stop_free_conn(adv); } if (IS_ENABLED(CONFIG_BT_EXT_ADV) && @@ -7631,7 +7705,7 @@ int bt_le_adv_stop(void) void bt_le_adv_resume(void) { struct bt_le_ext_adv *adv = bt_adv_lookup_legacy(); - struct bt_conn *adv_conn; + struct bt_conn *conn; int err; if (!adv) { @@ -7641,13 +7715,13 @@ void bt_le_adv_resume(void) BT_ASSERT(atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE)); - - adv_conn = bt_conn_add_le(adv->id, BT_ADDR_LE_NONE); - if (!adv_conn) { + err = le_adv_start_add_conn(adv, &conn); + if (err) { + BT_DBG("Cannot resume connectable advertising (%d)", err); return; } - bt_conn_set_state(adv_conn, BT_CONN_CONNECT_ADV); + BT_DBG("Resuming connectable advertising"); if (IS_ENABLED(CONFIG_BT_PRIVACY) && !atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_IDENTITY)) { @@ -7656,23 +7730,17 @@ void bt_le_adv_resume(void) err = set_le_adv_enable(adv, true); if (err) { - bt_conn_set_state(adv_conn, BT_CONN_DISCONNECTED); + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); } /* Since we don't give the application a reference to manage in * this case, we need to release this reference here. */ - bt_conn_unref(adv_conn); + bt_conn_unref(conn); } - #endif /* defined(CONFIG_BT_PERIPHERAL) */ #if defined(CONFIG_BT_EXT_ADV) -static bool valid_adv_ext_param(const struct bt_le_adv_param *param) -{ - return valid_adv_param(param, false); -} - int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv, struct bt_le_ext_adv_info *info) { @@ -7705,7 +7773,7 @@ int bt_le_ext_adv_create(const struct bt_le_adv_param *param, adv->id = param->id; adv->cb = cb; - err = le_ext_adv_param_set(adv, param, NULL, false); + err = le_ext_adv_param_set(adv, param, false); if (err) { adv_delete(adv); return err; @@ -7730,7 +7798,7 @@ int bt_le_ext_adv_update_param(struct bt_le_ext_adv *adv, atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID); } - return le_ext_adv_param_set(adv, param, NULL, false); + return le_ext_adv_param_set(adv, param, false); } int bt_le_ext_adv_start(struct bt_le_ext_adv *adv, @@ -7741,12 +7809,10 @@ int bt_le_ext_adv_start(struct bt_le_ext_adv *adv, if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE)) { - conn = bt_conn_add_le(adv->id, BT_ADDR_LE_NONE); - if (!conn) { - return -ENOMEM; + err = le_adv_start_add_conn(adv, &conn); + if (err) { + return err; } - - bt_conn_set_state(conn, BT_CONN_CONNECT_ADV); } atomic_set_bit_to(adv->flags, BT_ADV_LIMITED, param && @@ -7793,22 +7859,9 @@ int bt_le_ext_adv_stop(struct bt_le_ext_adv *adv) #endif } - if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { - struct bt_conn *conn; - - conn = bt_conn_lookup_state_le(adv->id, BT_ADDR_LE_NONE, - BT_CONN_CONNECT_ADV); - if (conn) { - bt_conn_set_state(conn, BT_CONN_DISCONNECTED); - bt_conn_unref(conn); - } - - conn = bt_conn_lookup_state_le(adv->id, NULL, - BT_CONN_CONNECT_DIR_ADV); - if (conn) { - bt_conn_set_state(conn, BT_CONN_DISCONNECTED); - bt_conn_unref(conn); - } + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE)) { + le_adv_stop_free_conn(adv); } return set_le_adv_enable_ext(adv, false, NULL); diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index f9813c45293..0354e4386ac 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -98,6 +98,9 @@ struct bt_le_ext_adv { /* Current local Random Address */ bt_addr_le_t random_addr; + /* Current target address */ + bt_addr_le_t target_addr; + ATOMIC_DEFINE(flags, BT_ADV_NUM_FLAGS); #if defined(CONFIG_BT_EXT_ADV) diff --git a/subsys/bluetooth/shell/bt.c b/subsys/bluetooth/shell/bt.c index 6c86d42770e..6a373946c74 100644 --- a/subsys/bluetooth/shell/bt.c +++ b/subsys/bluetooth/shell/bt.c @@ -800,10 +800,10 @@ static int cmd_directed_adv(const struct shell *shell, { int err; bt_addr_le_t addr; - struct bt_conn *conn; - struct bt_le_adv_param *param = BT_LE_ADV_CONN_DIR; + struct bt_le_adv_param param; err = bt_addr_le_from_str(argv[1], argv[2], &addr); + param = *BT_LE_ADV_CONN_DIR(&addr); if (err) { shell_error(shell, "Invalid peer address (err %d)", err); return err; @@ -811,23 +811,20 @@ static int cmd_directed_adv(const struct shell *shell, if (argc > 3) { if (!strcmp(argv[3], "low")) { - param = BT_LE_ADV_CONN_DIR_LOW_DUTY; + param = *BT_LE_ADV_CONN_DIR_LOW_DUTY(&addr); } else { shell_help(shell); return -ENOEXEC; } } - err = bt_conn_le_create_slave(&addr, param, &conn); + err = bt_le_adv_start(¶m, NULL, 0, NULL, 0); if (err) { shell_error(shell, "Failed to start directed advertising (%d)", err); return -ENOEXEC; } else { shell_print(shell, "Started directed advertising"); - - /* unref connection obj in advance as app user */ - bt_conn_unref(conn); } return 0; @@ -839,6 +836,7 @@ static bool adv_param_parse(size_t argc, char *argv[], struct bt_le_adv_param *param) { param->options = 0; + param->peer = NULL; if (!strcmp(argv[1], "conn-scan")) { param->options |= BT_LE_ADV_OPT_CONNECTABLE;