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 8341bfa7f5844ec1fb379efcd5c5935141e9e0a6 Author: Andrzej Kaczmarek <andrzej.kaczma...@codecoup.pl> AuthorDate: Mon May 4 15:56:09 2020 +0200 nimble/ll: Add controller-to-host flow control support --- nimble/controller/include/controller/ble_ll.h | 6 + nimble/controller/include/controller/ble_ll_conn.h | 4 + nimble/controller/src/ble_ll_conn.c | 216 ++++++++++++++++++++- nimble/controller/src/ble_ll_conn_priv.h | 6 + nimble/controller/src/ble_ll_hci.c | 102 +++++++++- nimble/controller/src/ble_ll_supp_cmd.c | 17 +- nimble/controller/syscfg.yml | 7 + nimble/include/nimble/ble.h | 9 +- 8 files changed, 354 insertions(+), 13 deletions(-) diff --git a/nimble/controller/include/controller/ble_ll.h b/nimble/controller/include/controller/ble_ll.h index 47d3ac4..5f9e236 100644 --- a/nimble/controller/include/controller/ble_ll.h +++ b/nimble/controller/include/controller/ble_ll.h @@ -396,6 +396,12 @@ struct ble_dev_addr #define BLE_LL_LLID_DATA_START (2) #define BLE_LL_LLID_CTRL (3) +#define BLE_LL_LLID_IS_CTRL(hdr) \ + (((hdr) & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) +#define BLE_LL_LLID_IS_DATA(hdr) \ + ((((hdr) & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_DATA_START) || \ + (((hdr) & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_DATA_FRAG)) + /* * CONNECT_REQ * -> InitA (6 bytes) diff --git a/nimble/controller/include/controller/ble_ll_conn.h b/nimble/controller/include/controller/ble_ll_conn.h index 94ea42c..8c07462 100644 --- a/nimble/controller/include/controller/ble_ll_conn.h +++ b/nimble/controller/include/controller/ble_ll_conn.h @@ -272,6 +272,10 @@ struct ble_ll_conn_sm uint8_t last_rxd_hdr_byte; /* note: possibly can make 1 bit since we only use the MD bit now */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + uint16_t cth_flow_pending; +#endif + /* connection event mgmt */ uint8_t reject_reason; uint8_t host_reply_opcode; diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c index c5a2a41..8799ff2 100644 --- a/nimble/controller/src/ble_ll_conn.c +++ b/nimble/controller/src/ble_ll_conn.c @@ -225,6 +225,166 @@ STATS_NAME_END(ble_ll_conn_stats) static void ble_ll_conn_event_end(struct ble_npl_event *ev); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) +struct ble_ll_conn_cth_flow { + bool enabled; + uint16_t max_buffers; + uint16_t num_buffers; +}; + +static struct ble_ll_conn_cth_flow g_ble_ll_conn_cth_flow; + +static struct ble_npl_event g_ble_ll_conn_cth_flow_error_ev; + +static bool +ble_ll_conn_cth_flow_is_enabled(void) +{ + return g_ble_ll_conn_cth_flow.enabled; +} + +static bool +ble_ll_conn_cth_flow_alloc_credit(struct ble_ll_conn_sm *connsm) +{ + struct ble_ll_conn_cth_flow *cth = &g_ble_ll_conn_cth_flow; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + if (!cth->num_buffers) { + OS_EXIT_CRITICAL(sr); + return false; + } + + connsm->cth_flow_pending++; + cth->num_buffers--; + + OS_EXIT_CRITICAL(sr); + + return true; +} + +static void +ble_ll_conn_cth_flow_free_credit(struct ble_ll_conn_sm *connsm, uint16_t credits) +{ + struct ble_ll_conn_cth_flow *cth = &g_ble_ll_conn_cth_flow; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + /* + * It's not quite clear what we should do if host gives back more credits + * that we have allocated. For now let's just set invalid values back to + * sane values and continue. + */ + + cth->num_buffers += credits; + if (cth->num_buffers > cth->max_buffers) { + cth->num_buffers = cth->max_buffers; + } + + if (connsm->cth_flow_pending < credits) { + connsm->cth_flow_pending = 0; + } else { + connsm->cth_flow_pending -= credits; + } + + OS_EXIT_CRITICAL(sr); +} + +static void +ble_ll_conn_cth_flow_error_fn(struct ble_npl_event *ev) +{ + struct ble_hci_ev *hci_ev; + struct ble_hci_ev_command_complete *hci_ev_cp; + uint16_t opcode; + + hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!hci_ev) { + /* Not much we can do anyway... */ + return; + } + + /* + * We are here in case length of HCI_Host_Number_Of_Completed_Packets was + * invalid. We will send an error back to host and we can only hope host is + * reasonable and will do some actions to recover, e.g. it should disconnect + * all connections to guarantee that all credits are back in pool and we're + * back in sync (although spec does not really say what should happen). + */ + + opcode = BLE_HCI_OP(BLE_HCI_OGF_CTLR_BASEBAND, + BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS); + + hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_COMPLETE; + hci_ev->length = sizeof(*hci_ev_cp); + + hci_ev_cp = (void *)hci_ev->data; + hci_ev_cp->num_packets = BLE_LL_CFG_NUM_HCI_CMD_PKTS; + hci_ev_cp->opcode = htole16(opcode); + hci_ev_cp->status = BLE_ERR_INV_HCI_CMD_PARMS; + + ble_ll_hci_event_send(hci_ev); +} + +void +ble_ll_conn_cth_flow_set_buffers(uint16_t num_buffers) +{ + BLE_LL_ASSERT(num_buffers); + + g_ble_ll_conn_cth_flow.max_buffers = num_buffers; + g_ble_ll_conn_cth_flow.num_buffers = num_buffers; +} + +bool +ble_ll_conn_cth_flow_enable(bool enabled) +{ + struct ble_ll_conn_cth_flow *cth = &g_ble_ll_conn_cth_flow; + + if (cth->enabled == enabled) { + return true; + } + + if (!SLIST_EMPTY(&g_ble_ll_conn_active_list)) { + return false; + } + + cth->enabled = enabled; + + return true; +} + +void +ble_ll_conn_cth_flow_process_cmd(const uint8_t *cmdbuf) +{ + const struct ble_hci_cmd *cmd; + const struct ble_hci_cb_host_num_comp_pkts_cp *cp; + struct ble_ll_conn_sm *connsm; + int i; + + cmd = (const void *)cmdbuf; + cp = (const void *)cmd->data; + + if (cmd->length != sizeof(cp->handles) + cp->handles * sizeof(cp->h[0])) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_conn_cth_flow_error_ev); + return; + } + + for (i = 0; i < cp->handles; i++) { + /* + * It's probably ok that we do not have active connection with given + * handle - this can happen if disconnection already happened in LL but + * host sent credits back before processing disconnection event. In such + * case we can simply ignore command for that connection since credits + * are returned by LL already. + */ + connsm = ble_ll_conn_find_active_conn(cp->h[i].handle); + if (connsm) { + ble_ll_conn_cth_flow_free_credit(connsm, cp->h[i].count); + } + } +} +#endif + #if (BLE_LL_BT5_PHY_SUPPORTED == 1) /** * Checks to see if we should start a PHY update procedure @@ -1861,6 +2021,10 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) /* Remove from the active connection list */ SLIST_REMOVE(&g_ble_ll_conn_active_list, connsm, ble_ll_conn_sm, act_sle); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_HOST_TO_CTRL_FLOW_CONTROL) + ble_ll_conn_cth_flow_free_credit(connsm, connsm->cth_flow_pending); +#endif + /* Free the current transmit pdu if there is one. */ if (connsm->cur_tx_pdu) { os_mbuf_free_chain(connsm->cur_tx_pdu); @@ -3577,6 +3741,13 @@ ble_ll_conn_rx_data_pdu(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) /* Free buffer */ conn_rx_data_pdu_end: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + /* Need to give credit back if we allocated one for this PDU */ + if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_CONN_CREDIT) { + ble_ll_conn_cth_flow_free_credit(connsm, 1); + } +#endif + os_mbuf_free_chain(rxpdu); } @@ -3597,7 +3768,6 @@ int ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) { int rc; - int is_ctrl; uint8_t hdr_byte; uint8_t hdr_sn; uint8_t hdr_nesn; @@ -3616,6 +3786,9 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) int rx_phy_mode; bool alloc_rxpdu = true; + rc = -1; + connsm = g_ble_ll_conn_cur_sm; + /* Retrieve the header and payload length */ hdr_byte = rxbuf[0]; rx_pyld_len = rxbuf[1]; @@ -3628,6 +3801,23 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) alloc_rxpdu = false; } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + /* + * If flow control is enabled, we need to have credit available for each + * non-empty data packet that LL may send to host. If there are no credits + * available, we don't need to allocate buffer for this packet so LL will + * nak it. + */ + if (ble_ll_conn_cth_flow_is_enabled() && + BLE_LL_LLID_IS_DATA(hdr_byte) && (rx_pyld_len > 0)) { + if (ble_ll_conn_cth_flow_alloc_credit(connsm)) { + rxhdr->rxinfo.flags |= BLE_MBUF_HDR_F_CONN_CREDIT; + } else { + alloc_rxpdu = false; + } + } +#endif + /* * We need to attempt to allocate a buffer here. The reason we do this * now is that we should not ack the packet if we have no receive @@ -3643,8 +3833,6 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) * We should have a current connection state machine. If we dont, we just * hand the packet to the higher layer to count it. */ - rc = -1; - connsm = g_ble_ll_conn_cur_sm; if (!connsm) { STATS_INC(ble_ll_conn_stats, rx_data_pdu_no_conn); goto conn_exit; @@ -3706,9 +3894,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) /* Set last received header byte */ connsm->last_rxd_hdr_byte = hdr_byte; - is_ctrl = 0; - if ((hdr_byte & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) { - is_ctrl = 1; + if (BLE_LL_LLID_IS_CTRL(hdr_byte)) { opcode = rxbuf[2]; } @@ -3797,7 +3983,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) /* Adjust payload for max TX time and octets */ #if (BLE_LL_BT5_PHY_SUPPORTED == 1) - if (is_ctrl && + if (BLE_LL_LLID_IS_CTRL(hdr_byte) && (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && (opcode == BLE_LL_CTRL_PHY_UPDATE_IND)) { connsm->phy_tx_transition = @@ -3816,8 +4002,9 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) /* If this is a TERMINATE_IND, we have to reply */ chk_rx_terminate_ind: /* If we received a terminate IND, we must set some flags */ - if (is_ctrl && (opcode == BLE_LL_CTRL_TERMINATE_IND) - && (rx_pyld_len == (1 + BLE_LL_CTRL_TERMINATE_IND_LEN))) { + if (BLE_LL_LLID_IS_CTRL(hdr_byte) && + (opcode == BLE_LL_CTRL_TERMINATE_IND) && + (rx_pyld_len == (1 + BLE_LL_CTRL_TERMINATE_IND_LEN))) { connsm->csmflags.cfbit.terminate_ind_rxd = 1; connsm->rxd_disconnect_reason = rxbuf[3]; } @@ -4239,6 +4426,12 @@ ble_ll_conn_module_reset(void) g_ble_ll_conn_sync_transfer_params.mode = 0; g_ble_ll_conn_sync_transfer_params.sync_timeout_us = 0; #endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + g_ble_ll_conn_cth_flow.enabled = false; + g_ble_ll_conn_cth_flow.max_buffers = 1; + g_ble_ll_conn_cth_flow.num_buffers = 1; +#endif } /* Initialize the connection module */ @@ -4278,6 +4471,11 @@ ble_ll_conn_module_init(void) "ble_ll_conn"); BLE_LL_ASSERT(rc == 0); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + ble_npl_event_init(&g_ble_ll_conn_cth_flow_error_ev, + ble_ll_conn_cth_flow_error_fn, NULL); +#endif + /* Call reset to finish reset of initialization */ ble_ll_conn_module_reset(); } diff --git a/nimble/controller/src/ble_ll_conn_priv.h b/nimble/controller/src/ble_ll_conn_priv.h index 9891bfa..53358c4 100644 --- a/nimble/controller/src/ble_ll_conn_priv.h +++ b/nimble/controller/src/ble_ll_conn_priv.h @@ -207,6 +207,12 @@ void ble_ll_conn_auth_pyld_timer_start(struct ble_ll_conn_sm *connsm); #define ble_ll_conn_auth_pyld_timer_start(x) #endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) +void ble_ll_conn_cth_flow_set_buffers(uint16_t num_buffers); +bool ble_ll_conn_cth_flow_enable(bool enabled); +void ble_ll_conn_cth_flow_process_cmd(const uint8_t *cmdbuf); +#endif + int ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg); int ble_ll_hci_acl_rx(struct os_mbuf *om, void *arg); diff --git a/nimble/controller/src/ble_ll_hci.c b/nimble/controller/src/ble_ll_hci.c index c42e24d..a3da98d 100644 --- a/nimble/controller/src/ble_ll_hci.c +++ b/nimble/controller/src/ble_ll_hci.c @@ -1343,6 +1343,64 @@ ble_ll_hci_cb_set_event_mask(const uint8_t *cmdbuf, uint8_t len) return BLE_ERR_SUCCESS; } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) +static int +ble_ll_hci_cb_set_ctrlr_to_host_fc(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_cb_ctlr_to_host_fc_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof (*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* We only allow to either disable flow control or enable for ACL only */ + if (cmd->enable > 1) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!ble_ll_conn_cth_flow_enable(cmd->enable)) { + return BLE_ERR_CMD_DISALLOWED; + } + + return BLE_ERR_SUCCESS; +} + +static int +ble_ll_hci_cb_host_buf_size(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_cb_host_buf_size_cp *cmd = (const void *) cmdbuf; + uint16_t acl_num; + uint16_t acl_data_len; + + if (len != sizeof (*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* We do not support SCO so those parameters should be set to 0 */ + if (cmd->sco_num || cmd->sco_data_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* + * Core 5.2 Vol 4 Part E section 7.3.39 states that "Both the Host and the + * Controller shall support command and event packets, where the data portion + * (excluding header) contained in the packets is 255 octets in size.". + * This means we can basically accept any allowed value since LL does not + * reassemble incoming data thus will not send more than 255 octets in single + * data packet. + */ + acl_num = le16toh(cmd->acl_num); + acl_data_len = le16toh(cmd->acl_data_len); + if (acl_data_len < 255) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + ble_ll_conn_cth_flow_set_buffers(acl_num); + + return BLE_ERR_SUCCESS; +} +#endif + static int ble_ll_hci_cb_set_event_mask2(const uint8_t *cmdbuf, uint8_t len) { @@ -1375,6 +1433,22 @@ ble_ll_hci_ctlr_bb_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf, rc = ble_ll_reset(); } break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + case BLE_HCI_OCF_CB_SET_CTLR_TO_HOST_FC: + rc = ble_ll_hci_cb_set_ctrlr_to_host_fc(cmdbuf, len); + break; + case BLE_HCI_OCF_CB_HOST_BUF_SIZE: + rc = ble_ll_hci_cb_host_buf_size(cmdbuf, len); + break; + case BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS: + /* + * HCI_Host_Number_Of_Completed_Packets is handled immediately when + * received from transport so we should never receive it here. + */ + BLE_LL_ASSERT(0); + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; +#endif case BLE_HCI_OCF_CB_SET_EVENT_MASK2: rc = ble_ll_hci_cb_set_event_mask2(cmdbuf, len); break; @@ -1570,9 +1644,33 @@ ble_ll_hci_cmd_proc(struct ble_npl_event *ev) * BLE_ERR_MEM_CAPACITY on HCI buffer exhaustion. */ int -ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg) +ble_ll_hci_cmd_rx(uint8_t *cmdbuf, void *arg) { struct ble_npl_event *ev; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) + const struct ble_hci_cmd *cmd; + uint16_t opcode; + uint16_t ocf; + uint16_t ogf; + + cmd = (const void *)cmdbuf; + opcode = le16toh(cmd->opcode); + ogf = BLE_HCI_OGF(opcode); + ocf = BLE_HCI_OCF(opcode); + + /* + * HCI_Host_Number_Of_Completed_Packets is processed outside standard flow + * thus it can be sent at any time, even if another command is already + * pending. This means we should better process it here and send an event to + * LL in case of error. + */ + if ((ogf == BLE_HCI_OGF_CTLR_BASEBAND) && + (ocf == BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS)) { + ble_ll_conn_cth_flow_process_cmd(cmdbuf); + ble_hci_trans_buf_free(cmdbuf); + return 0; + } +#endif /* Get an event structure off the queue */ ev = &g_ble_ll_hci_cmd_ev; @@ -1581,7 +1679,7 @@ ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg) } /* Fill out the event and post to Link Layer */ - ble_npl_event_set_arg(ev, cmd); + ble_npl_event_set_arg(ev, cmdbuf); ble_npl_eventq_put(&g_ble_ll_data.ll_evq, ev); return 0; diff --git a/nimble/controller/src/ble_ll_supp_cmd.c b/nimble/controller/src/ble_ll_supp_cmd.c index 863e698..cae9eb7 100644 --- a/nimble/controller/src/ble_ll_supp_cmd.c +++ b/nimble/controller/src/ble_ll_supp_cmd.c @@ -36,7 +36,22 @@ /* Octet 10 */ #define BLE_SUPP_CMD_RD_TX_PWR (0 << 2) -#define BLE_LL_SUPP_CMD_OCTET_10 (BLE_SUPP_CMD_RD_TX_PWR) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL) +#define BLE_SUPP_CMD_SET_CTRL_TO_HOST_FLOW (1 << 5) +#define BLE_SUPP_CMD_HOST_BUFFER_SIZE (1 << 6) +#define BLE_SUPP_CMD_HOST_NUM_COMP_PACKETS (1 << 7) +#else +#define BLE_SUPP_CMD_SET_CTRL_TO_HOST_FLOW (0 << 5) +#define BLE_SUPP_CMD_HOST_BUFFER_SIZE (0 << 6) +#define BLE_SUPP_CMD_HOST_NUM_COMP_PACKETS (0 << 7) +#endif +#define BLE_LL_SUPP_CMD_OCTET_10 \ +( \ + BLE_SUPP_CMD_RD_TX_PWR | \ + BLE_SUPP_CMD_SET_CTRL_TO_HOST_FLOW | \ + BLE_SUPP_CMD_HOST_BUFFER_SIZE | \ + BLE_SUPP_CMD_HOST_NUM_COMP_PACKETS \ +) /* Octet 14 */ #define BLE_SUPP_CMD_RD_LOC_VER (1 << 3) diff --git a/nimble/controller/syscfg.yml b/nimble/controller/syscfg.yml index ea04388..2c7c2cb 100644 --- a/nimble/controller/syscfg.yml +++ b/nimble/controller/syscfg.yml @@ -260,6 +260,13 @@ syscfg.defs: Advertising Sync Transfer Feature. value: MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL: + description: > + Enable controller-to-host flow control support. This allows host to + limit number of ACL packets sent at once from controller to avoid + congestion on HCI transport if feature is also supported by host. + value: 0 + BLE_LL_CFG_FEAT_LL_SCA_UPDATE: description: > This option is used to enable/disable support for SCA update procedure diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h index 3fc2902..248e8ae 100644 --- a/nimble/include/nimble/ble.h +++ b/nimble/include/nimble/ble.h @@ -81,7 +81,14 @@ struct ble_mbuf_hdr_rxinfo #endif }; -/* Flag definitions for rxinfo */ +/* + * Flag definitions for rxinfo + * + * Note: it's ok to have symbols with the same values as long as they cannot be + * set for the same PDU (e.g. one use by scanner, other one used by + * connection) + */ +#define BLE_MBUF_HDR_F_CONN_CREDIT (0x8000) #define BLE_MBUF_HDR_F_IGNORED (0x8000) #define BLE_MBUF_HDR_F_SCAN_REQ_TXD (0x4000) #define BLE_MBUF_HDR_F_INITA_RESOLVED (0x2000)