mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(ble_esl): speed up OTS by lengthening ACL events
A 15 ms link with max_ce_len=0 sent one DLE PDU per event and capped badge frames at ~17 KB/s. Request 7.5–15 ms and a long CE, and refill the single CoC TX slot instead of treating EBUSY as a hard failure.
This commit is contained in:
@@ -37,10 +37,15 @@ static const char *TAG = "esl_ap_conn";
|
||||
/** Connection-establishment timeout for the PAwR connection procedure (ms) */
|
||||
#define PAWR_CONNECT_TIMEOUT_MS 30000
|
||||
|
||||
/* Bulk OTS transfers need a low-latency, full-length 2M ACL link. */
|
||||
#define ESL_CONN_ITVL_MIN 12 /* 15 ms */
|
||||
#define ESL_CONN_ITVL_MAX 24 /* 30 ms */
|
||||
/* Bulk OTS transfers need a low-latency, full-length 2M ACL link.
|
||||
* One 1024-byte CoC SDU is ~5 DLE PDUs. With max_ce_len=0 the controller
|
||||
* typically sends one PDU per event, which caps a 15 ms link at ~17 KB/s
|
||||
* (the rate measured on 460800-byte badge frames). A long CE lets one
|
||||
* event carry the whole SDU; NimBLE CoC still only holds one TX SDU. */
|
||||
#define ESL_CONN_ITVL_MIN 6 /* 7.5 ms */
|
||||
#define ESL_CONN_ITVL_MAX 12 /* 15 ms */
|
||||
#define ESL_CONN_TIMEOUT 400 /* 4 s */
|
||||
#define ESL_CONN_CE_LEN_MAX 0xffff
|
||||
#define ESL_LL_TX_OCTETS 251
|
||||
#define ESL_LL_TX_TIME 2120
|
||||
|
||||
@@ -71,7 +76,7 @@ static void request_fast_esl_link(uint16_t conn_handle)
|
||||
.latency = 0,
|
||||
.supervision_timeout = ESL_CONN_TIMEOUT,
|
||||
.min_ce_len = 0,
|
||||
.max_ce_len = 0,
|
||||
.max_ce_len = ESL_CONN_CE_LEN_MAX,
|
||||
};
|
||||
|
||||
int rc = ble_gap_update_params(conn_handle, ¶ms);
|
||||
|
||||
@@ -173,6 +173,8 @@ void ble_esl_ap_lifecycle_handle_ots_event(uint16_t conn_id,
|
||||
|
||||
/** Active image transfer context (one at a time) */
|
||||
static image_transfer_ctx_t *s_image_ctx = NULL;
|
||||
static bool s_image_pumping;
|
||||
static bool s_image_done;
|
||||
|
||||
/** Active synchronize context (only one at a time; new requests are rejected while active) */
|
||||
static synchronize_ctx_t *s_sync_ctx = NULL;
|
||||
@@ -1192,6 +1194,8 @@ esp_err_t ble_esl_ap_transfer_image(const ble_esl_ap_image_transfer_params_t *pa
|
||||
s_image_ctx->data_len = params->data_len;
|
||||
s_image_ctx->truncate = params->truncate;
|
||||
s_image_ctx->otc_mtu = 256; /* Default L2CAP OTC MTU; updated on CHANNEL_OPEN */
|
||||
s_image_pumping = false;
|
||||
s_image_done = false;
|
||||
|
||||
/* Step 1: Select the target object via OLCP Go To (over GATT).
|
||||
* Per the OTS spec, the Current Object must be selected *before* the
|
||||
@@ -1255,6 +1259,46 @@ static void image_transfer_finish(uint16_t conn_id, esp_err_t status)
|
||||
|
||||
free(s_image_ctx);
|
||||
s_image_ctx = NULL;
|
||||
s_image_pumping = false;
|
||||
s_image_done = false;
|
||||
}
|
||||
|
||||
/* NimBLE CoC TX has a single SDU slot. Keep filling it until the stack is
|
||||
* busy or stalled; DATA_SENT / TX_UNSTALLED refill. Do not finish from the
|
||||
* nested DATA_SENT that send_data() dispatches on success. */
|
||||
static int image_transfer_pump(uint16_t conn_id)
|
||||
{
|
||||
if (s_image_ctx == NULL || s_image_pumping) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s_image_pumping = true;
|
||||
int rc = 0;
|
||||
for (int burst = 0; burst < 4; burst++) {
|
||||
uint32_t offset = s_image_ctx->sent_offset;
|
||||
if (offset >= s_image_ctx->data_len) {
|
||||
break;
|
||||
}
|
||||
uint32_t left = s_image_ctx->data_len - offset;
|
||||
uint16_t chunk = (left > s_image_ctx->otc_mtu) ?
|
||||
s_image_ctx->otc_mtu : (uint16_t)left;
|
||||
rc = ble_ots_client_send_data(conn_id, s_image_ctx->data + offset, chunk);
|
||||
if (rc == BLE_HS_EBUSY) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
if (rc != 0) {
|
||||
break;
|
||||
}
|
||||
s_image_ctx->sent_offset += chunk;
|
||||
}
|
||||
s_image_pumping = false;
|
||||
if (rc == 0 && s_image_done) {
|
||||
ESP_LOGI(TAG, "transfer_image: image transfer complete for index %d",
|
||||
s_image_ctx->image_index);
|
||||
image_transfer_finish(conn_id, ESP_OK);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1337,15 +1381,11 @@ void ble_esl_ap_lifecycle_handle_ots_event(uint16_t conn_id,
|
||||
case BLE_OTS_CLIENT_EVT_OACP_RESPONSE: {
|
||||
const ble_ots_client_oacp_response_t *oacp = (const ble_ots_client_oacp_response_t *)param;
|
||||
if (oacp->request_opcode == 0x06 && oacp->result_code == 0x01) {
|
||||
/* OACP Write success — send the first chunk of data.
|
||||
* One send_data() call becomes one L2CAP SDU, so it must not
|
||||
* exceed the negotiated OTC MTU (max SDU size) or ble_l2cap_send
|
||||
* rejects it with BLE_HS_EBADDATA. Chunk by otc_mtu. */
|
||||
/* OACP Write success — fill the CoC TX slot. One send_data() call
|
||||
* is one L2CAP SDU and must stay within otc_mtu. */
|
||||
ESP_LOGD(TAG, "transfer_image: OACP Write accepted, sending data");
|
||||
uint16_t chunk_len = (s_image_ctx->data_len > s_image_ctx->otc_mtu) ?
|
||||
s_image_ctx->otc_mtu : (uint16_t)s_image_ctx->data_len;
|
||||
s_image_ctx->sent_offset = chunk_len;
|
||||
int rc = ble_ots_client_send_data(conn_id, s_image_ctx->data, chunk_len);
|
||||
s_image_done = false;
|
||||
int rc = image_transfer_pump(conn_id);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "transfer_image: send_data failed; rc=%d", rc);
|
||||
image_transfer_finish(conn_id, ESP_FAIL);
|
||||
@@ -1361,26 +1401,19 @@ void ble_esl_ap_lifecycle_handle_ots_event(uint16_t conn_id,
|
||||
case BLE_OTS_CLIENT_EVT_DATA_SENT: {
|
||||
const ble_ots_client_data_sent_t *sent = (const ble_ots_client_data_sent_t *)param;
|
||||
if (sent->remaining == 0) {
|
||||
/* Transfer complete */
|
||||
ESP_LOGI(TAG, "transfer_image: image transfer complete for index %d",
|
||||
s_image_ctx->image_index);
|
||||
image_transfer_finish(conn_id, ESP_OK);
|
||||
} else {
|
||||
/* Send the next chunk, capped at the negotiated OTC MTU (one SDU). */
|
||||
uint32_t offset = s_image_ctx->data_len - sent->remaining;
|
||||
uint16_t chunk_len = (sent->remaining > s_image_ctx->otc_mtu) ?
|
||||
s_image_ctx->otc_mtu : (uint16_t)sent->remaining;
|
||||
s_image_ctx->sent_offset = offset + chunk_len;
|
||||
ESP_LOGD(TAG, "transfer_image: sending next chunk at offset %lu, %u bytes "
|
||||
"(%lu remaining)", (unsigned long)offset, chunk_len,
|
||||
(unsigned long)sent->remaining);
|
||||
int rc = ble_ots_client_send_data(conn_id,
|
||||
s_image_ctx->data + offset,
|
||||
chunk_len);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "transfer_image: send_data failed for chunk; rc=%d", rc);
|
||||
image_transfer_finish(conn_id, ESP_FAIL);
|
||||
if (s_image_pumping) {
|
||||
s_image_done = true;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "transfer_image: image transfer complete for index %d",
|
||||
s_image_ctx->image_index);
|
||||
image_transfer_finish(conn_id, ESP_OK);
|
||||
}
|
||||
break;
|
||||
}
|
||||
int rc = image_transfer_pump(conn_id);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "transfer_image: send_data failed for chunk; rc=%d", rc);
|
||||
image_transfer_finish(conn_id, ESP_FAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user