Skip to content

Commit 6791da9

Browse files
committed
[nrf noup] bluetooth: att: Allow ATT sent callback after data TX is done
By default, the BLE stack calls sent callback for ATT data when the data is passed to BLE controller for transmission. Enabling this Kconfig option delays calling the sent callback until data transmission is finished by BLE controller (the callback is delayed until receiving the Number of Completed Packets HCI Event). If the ATT sent callback is delayed until data transmission is done by BLE controller, the transmitted buffer may have an additional reference. The reference is used to extend lifetime of the net buffer until the data transmission is confirmed by ACK of the remote. Jira: NCSDK-27422 Jira: NCSDK-28624 Jira: NCSDK-35650 Signed-off-by: Marek Pieta <[email protected]>
1 parent 8638ca1 commit 6791da9

File tree

4 files changed

+78
-8
lines changed

4 files changed

+78
-8
lines changed

subsys/bluetooth/host/Kconfig.gatt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ config BT_ATT_RETRY_ON_SEC_ERR
3838
If an ATT request fails due to insufficient security, the host will
3939
try to elevate the security level and retry the ATT request.
4040

41+
config BT_ATT_SENT_CB_AFTER_TX
42+
bool "Delay ATT sent callback until data transmission is done by controller [EXPERIMENTAL]"
43+
select EXPERIMENTAL
44+
help
45+
By default, the BLE stack calls sent callback for ATT data when the
46+
data is passed to BLE controller for transmission. Enabling this
47+
Kconfig option delays calling the sent callback until data
48+
transmission is finished by BLE controller (the callback is called
49+
upon receiving the Number of Completed Packets HCI Event).
50+
51+
The feature is not available in Zephyr RTOS (it's specific to NCS
52+
Zephyr fork). It is a temporary solution allowing to control flow of
53+
GATT notifications with HID reports for HID use-case.
54+
4155
config BT_EATT
4256
bool "Enhanced ATT Bearers support [EXPERIMENTAL]"
4357
depends on BT_L2CAP_ECRED

subsys/bluetooth/host/att.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,13 @@ static void att_disconnect(struct bt_att_chan *chan)
328328
}
329329
}
330330

331+
static void chan_sent_cb(struct bt_conn *conn, void *user_data, int err)
332+
{
333+
struct net_buf *nb = user_data;
334+
335+
net_buf_unref(nb);
336+
}
337+
331338
/* In case of success the ownership of the buffer is transferred to the stack
332339
* which takes care of releasing it when it completes transmitting to the
333340
* controller.
@@ -421,7 +428,15 @@ static int chan_send(struct bt_att_chan *chan, struct net_buf *buf)
421428

422429
data->att_chan = chan;
423430

424-
err = bt_l2cap_send_pdu(&chan->chan, buf, NULL, NULL);
431+
if (IS_ENABLED(CONFIG_BT_ATT_SENT_CB_AFTER_TX)) {
432+
err = bt_l2cap_send_pdu(&chan->chan, buf, chan_sent_cb, net_buf_ref(buf));
433+
if (err) {
434+
net_buf_unref(buf);
435+
}
436+
} else {
437+
err = bt_l2cap_send_pdu(&chan->chan, buf, NULL, NULL);
438+
}
439+
425440
if (err) {
426441
if (err == -ENOBUFS) {
427442
LOG_ERR("Ran out of TX buffers or contexts.");

subsys/bluetooth/host/conn.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -711,12 +711,29 @@ static int send_buf(struct bt_conn *conn, struct net_buf *buf,
711711

712712
uint16_t frag_len = MIN(conn_mtu(conn), len);
713713

714-
/* Check that buf->ref is 1 or 2. It would be 1 if this
715-
* was the only reference (e.g. buf was removed
716-
* from the conn tx_queue). It would be 2 if the
717-
* tx_data_pull kept it on the tx_queue for segmentation.
714+
/* If ATT sent callback is delayed until data transmission
715+
* is done by BLE controller (CONFIG_BT_ATT_SENT_CB_AFTER_TX),
716+
* the `chan_send` function from `att.c` introduces an additional
717+
* reference. The reference is used to extend lifetime of the net
718+
* buffer until the data transmission is confirmed by ACK of the
719+
* remote (the reference is removed when the TX callback passed
720+
* to `bt_l2cap_send_pdu` is called).
721+
*
722+
* send_buf function can be called multiple times, if buffer
723+
* has to be fragmented over HCI. In that case, the callback
724+
* is provided as an argument only for the last transmitted
725+
* fragment. The `buf->ref == 1` (or 2) check is skipped
726+
* because it's impossible to properly validate number of
727+
* references for the sent fragments if buffers may have the
728+
* additional reference.
729+
*
730+
* Otherwise, check that buf->ref is 1 or 2. It would be 1
731+
* if this was the only reference (e.g. buf was removed from
732+
* the conn tx_queue). It would be 2 if the tx_data_pull
733+
* kept it on the tx_queue for segmentation.
718734
*/
719-
__ASSERT_NO_MSG((buf->ref == 1) || (buf->ref == 2));
735+
__ASSERT_NO_MSG(IS_ENABLED(CONFIG_BT_ATT_SENT_CB_AFTER_TX) || (buf->ref == 1) ||
736+
(buf->ref == 2));
720737

