This is an automated email from the ASF dual-hosted git repository. andk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit e6f0aa1243f6dbce21977f37203138efc01f1ae4 Author: Andrzej Kaczmarek <[email protected]> AuthorDate: Thu Feb 20 18:04:30 2025 +0100 nimble/ll: Fix transmitting fragmented HCI ACL data For whatever reason HCI ACL packets with empty payload are allowed, but at the same time we are not allowed to send and empty fragment with LLID=0b00 over the air. We need to wait for the 1st non-empty payload and "recombine" it with the empty payload(s) to form a proper payload with LLID=0b00. We don't really need to recombine payloads, instead we can just count and then discard initial empty payloads and the update non-empty payload accordingly. See Core 6.0, Vol 6, Part B, 2.4.1. --- nimble/controller/include/controller/ble_ll_conn.h | 1 + nimble/controller/src/ble_ll.c | 4 +-- nimble/controller/src/ble_ll_conn.c | 37 ++++++++++++++++++++-- nimble/controller/src/ble_ll_dtm.c | 2 +- nimble/include/nimble/ble.h | 2 +- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/nimble/controller/include/controller/ble_ll_conn.h b/nimble/controller/include/controller/ble_ll_conn.h index 1d29063bf..c0ba84afa 100644 --- a/nimble/controller/include/controller/ble_ll_conn.h +++ b/nimble/controller/include/controller/ble_ll_conn.h @@ -344,6 +344,7 @@ struct ble_ll_conn_sm /* Packet transmit queue */ struct os_mbuf *cur_tx_pdu; STAILQ_HEAD(conn_txq_head, os_mbuf_pkthdr) conn_txq; + uint8_t conn_txq_num_zero_pkt; /* List entry for active/free connection pools */ union { diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c index 4e0a74c53..8c22c89fb 100644 --- a/nimble/controller/src/ble_ll.c +++ b/nimble/controller/src/ble_ll.c @@ -847,7 +847,7 @@ ble_ll_tx_pkt_in(void) /* Do some basic error checking */ pb = handle & 0x3000; - if ((pkthdr->omp_len != length) || (pb > 0x1000) || (length == 0)) { + if ((pkthdr->omp_len != length) || (pb > 0x1000)) { /* This is a bad ACL packet. Count a stat and free it */ STATS_INC(ble_ll_stats, bad_acl_hdr); os_mbuf_free_chain(om); @@ -1548,7 +1548,7 @@ ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr) /* Set BLE transmit header */ ble_hdr = BLE_MBUF_HDR_PTR(m); - ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.num_data_pkt = 0; ble_hdr->txinfo.offset = 0; ble_hdr->txinfo.pyld_len = pdulen; ble_hdr->txinfo.hdr_byte = hdr; diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c index 65ff4cb0d..dcaa3aae5 100644 --- a/nimble/controller/src/ble_ll_conn.c +++ b/nimble/controller/src/ble_ll_conn.c @@ -1301,7 +1301,7 @@ conn_tx_pdu: m->om_data = (uint8_t *)&empty_pdu; m->om_data += BLE_MBUF_MEMBLOCK_OVERHEAD; ble_hdr = &empty_pdu.ble_hdr; - ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.num_data_pkt = 0; ble_hdr->txinfo.offset = 0; ble_hdr->txinfo.pyld_len = 0; } @@ -2034,6 +2034,7 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm) /* Initialize transmit queue and ack/flow control elements */ STAILQ_INIT(&connsm->conn_txq); + connsm->conn_txq_num_zero_pkt = 0; connsm->cur_tx_pdu = NULL; connsm->tx_seqnum = 0; connsm->next_exp_seqnum = 0; @@ -3845,7 +3846,8 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) #if (BLETEST_THROUGHPUT_TEST == 1) bletest_completed_pkt(connsm->conn_handle); #endif - ++connsm->completed_pkts; + BLE_LL_ASSERT(txhdr->txinfo.num_data_pkt >= 1); + connsm->completed_pkts += txhdr->txinfo.num_data_pkt; if (connsm->completed_pkts > 2) { ble_ll_event_add(&g_ble_ll_data.ll_comp_pkt_ev); } @@ -3946,17 +3948,46 @@ ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om, os_sr_t sr; struct os_mbuf_pkthdr *pkthdr; struct ble_mbuf_hdr *ble_hdr; + uint8_t num_pkt; int lifo; + /* We cannot send empty payload with LLID=0b10. Instead, we need to wait for + * non-empty payload and combine it together. Since we don't really recombine + * fragments we just count number of consecutive empty payloads and use 1st + * non-empty as a start packet. Empty payloads can be freed immediately as + * we don't need to enqueue them. + * + * Reference: Core 6.0, Vol 6, Part B, 2.4.1 + */ + if ((length == 0) && + ((hdr_byte == BLE_LL_LLID_DATA_START) || (connsm->conn_txq_num_zero_pkt > 0))) { + connsm->conn_txq_num_zero_pkt++; + os_mbuf_free_chain(om); + return; + } + /* Set mbuf length and packet length if a control PDU */ if (hdr_byte == BLE_LL_LLID_CTRL) { om->om_len = length; OS_MBUF_PKTHDR(om)->omp_len = length; + num_pkt = 0; + } else { + num_pkt = 1; + + /* This is the 1st non-empty data fragment so adjust LLID accordingly. + * num_pkt is updated to make sure we send back proper numbber of + * completed packets back to host. + */ + if (connsm->conn_txq_num_zero_pkt) { + hdr_byte = BLE_LL_LLID_DATA_START; + num_pkt += connsm->conn_txq_num_zero_pkt; + connsm->conn_txq_num_zero_pkt = 0; + } } /* Set BLE transmit header */ ble_hdr = BLE_MBUF_HDR_PTR(om); - ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.num_data_pkt = num_pkt; ble_hdr->txinfo.offset = 0; ble_hdr->txinfo.hdr_byte = hdr_byte; diff --git a/nimble/controller/src/ble_ll_dtm.c b/nimble/controller/src/ble_ll_dtm.c index c57a1357a..c7823e0f1 100644 --- a/nimble/controller/src/ble_ll_dtm.c +++ b/nimble/controller/src/ble_ll_dtm.c @@ -308,7 +308,7 @@ ble_ll_dtm_tx_create_ctx(uint8_t packet_payload, uint8_t len, /* Set BLE transmit header */ ble_hdr = BLE_MBUF_HDR_PTR(m); - ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.num_data_pkt = 0; ble_hdr->txinfo.offset = 0; ble_hdr->txinfo.pyld_len = len; ble_hdr->txinfo.hdr_byte = packet_payload; diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h index c42d5c5ba..a2add5d79 100644 --- a/nimble/include/nimble/ble.h +++ b/nimble/include/nimble/ble.h @@ -120,7 +120,7 @@ struct ble_mbuf_hdr_rxinfo /* Transmit info. NOTE: no flags defined */ struct ble_mbuf_hdr_txinfo { - uint8_t flags; + uint8_t num_data_pkt; uint8_t hdr_byte; uint16_t offset; uint16_t pyld_len;
