From 8e1ad2bcfc6044d73dbe2bf2757b333cb50eb908 Mon Sep 17 00:00:00 2001 From: Szymon Czapracki Date: Thu, 27 Feb 2025 13:32:14 +0100 Subject: [PATCH] [WIP]host/eatt: Collision mitigation Handle collision mitigation --- apps/bttester/src/btp/btp_gatt.h | 6 +++ apps/bttester/src/btp_gatt.c | 21 +++++++++ apps/bttester/syscfg.yml | 2 +- nimble/host/include/host/ble_att.h | 2 + nimble/host/src/ble_eatt.c | 72 +++++++++++++++++++++++++----- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/apps/bttester/src/btp/btp_gatt.h b/apps/bttester/src/btp/btp_gatt.h index 8da6f43df8..b5171531a0 100644 --- a/apps/bttester/src/btp/btp_gatt.h +++ b/apps/bttester/src/btp/btp_gatt.h @@ -320,6 +320,12 @@ struct btp_gatt_set_mult_val_cmd { uint8_t data[0]; } __packed; +#define BTP_GATT_EATT_CONNECT 0x1f +struct btp_gatt_eatt_conn_cmd { + ble_addr_t address; + uint8_t num_channels; +} __packed; + /* GATT events */ #define BTP_GATT_EV_NOTIFICATION 0x80 struct btp_gatt_notification_ev { diff --git a/apps/bttester/src/btp_gatt.c b/apps/bttester/src/btp_gatt.c index 157e7da1cb..cceefce842 100644 --- a/apps/bttester/src/btp_gatt.c +++ b/apps/bttester/src/btp_gatt.c @@ -1950,6 +1950,21 @@ set_mult(const void *cmd, uint16_t cmd_len, return BTP_STATUS_SUCCESS; } +static uint8_t +eatt_conn(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + uint16_t conn_handle; + const struct btp_gatt_eatt_conn_cmd *cp = cmd; + + ble_gap_conn_find_handle_by_addr(&cp->address, &conn_handle); + + ble_eatt_connect(conn_handle, cp->num_channels); + + return BTP_STATUS_SUCCESS; +} + + static uint8_t change_database(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) @@ -2001,6 +2016,7 @@ supported_commands(const void *cmd, uint16_t cmd_len, tester_set_bit(rp->data, BTP_GATT_GET_ATTRIBUTES); tester_set_bit(rp->data, BTP_GATT_GET_ATTRIBUTE_VALUE); tester_set_bit(rp->data, BTP_GATT_CHANGE_DATABASE); + tester_set_bit(rp->data, BTP_GATT_EATT_CONNECT); *rsp_len = sizeof(*rp) + 4; @@ -2137,6 +2153,11 @@ static const struct btp_handler handlers[] = { .expect_len = BTP_HANDLER_LENGTH_VARIABLE, .func = set_mult, }, + { + .opcode = BTP_GATT_EATT_CONNECT, + .expect_len = sizeof(struct btp_gatt_eatt_conn_cmd), + .func = eatt_conn, + } }; int diff --git a/apps/bttester/syscfg.yml b/apps/bttester/syscfg.yml index 1be2945575..01a50f1ac7 100644 --- a/apps/bttester/syscfg.yml +++ b/apps/bttester/syscfg.yml @@ -123,7 +123,7 @@ syscfg.vals.!BTTESTER_NODEFAULT: BLE_L2CAP_COC_MAX_NUM: 5 BLE_L2CAP_SIG_MAX_PROCS: 2 BLE_L2CAP_ENHANCED_COC: 1 - BLE_EATT_CHAN_NUM: 5 + BLE_EATT_CHAN_NUM: 1 # Some testcases require MPS < MTU BLE_L2CAP_COC_MPS: 100 BLE_RPA_TIMEOUT: 30 diff --git a/nimble/host/include/host/ble_att.h b/nimble/host/include/host/ble_att.h index 8323c9d764..d06b4b25bc 100644 --- a/nimble/host/include/host/ble_att.h +++ b/nimble/host/include/host/ble_att.h @@ -355,6 +355,8 @@ uint16_t ble_att_preferred_mtu(void); */ int ble_att_set_preferred_mtu(uint16_t mtu); +void ble_eatt_connect(uint16_t conn_handle, uint16_t num_channels); + #ifdef __cplusplus } #endif diff --git a/nimble/host/src/ble_eatt.c b/nimble/host/src/ble_eatt.c index 3ec9bd3b8e..45dff1d986 100644 --- a/nimble/host/src/ble_eatt.c +++ b/nimble/host/src/ble_eatt.c @@ -50,6 +50,9 @@ SLIST_HEAD(ble_eatt_list, ble_eatt); static struct ble_eatt_list g_ble_eatt_list; static ble_eatt_att_rx_fn ble_eatt_att_rx_cb; +static uint8_t retry_cnt; +static uint8_t prev_conn_result; + #define BLE_EATT_DATABUF_SIZE ( \ MYNEWT_VAL(BLE_EATT_MTU) + \ 2 + \ @@ -214,6 +217,38 @@ ble_eatt_free(struct ble_eatt *eatt) os_memblock_put(&ble_eatt_conn_pool, eatt); } +static void +ble_eatt_retry(uint16_t conn_handle, struct ble_eatt *eatt) +{ + struct ble_gap_conn_desc desc; + int rc; + + if (retry_cnt > 2) { + ble_eatt_free(eatt); + return; + } + + rc = ble_gap_conn_find(conn_handle, &desc); + assert(rc == 0); + + /* + * 5.3 Vol 3, Part G, Sec. 5.4 L2CAP collision mitigation + * Peripheral shall wait some time before retrying connection + * We are waiting here for 500 ms + */ + if (desc.role == BLE_GAP_ROLE_SLAVE) { + BLE_EATT_LOG_DEBUG("eatt: Collision - wait before reconnect\n"); + os_time_delay(500 * OS_TICKS_PER_SEC / 1000); + } + + eatt->conn_handle = conn_handle; + + retry_cnt++; + + /* Setup EATT */ + ble_npl_eventq_put(ble_hs_evq_get(), &eatt->setup_ev); +} + static int ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg) { @@ -225,9 +260,15 @@ ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg) switch (event->type) { case BLE_L2CAP_EVENT_COC_CONNECTED: BLE_EATT_LOG_DEBUG("eatt: Connected \n"); - if (event->connect.status) { - ble_eatt_free(eatt); + BLE_EATT_LOG_DEBUG("eatt: Conneted status: %d \n", event->connect.status); + if (event->connect.status == 6 && + prev_conn_result == BLE_L2CAP_COC_ERR_NO_RESOURCES) { + BLE_EATT_LOG_DEBUG("eatt: Limited resources error\n"); + prev_conn_result = BLE_L2CAP_COC_ERR_NO_RESOURCES; + ble_eatt_retry(event->connect.conn_handle, eatt); return 0; + } else if (event->connect.status) { + ble_eatt_free(eatt); } eatt->chan = event->connect.chan; break; @@ -239,22 +280,25 @@ ble_eatt_l2cap_event_fn(struct ble_l2cap_event *event, void *arg) BLE_EATT_LOG_DEBUG("eatt: Accept request\n"); eatt = ble_eatt_find_by_conn_handle(event->accept.conn_handle); if (eatt) { + BLE_EATT_LOG_DEBUG("eatt: Accept no mem 1\n"); /* For now we accept only one additional coc channel per ACL * TODO: improve it */ + prev_conn_result = BLE_L2CAP_COC_ERR_NO_RESOURCES; return BLE_HS_ENOMEM; } eatt = ble_eatt_alloc(); if (!eatt) { + BLE_EATT_LOG_DEBUG("eatt: Accept no mem 2\n"); return BLE_HS_ENOMEM; } - eatt->conn_handle = event->accept.conn_handle; event->accept.chan->cb_arg = eatt; rc = ble_eatt_prepare_rx_sdu(event->accept.chan); if (rc) { + BLE_EATT_LOG_DEBUG("eatt: Accept no mem 3\n"); ble_eatt_free(eatt); return rc; } @@ -341,7 +385,9 @@ ble_eatt_setup_cb(struct ble_npl_event *ev) BLE_EATT_LOG_ERROR("eatt: Failed to connect EATT on conn_handle 0x%04x (status=%d)\n", eatt->conn_handle, rc); os_mbuf_free_chain(om); - ble_eatt_free(eatt); + if (rc != BLE_L2CAP_COC_ERR_NO_RESOURCES) { + ble_eatt_free(eatt); + } } } @@ -509,16 +555,14 @@ ble_eatt_start(uint16_t conn_handle) rc = ble_gap_conn_find(conn_handle, &desc); assert(rc == 0); - if (desc.role != BLE_GAP_ROLE_MASTER) { - /* Let master to create ecoc. - * TODO: Slave could setup after some timeout - */ - return; - } - eatt = ble_eatt_alloc(); + eatt = ble_eatt_find_by_conn_handle(conn_handle); if (!eatt) { + eatt = ble_eatt_alloc(); + if (!eatt) { + BLE_EATT_LOG_ERROR("eatt: %s, ERROR %d ", __func__, rc); return; + } } eatt->conn_handle = conn_handle; @@ -527,6 +571,12 @@ ble_eatt_start(uint16_t conn_handle) ble_npl_eventq_put(ble_hs_evq_get(), &eatt->setup_ev); } +void +ble_eatt_connect(uint16_t conn_handle, uint16_t num_channels) +{ + ble_eatt_start(conn_handle); +} + void ble_eatt_init(ble_eatt_att_rx_fn att_rx_cb) {