721738
/* The reference is always transferred to the frag, so when
722739
* the frag is destroyed, the parent reference is decremented.

subsys/bluetooth/host/l2cap.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,24 @@ static void l2cap_chan_del(struct bt_l2cap_chan *chan)
273273
* `l2cap_chan_destroy()` as it is not called for fixed channels.
274274
*/
275275
while ((buf = k_fifo_get(&le_chan->tx_queue, K_NO_WAIT))) {
276+
/* If ATT sent callback is delayed until data transmission is done by BLE controller
277+
* (CONFIG_BT_ATT_SENT_CB_AFTER_TX), the `chan_send` function from `att.c`
278+
* introduces an additional reference. The reference is used to extend lifetime of
279+
* the net buffer until the data transmission is confirmed by ACK of the
280+
* remote (the reference is removed when the TX callback passed to
281+
* `bt_l2cap_send_pdu` is called).
282+
*
283+
* The TX callbacks of the buffers removed here are not called, therefore the
284+
* additional reference needs to be removed here.
285+
*/
286+
if (IS_ENABLED(CONFIG_BT_ATT_SENT_CB_AFTER_TX)) {
287+
__ASSERT_NO_MSG(buf->ref <= 2);
288+
289+
if (buf->ref == 2) {
290+
net_buf_unref(buf);
291+
}
292+
}
293+
276294
net_buf_unref(buf);
277295
}
278296

@@ -761,13 +779,19 @@ int bt_l2cap_send_pdu(struct bt_l2cap_le_chan *le_chan, struct net_buf *pdu,
761779
return -ENOTCONN;
762780
}
763781

764-
if (pdu->ref != 1) {
782+
/* If ATT sent callback is delayed until data transmission is done by BLE controller
783+
* (CONFIG_BT_ATT_SENT_CB_AFTER_TX), the `chan_send` function from `att.c` introduces an
784+
* additional reference. The reference is used to extend lifetime of the net buffer until
785+
* the data transmission is confirmed by ACK of the remote (the reference is removed when
786+
* the TX callback passed to `bt_l2cap_send_pdu` is called).
787+
*/
788+
if (pdu->ref > 1 + (cb ? 1 : 0)) {
765789
/* The host may alter the buf contents when fragmenting. Higher
766790
* layers cannot expect the buf contents to stay intact. Extra
767791
* refs suggests a silent data corruption would occur if not for
768792
* this error.
769793
*/
770-
LOG_ERR("Expecting 1 ref, got %d", pdu->ref);
794+
LOG_ERR("Expecting up to %d refs, got %d", cb ? 2 : 1, pdu->ref);
771795
return -EINVAL;
772796
}
773797

0 commit comments

Comments
 (0)