Bluetooth: Host: Added Recycled evt notifying conn object is available

- Which allow listeners to attempt to use the freed connection object to
perform actions as e.g: start connectable advertisements.
- Refactored bt_conn_unref() so it does not access conn struct after
decrementing its reference count.

Signed-off-by: Luis Ubieda <luisf@croxel.com>
This commit is contained in:
Luis Ubieda
2024-01-29 16:31:50 -05:00
committed by Fabio Baltieri
parent 4c88deaa82
commit efb5d8372d
2 changed files with 59 additions and 3 deletions

View File

@@ -1000,6 +1000,20 @@ struct bt_conn_cb {
*/
void (*disconnected)(struct bt_conn *conn, uint8_t reason);
/** @brief A connection object has been returned to the pool.
*
* This callback notifies the application that it might be able to
* allocate a connection object. No guarantee, first come, first serve.
*
* Use this to e.g. re-start connectable advertising or scanning.
*
* Treat this callback as an ISR, as it originates from
* @ref bt_conn_unref which is used by the BT stack. Making
* Bluetooth API calls in this context is error-prone and strongly
* discouraged.
*/
void (*recycled)(void);
/** @brief LE connection parameter update request.
*
* This callback notifies the application that a remote device

View File

@@ -85,6 +85,8 @@ static void conn_tx_destroy(struct bt_conn *conn, struct bt_conn_tx *tx)
static void tx_complete_work(struct k_work *work);
#endif /* CONFIG_BT_CONN_TX */
static void notify_recycled_conn_slot(void);
/* Group Connected BT_CONN only in this */
#if defined(CONFIG_BT_CONN)
/* Peripheral timeout to initialize Connection Parameter Update procedure */
@@ -1311,15 +1313,38 @@ struct bt_conn *bt_conn_ref(struct bt_conn *conn)
void bt_conn_unref(struct bt_conn *conn)
{
atomic_val_t old;
bool deallocated;
enum bt_conn_type conn_type;
uint8_t conn_role;
uint16_t conn_handle;
__ASSERT(conn, "Invalid connection reference");
/* Storing parameters of interest so we don't access the object
* after decrementing its ref-count
*/
conn_type = conn->type;
conn_role = conn->role;
conn_handle = conn->handle;
old = atomic_dec(&conn->ref);
/* Prevent from accessing connection object */
conn = NULL;
deallocated = (atomic_get(&old) == 1);
LOG_DBG("handle %u ref %ld -> %ld", conn->handle, old, atomic_get(&conn->ref));
LOG_DBG("handle %u ref %ld -> %ld", conn_handle, old, (old - 1));
__ASSERT(old > 0, "Conn reference counter is 0");
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn->type == BT_CONN_TYPE_LE &&
conn->role == BT_CONN_ROLE_PERIPHERAL && atomic_get(&conn->ref) == 0) {
/* Slot has been freed and can be taken. No guarantees are made on requests
* to claim connection object as only the first claim will be served.
*/
if (deallocated) {
notify_recycled_conn_slot();
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn_type == BT_CONN_TYPE_LE &&
conn_role == BT_CONN_ROLE_PERIPHERAL && deallocated) {
bt_le_adv_resume();
}
}
@@ -1431,6 +1456,23 @@ static void tx_complete_work(struct k_work *work)
}
#endif /* CONFIG_BT_CONN_TX */
static void notify_recycled_conn_slot(void)
{
#if defined(CONFIG_BT_CONN)
for (struct bt_conn_cb *cb = callback_list; cb; cb = cb->_next) {
if (cb->recycled) {
cb->recycled();
}
}
STRUCT_SECTION_FOREACH(bt_conn_cb, cb) {
if (cb->recycled) {
cb->recycled();
}
}
#endif
}
/* Group Connected BT_CONN only in this */
#if defined(CONFIG_BT_CONN)