fix(nimble): recover PAwR connect timeout and V2 handle routing

This commit is contained in:
Rahul Tank
2026-08-24 22:11:22 +05:30
parent 6ad55ca0aa
commit 96ab0e4d86
6 changed files with 458 additions and 45 deletions
+6 -4
View File
@@ -3282,10 +3282,12 @@ int ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr,
* @param peer_addr The address of the peer to connect to.
* If this parameter is NULL, the white list
* is used.
* @param duration_ms The duration of the discovery procedure.
* On expiration, the procedure ends and a
* BLE_GAP_EVENT_DISC_COMPLETE event is
* reported. Units are milliseconds.
* @param duration_ms The duration of the connect procedure. On
* expiration, the procedure is cancelled and
* a BLE_GAP_EVENT_CONNECT event with a status
* of BLE_HS_ETIMEOUT is reported. Units are
* milliseconds. Specify BLE_HS_FOREVER to
* wait indefinitely for the controller.
* @param phy_mask Define on which PHYs connection attempt should
* be done
* @param phy_1m_conn_params Additional arguments specifying the
+256 -19
View File
@@ -222,6 +222,10 @@ struct ble_gap_master_state {
uint8_t using_wl:1;
uint8_t our_addr_type:2;
uint8_t cancel:1;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
uint8_t synchronized:1;
uint8_t timeout_cancel_pending:1;
#endif
ble_addr_t peer_addr;
} conn;
@@ -1672,6 +1676,10 @@ ble_gap_master_reset_state(void)
ble_gap_master.conn.cancel = 0;
ble_gap_master.cb = NULL;
ble_gap_master.cb_arg = NULL;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
ble_gap_master.conn.synchronized = 0;
ble_gap_master.conn.timeout_cancel_pending = 0;
#endif
memset(&ble_gap_master.conn.peer_addr, 0,
sizeof(ble_gap_master.conn.peer_addr));
@@ -3275,7 +3283,8 @@ ble_gap_periodic_sync_reattempt_clear(bool clear_sync_reattempt)
#endif
void
ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev)
ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev,
uint8_t subevent)
{
uint16_t sync_handle;
struct ble_gap_event event;
@@ -3284,6 +3293,9 @@ ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv
#if MYNEWT_VAL(BLE_ENABLE_CONN_REATTEMPT) && NIMBLE_BLE_CONNECT
int rc;
bool reattempt_triggered = false;
#endif
#if !MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
(void)subevent;
#endif
memset(&event, 0, sizeof event);
@@ -3320,10 +3332,13 @@ ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv
event.periodic_sync.per_adv_ival = ev->interval;
event.periodic_sync.adv_clk_accuracy = ev->aca;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
event.periodic_sync.num_subevents = ev->num_subevents;
event.periodic_sync.subevent_interval = ev->subevent_interval;
event.periodic_sync.response_slot_delay = ev->response_slot_delay;
event.periodic_sync.response_slot_spacing = ev->response_slot_spacing;
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB_V2) {
ble_gap_sync.psync->num_subevents = ev->num_subevents;
event.periodic_sync.num_subevents = ev->num_subevents;
event.periodic_sync.subevent_interval = ev->subevent_interval;
event.periodic_sync.response_slot_delay = ev->response_slot_delay;
event.periodic_sync.response_slot_spacing = ev->response_slot_spacing;
}
#endif
ble_hs_periodic_sync_insert(ble_gap_sync.psync);
@@ -3657,7 +3672,8 @@ periodic_adv_transfer_disable(uint16_t conn_handle)
}
void
ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev)
ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev,
uint8_t subevent)
{
struct ble_hci_le_periodic_adv_term_sync_cp cmd_term;
struct ble_gap_event event;
@@ -3667,6 +3683,9 @@ ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_
uint16_t conn_handle;
uint16_t opcode;
void *cb_arg;
#if !MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
(void)subevent;
#endif
conn_handle = le16toh(ev->conn_handle);
@@ -3697,7 +3716,11 @@ ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_
memset(&event, 0, sizeof event);
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
event.type = BLE_GAP_EVENT_PERIODIC_TRANSFER_V2;
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER_V2) {
event.type = BLE_GAP_EVENT_PERIODIC_TRANSFER_V2;
} else {
event.type = BLE_GAP_EVENT_PERIODIC_TRANSFER;
}
#else
event.type = BLE_GAP_EVENT_PERIODIC_TRANSFER;
#endif // MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
@@ -3714,6 +3737,11 @@ ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_
conn->psync->adv_sid = ev->sid;
memcpy(conn->psync->advertiser_addr.val, ev->peer_addr, 6);
conn->psync->advertiser_addr.type = ev->peer_addr_type;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER_V2) {
conn->psync->num_subevents = ev->num_subevents;
}
#endif
ble_hs_periodic_sync_insert(conn->psync);
}
@@ -3730,10 +3758,12 @@ ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_
event.periodic_transfer.per_adv_itvl = le16toh(ev->interval);
event.periodic_transfer.adv_clk_accuracy = ev->aca;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
event.periodic_transfer.num_subevents = ev->num_subevents;
event.periodic_transfer.subevent_interval = ev->subevent_interval;
event.periodic_transfer.response_slot_delay = ev->response_slot_delay;
event.periodic_transfer.response_slot_spacing = ev->response_slot_spacing;
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER_V2) {
event.periodic_transfer.num_subevents = ev->num_subevents;
event.periodic_transfer.subevent_interval = ev->subevent_interval;
event.periodic_transfer.response_slot_delay = ev->response_slot_delay;
event.periodic_transfer.response_slot_spacing = ev->response_slot_spacing;
}
#endif
ble_hs_unlock();
@@ -3953,6 +3983,69 @@ ble_gap_rd_rem_ver_tx(uint16_t handle)
}
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES) && NIMBLE_BLE_CONNECT
/**
* Determines the local address type used for a PAwR peripheral connection.
*
* Such a link is established by the controller through the periodic sync, so
* no advertising instance describes it; the controller uses our identity
* address, or an RPA generated from it.
*/
static int
ble_gap_pawr_conn_our_addr(const struct ble_gap_conn_complete *evt,
uint8_t *out_own_addr_type,
uint8_t *out_random_addr)
{
uint8_t own_addr_type;
int privacy;
int rc;
privacy = memcmp(evt->local_rpa, BLE_ADDR_ANY->val, 6) != 0;
rc = ble_hs_id_infer_auto(privacy, &own_addr_type);
if (rc != 0) {
return rc;
}
#if MYNEWT_VAL(BLE_EXT_ADV)
if (ble_hs_misc_own_addr_type_to_id(own_addr_type) == BLE_ADDR_RANDOM) {
rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, out_random_addr, NULL);
if (rc != 0) {
return rc;
}
}
#else
(void)out_random_addr;
#endif
*out_own_addr_type = own_addr_type;
return 0;
}
#endif
#if NIMBLE_BLE_CONNECT
/**
* Drops a link the host has no state for. Such a connection can never be used
* or terminated by the application, so leaving it up would strand it until the
* supervision timeout expires.
*/
static void
ble_gap_disconnect_unclaimed_conn(uint16_t conn_handle)
{
struct ble_hci_lc_disconnect_cp cmd;
int rc;
cmd.conn_handle = htole16(conn_handle);
cmd.reason = BLE_ERR_REM_USER_CONN_TERM;
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LINK_CTRL,
BLE_HCI_OCF_DISCONNECT_CMD),
&cmd, sizeof(cmd), NULL, 0);
BLE_HS_LOG(WARN, "GAP: no procedure owns conn_handle=%d; disconnecting; "
"rc=%d\n", conn_handle, rc);
}
#endif
/**
* Processes an incoming connection-complete HCI event.
* instance parameter is valid only for slave connection.
@@ -4010,13 +4103,26 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance)
}
#endif
break;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
case BLE_ERR_CONN_ESTABLISHMENT:
#if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER)
/*
* Failed enhanced connection-complete events do not contain
* valid role or PAwR handle fields. Use host state to route
* failures before inspecting those fields.
*/
if (ble_gap_conn_active()) {
ble_gap_master_connect_failure(
BLE_HS_HCI_ERR(evt->status));
break;
}
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (!v1_evt) {
ble_gap_rx_conn_comp_failed(evt);
break;
}
#endif
break;
#endif // MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
default:
/* this should never happen, unless controller is broken */
BLE_HS_LOG(INFO, "controller reported invalid error code in conn"
@@ -4044,13 +4150,22 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance)
case BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER:
rc = ble_gap_accept_master_conn();
if (rc != 0) {
ble_gap_disconnect_unclaimed_conn(evt->connection_handle);
return rc;
}
break;
case BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE:
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (evt->sync_handle != 0xFFFF) {
/* PAwR peripheral links are accepted by the controller through
* the synchronized connection procedure, not local advertising. */
rc = 0;
} else
#endif
rc = ble_gap_accept_slave_conn(instance);
if (rc != 0) {
ble_gap_disconnect_unclaimed_conn(evt->connection_handle);
return rc;
}
break;
@@ -4082,7 +4197,26 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance)
if (!v1_evt && evt->sync_handle != 0xFFFF) {
/* PAwR peripheral: application callback is on the periodic sync. */
struct ble_hs_periodic_sync *psync;
uint8_t own_addr_type;
#if MYNEWT_VAL(BLE_EXT_ADV)
uint8_t random_addr[BLE_DEV_ADDR_LEN];
#endif
#if MYNEWT_VAL(BLE_EXT_ADV)
rc = ble_gap_pawr_conn_our_addr(evt, &own_addr_type, random_addr);
#else
rc = ble_gap_pawr_conn_our_addr(evt, &own_addr_type, NULL);
#endif
if (rc == 0) {
conn->bhc_our_addr_type = own_addr_type;
#if MYNEWT_VAL(BLE_EXT_ADV)
if (ble_hs_misc_own_addr_type_to_id(own_addr_type) ==
BLE_ADDR_RANDOM) {
memcpy(conn->bhc_our_rnd_addr, random_addr,
BLE_DEV_ADDR_LEN);
}
#endif
}
ble_hs_lock();
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);
if (psync != NULL) {
@@ -4094,12 +4228,20 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance)
conn->bhc_cb = ble_gap_master.cb;
conn->bhc_cb_arg = ble_gap_master.cb_arg;
conn->bhc_our_addr_type = ble_gap_master.conn.our_addr_type;
ble_gap_master_reset_state();
} else {
ble_hs_lock();
conn->bhc_cb = ble_gap_slave[instance].cb;
conn->bhc_cb_arg = ble_gap_slave[instance].cb_arg;
conn->bhc_our_addr_type = ble_gap_slave[instance].our_addr_type;
#if MYNEWT_VAL(BLE_EXT_ADV)
memcpy(conn->bhc_our_rnd_addr, ble_gap_slave[instance].rnd_addr, 6);
#endif
ble_gap_slave_reset_state(instance);
ble_hs_unlock();
}
#else
ble_hs_lock();
conn->bhc_cb = ble_gap_slave[instance].cb;
conn->bhc_cb_arg = ble_gap_slave[instance].cb_arg;
conn->bhc_our_addr_type = ble_gap_slave[instance].our_addr_type;
@@ -4107,6 +4249,7 @@ ble_gap_rx_conn_complete(struct ble_gap_conn_complete *evt, uint8_t instance)
memcpy(conn->bhc_our_rnd_addr, ble_gap_slave[instance].rnd_addr, 6);
#endif
ble_gap_slave_reset_state(instance);
ble_hs_unlock();
#endif
if (master_match) {
@@ -4439,11 +4582,28 @@ static int32_t
ble_gap_master_timer(void)
{
uint32_t ticks_until_exp;
uint8_t op;
#if NIMBLE_BLE_CONNECT && MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES) && \
(MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER))
bool synchronized;
bool timeout_cancel_pending;
#endif
#if NIMBLE_BLE_CONNECT || (NIMBLE_BLE_SCAN && !MYNEWT_VAL(BLE_EXT_ADV))
int rc;
#endif
ble_hs_lock();
ticks_until_exp = ble_gap_master_ticks_until_exp();
op = ble_gap_master.op;
#if NIMBLE_BLE_CONNECT && MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES) && \
(MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER))
synchronized = op == BLE_GAP_OP_M_CONN &&
ble_gap_master.conn.synchronized;
timeout_cancel_pending = op == BLE_GAP_OP_M_CONN &&
ble_gap_master.conn.timeout_cancel_pending;
#endif
ble_hs_unlock();
if (ticks_until_exp != 0) {
/* Timer not expired yet. */
return ticks_until_exp;
@@ -4451,16 +4611,70 @@ ble_gap_master_timer(void)
/*** Timer expired; process event. */
switch (ble_gap_master.op) {
switch (op) {
#if NIMBLE_BLE_CONNECT
case BLE_GAP_OP_M_CONN:
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES) && \
(MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER))
if (timeout_cancel_pending) {
/*
* The controller accepted the cancel but did not report its
* connection-complete result within the grace period.
*/
ble_hs_lock();
ble_gap_master.exp_set = 0;
ble_hs_unlock();
ble_gap_master_connect_cancelled();
break;
}
#endif
rc = ble_gap_conn_cancel_tx();
#if MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER)
if (rc == BLE_HS_HCI_ERR(BLE_ERR_CMD_DISALLOWED) ||
rc == BLE_HS_HCI_ERR(BLE_ERR_UNKNOWN_HCI_CMD)) {
/* The controller has no connection attempt pending, so no
* connection complete event will follow the cancel. Report the
* timeout here instead of retrying the cancel forever.
*/
ble_hs_lock();
ble_gap_master.exp_set = 0;
ble_hs_unlock();
ble_gap_master_connect_cancelled();
break;
}
#endif
if (rc != 0) {
/* Failed to stop connecting; try again in 100 ms. */
return ble_npl_time_ms_to_ticks32(BLE_GAP_CANCEL_RETRY_TIMEOUT_MS);
} else {
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES) && \
(MYNEWT_VAL(BLE_ROLE_CENTRAL) || MYNEWT_VAL(BLE_ROLE_OBSERVER))
/*
* A synchronized create-connection controller can fail to send
* the completion event after accepting cancel. Allow the normal
* event a short grace period, then complete the host procedure if
* the controller remains silent.
*/
if (synchronized) {
uint32_t grace_ticks =
ble_npl_time_ms_to_ticks32(
BLE_GAP_CANCEL_RETRY_TIMEOUT_MS);
ble_hs_lock();
ble_gap_master.conn.timeout_cancel_pending = 1;
ble_gap_master.exp_os_ticks =
ble_npl_time_get() + grace_ticks;
ble_hs_unlock();
return grace_ticks;
}
#endif
/* Stop the timer now that the cancel command has been acked. */
ble_hs_lock();
ble_gap_master.exp_set = 0;
ble_hs_unlock();
/* Timeout gets reported when we receive a connection complete
* event indicating the connect procedure has been cancelled.
@@ -8840,6 +9054,7 @@ ble_gap_connect_with_synced(uint8_t own_addr_type, uint8_t advertising_handle,
const struct ble_gap_conn_params *phy_coded_conn_params,
ble_gap_event_fn *cb, void *cb_arg)
{
uint32_t duration_ticks = 0;
int rc;
STATS_INC(ble_gap_stats, initiate);
@@ -8903,11 +9118,20 @@ ble_gap_connect_with_synced(uint8_t own_addr_type, uint8_t advertising_handle,
duration_ms = BLE_GAP_CONN_DUR_DFLT;
}
/* The connection creation timeout is not really useful for PAwR.
* The controller will give a result for the connection attempt
* within a periodic interval. We do not know the periodic interval
* used, so disable the timeout.
*/
/* The controller reports the outcome of a synchronized connection attempt
* within a periodic interval, but a controller that never reports it would
* otherwise leave the initiate procedure pending forever with no event for
* the application. Keep the host timeout as a recovery path; callers that
* do not want it can pass BLE_HS_FOREVER.
*/
if (duration_ms != BLE_HS_FOREVER) {
rc = ble_npl_time_ms_to_ticks(duration_ms, &duration_ticks);
if (rc != 0) {
/* Duration too great. */
rc = BLE_HS_EINVAL;
goto done;
}
}
/* Verify peer not already connected. */
if (ble_hs_conn_find_by_addr(peer_addr) != NULL) {
@@ -8928,6 +9152,7 @@ ble_gap_connect_with_synced(uint8_t own_addr_type, uint8_t advertising_handle,
ble_gap_master.cb_arg = cb_arg;
ble_gap_master.conn.using_wl = peer_addr == NULL;
ble_gap_master.conn.our_addr_type = own_addr_type;
ble_gap_master.conn.synchronized = 1;
if (peer_addr != NULL) {
ble_gap_master.conn.peer_addr = *peer_addr;
} else {
@@ -8945,6 +9170,12 @@ ble_gap_connect_with_synced(uint8_t own_addr_type, uint8_t advertising_handle,
goto done;
}
#if MYNEWT_VAL(BLE_ROLE_CENTRAL)
if (duration_ms != BLE_HS_FOREVER) {
ble_gap_master_set_timer(duration_ticks);
}
#endif
rc = 0;
done:
@@ -9300,6 +9531,9 @@ ble_gap_ext_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr,
ble_gap_master.cb_arg = cb_arg;
ble_gap_master.conn.using_wl = peer_addr == NULL;
ble_gap_master.conn.our_addr_type = own_addr_type;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
ble_gap_master.conn.synchronized = 0;
#endif
if (peer_addr != NULL) {
ble_gap_master.conn.peer_addr = *peer_addr;
} else {
@@ -9483,6 +9717,9 @@ ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr,
ble_gap_master.cb_arg = cb_arg;
ble_gap_master.conn.using_wl = peer_addr == NULL;
ble_gap_master.conn.our_addr_type = own_addr_type;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
ble_gap_master.conn.synchronized = 0;
#endif
if (peer_addr != NULL) {
ble_gap_master.conn.peer_addr = *peer_addr;
} else {
+4 -2
View File
@@ -84,10 +84,12 @@ void ble_gap_rx_le_scan_timeout(void);
void ble_gap_rx_ext_adv_report(struct ble_gap_ext_disc_desc *desc);
void ble_gap_rx_adv_set_terminated(const struct ble_hci_ev_le_subev_adv_set_terminated *ev);
#if MYNEWT_VAL(BLE_PERIODIC_ADV)
void ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev);
void ble_gap_rx_peroidic_adv_sync_estab(const struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev,
uint8_t subevent);
void ble_gap_rx_periodic_adv_rpt(const struct ble_hci_ev_le_subev_periodic_adv_rpt *ev);
void ble_gap_rx_periodic_adv_sync_lost(const struct ble_hci_ev_le_subev_periodic_adv_sync_lost *ev);
void ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev);
void ble_gap_rx_periodic_adv_sync_transfer(const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev,
uint8_t subevent);
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS) && !MYNEWT_VAL(BLE_ISO)
void ble_gap_rx_biginfo_adv_rpt(const struct ble_hci_ev_le_subev_biginfo_adv_report *ev);
+159 -20
View File
@@ -17,6 +17,7 @@
* under the License.
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
@@ -761,6 +762,86 @@ ble_hs_hci_evt_le_meta(uint8_t event_code, const void *data, unsigned int len)
#if NIMBLE_BLE_CONNECT
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
static void
ble_hs_hci_evt_conn_norm_handles(struct ble_gap_conn_complete *evt)
{
if (evt->status != BLE_ERR_SUCCESS) {
evt->adv_handle = 0xff;
evt->sync_handle = 0xffff;
return;
}
switch (evt->role) {
case BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER:
evt->sync_handle = 0xffff;
if (evt->adv_handle > 0xef) {
evt->adv_handle = 0xff;
}
break;
case BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE:
evt->adv_handle = 0xff;
if (evt->sync_handle > 0x0eff) {
evt->sync_handle = 0xffff;
}
break;
default:
evt->adv_handle = 0xff;
evt->sync_handle = 0xffff;
break;
}
}
static bool
ble_hs_hci_evt_pawr_conn(struct ble_gap_conn_complete *evt,
bool handles_present)
{
struct ble_hs_periodic_sync *psync;
/* Only a peripheral is identified by the sync handle. */
if (evt->status == BLE_ERR_SUCCESS &&
evt->role != BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE) {
return false;
}
ble_hs_lock();
/*
* The sync handle is only meaningful on a successful V2 event; it has
* already been dropped in all the circumstances where the spec requires the
* host to ignore it. Regular periodic syncs share the same list, so a sync
* without subevents does not describe a PAwR train and must not
* short-circuit the pending-connection / Advertising Set Terminated path.
* That also rejects controllers reporting sync handle 0 for a connection
* established from extended advertising.
*/
psync = NULL;
if (evt->status == BLE_ERR_SUCCESS && handles_present &&
evt->sync_handle != 0xffff) {
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);
if (psync != NULL && psync->num_subevents == 0) {
psync = NULL;
}
}
if (psync == NULL &&
(evt->status == BLE_ERR_CONN_ESTABLISHMENT ||
(evt->status == BLE_ERR_SUCCESS && !handles_present))) {
psync = ble_hs_periodic_sync_find_pawr_locked();
}
if (psync != NULL) {
evt->sync_handle = psync->sync_handle;
} else {
evt->sync_handle = 0xffff;
}
ble_hs_unlock();
return psync != NULL;
}
#endif
static int
ble_hs_hci_evt_le_enh_conn_complete(uint8_t subevent, const void *data,
unsigned int len)
@@ -791,6 +872,16 @@ ble_hs_hci_evt_le_enh_conn_complete(uint8_t subevent, const void *data,
evt.status = ev->status;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE) {
evt.adv_handle = 0xff;
evt.sync_handle = 0xffff;
} else {
evt.adv_handle = ev->adv_handle;
evt.sync_handle = le16toh(ev->sync_handle);
}
#endif
if (evt.status == BLE_ERR_SUCCESS) {
evt.connection_handle = le16toh(ev->conn_handle);
evt.role = ev->role;
@@ -824,10 +915,32 @@ ble_hs_hci_evt_le_enh_conn_complete(uint8_t subevent, const void *data,
evt.connection_handle = BLE_HS_CONN_HANDLE_NONE;
#endif
}
#if MYNEWT_VAL(BLE_EXT_ADV) && !MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
ble_hs_hci_evt_conn_norm_handles(&evt);
/*
* PAwR connection-complete failures contain only a valid status field,
* so associate the event with the active PAwR sync before inspecting role.
*/
if (subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE_V2 &&
ble_hs_hci_evt_pawr_conn(&evt, true)) {
return ble_gap_rx_conn_complete(&evt, 0);
}
#endif
#if MYNEWT_VAL(BLE_EXT_ADV)
if (evt.status == BLE_ERR_DIR_ADV_TMO ||
evt.role == BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE) {
/* PAwR slave connections are not followed by LE Advertising Set
* Terminated, so they must not be stored as pending.
*/
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (evt.status == BLE_ERR_SUCCESS &&
subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE &&
ble_hs_hci_evt_pawr_conn(&evt, false)) {
return ble_gap_rx_conn_complete(&evt, 0);
}
#endif
#if MYNEWT_VAL(BLE_STATIC_TO_DYNAMIC)
if (ble_hs_hci_ensure_ctx()) {
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_ENOMEM);
@@ -843,16 +956,15 @@ ble_hs_hci_evt_le_enh_conn_complete(uint8_t subevent, const void *data,
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE) {
evt.adv_handle = 0xFF;
evt.sync_handle = 0xFFFF;
} else {
evt.adv_handle = ev->adv_handle;
evt.sync_handle = ev->sync_handle;
}
#endif
return ble_gap_rx_conn_complete(&evt, 0);
uint8_t instance = 0;
if (evt.adv_handle < BLE_ADV_INSTANCES) {
instance = evt.adv_handle;
}
return ble_gap_rx_conn_complete(&evt, instance);
#else
return ble_gap_rx_conn_complete(&evt, 0);
#endif
}
static int
@@ -871,6 +983,12 @@ ble_hs_hci_evt_le_conn_complete(uint8_t subevent, const void *data,
evt.status = ev->status;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
/* This event carries no PAwR handles. */
evt.adv_handle = 0xff;
evt.sync_handle = 0xffff;
#endif
if (evt.status == BLE_ERR_SUCCESS) {
evt.connection_handle = le16toh(ev->conn_handle);
evt.role = ev->role;
@@ -907,6 +1025,12 @@ ble_hs_hci_evt_le_conn_complete(uint8_t subevent, const void *data,
if (evt.status == BLE_ERR_DIR_ADV_TMO ||
evt.role == BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE) {
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (evt.status == BLE_ERR_SUCCESS &&
ble_hs_hci_evt_pawr_conn(&evt, false)) {
return ble_gap_rx_conn_complete(&evt, 0);
}
#endif
#if MYNEWT_VAL(BLE_STATIC_TO_DYNAMIC)
if (ble_hs_hci_ensure_ctx()) {
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_ENOMEM);
@@ -1477,13 +1601,25 @@ ble_hs_hci_evt_le_periodic_adv_sync_estab(uint8_t subevent, const void *data,
unsigned int len)
{
const struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev = data;
unsigned int min_len;
if (len != sizeof(*ev)) {
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB) {
min_len = offsetof(struct ble_hci_ev_le_subev_periodic_adv_sync_estab,
num_subevents);
} else {
min_len = sizeof(*ev);
}
#else
min_len = sizeof(*ev);
#endif
if (len < min_len) {
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_ECONTROLLER);
return BLE_HS_ECONTROLLER;
}
ble_gap_rx_peroidic_adv_sync_estab(ev);
ble_gap_rx_peroidic_adv_sync_estab(ev, subevent);
return 0;
}
@@ -1564,22 +1700,25 @@ ble_hs_hci_evt_le_periodic_adv_sync_transfer(uint8_t subevent, const void *data,
{
#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER)
const struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev = data;
struct ble_hci_ev_le_subev_periodic_adv_sync_transfer ev_copy;
unsigned int min_len;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
/* V1 event is 4 bytes shorter (lacks PAwR fields); accept both lengths */
unsigned int v1_len = sizeof(*ev) - 4;
if (len != sizeof(*ev) && len != v1_len) {
if (subevent == BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER) {
min_len = offsetof(struct ble_hci_ev_le_subev_periodic_adv_sync_transfer,
num_subevents);
} else {
min_len = sizeof(*ev);
}
#else
if (len != sizeof(*ev)) {
min_len = sizeof(*ev);
#endif
if (len < min_len) {
BLE_HS_LOG(ERROR, "%s rc=%d\n", __func__, BLE_HS_EBADDATA);
return BLE_HS_EBADDATA;
}
memset(&ev_copy, 0, sizeof(ev_copy));
memcpy(&ev_copy, ev, len);
ble_gap_rx_periodic_adv_sync_transfer(&ev_copy);
ble_gap_rx_periodic_adv_sync_transfer(ev, subevent);
#endif
return 0;
+26
View File
@@ -206,6 +206,32 @@ ble_hs_periodic_sync_first(void)
return psync;
}
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
/**
* Find first PAwR sync (num_subevents > 0) in the active sync list.
*
* Regular periodic syncs share the same list; callers that need a PAwR
* association must not use ble_hs_periodic_sync_first().
*
* @note Host lock must be held.
*/
struct ble_hs_periodic_sync *
ble_hs_periodic_sync_find_pawr_locked(void)
{
struct ble_hs_periodic_sync *psync;
BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
SLIST_FOREACH(psync, &g_ble_hs_periodic_sync_handles, next) {
if (psync->num_subevents > 0) {
return psync;
}
}
return NULL;
}
#endif
void
ble_hs_periodic_sync_deinit(void)
{
@@ -31,6 +31,10 @@ struct ble_hs_periodic_sync {
uint16_t sync_handle;
ble_addr_t advertiser_addr;
uint8_t adv_sid;
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
/* From Sync Established/Transfer V2; >0 means PAwR sync. */
uint8_t num_subevents;
#endif
ble_gap_event_fn *cb;
void *cb_arg;
@@ -46,6 +50,9 @@ struct ble_hs_periodic_sync *ble_hs_periodic_sync_find_by_handle(uint16_t sync_h
struct ble_hs_periodic_sync *ble_hs_periodic_sync_find(const ble_addr_t *addr,
uint8_t sid);
struct ble_hs_periodic_sync *ble_hs_periodic_sync_first(void);
#if MYNEWT_VAL(BLE_PERIODIC_ADV_WITH_RESPONSES)
struct ble_hs_periodic_sync *ble_hs_periodic_sync_find_pawr_locked(void);
#endif
int ble_hs_periodic_sync_init(void);
void ble_hs_periodic_sync_deinit(void);
#ifdef __cplusplus