mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +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) */
|
/** Connection-establishment timeout for the PAwR connection procedure (ms) */
|
||||||
#define PAWR_CONNECT_TIMEOUT_MS 30000
|
#define PAWR_CONNECT_TIMEOUT_MS 30000
|
||||||
|
|
||||||
/* Bulk OTS transfers need a low-latency, full-length 2M ACL link. */
|
/* Bulk OTS transfers need a low-latency, full-length 2M ACL link.
|
||||||
#define ESL_CONN_ITVL_MIN 12 /* 15 ms */
|
* One 1024-byte CoC SDU is ~5 DLE PDUs. With max_ce_len=0 the controller
|
||||||
#define ESL_CONN_ITVL_MAX 24 /* 30 ms */
|
* 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_TIMEOUT 400 /* 4 s */
|
||||||
|
#define ESL_CONN_CE_LEN_MAX 0xffff
|
||||||
#define ESL_LL_TX_OCTETS 251
|
#define ESL_LL_TX_OCTETS 251
|
||||||
#define ESL_LL_TX_TIME 2120
|
#define ESL_LL_TX_TIME 2120
|
||||||
|
|
||||||
@@ -71,7 +76,7 @@ static void request_fast_esl_link(uint16_t conn_handle)
|
|||||||
.latency = 0,
|
.latency = 0,
|
||||||
.supervision_timeout = ESL_CONN_TIMEOUT,
|
.supervision_timeout = ESL_CONN_TIMEOUT,
|
||||||
.min_ce_len = 0,
|
.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);
|
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) */
|
/** Active image transfer context (one at a time) */
|
||||||
static image_transfer_ctx_t *s_image_ctx = NULL;
|
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) */
|
/** Active synchronize context (only one at a time; new requests are rejected while active) */
|
||||||
static synchronize_ctx_t *s_sync_ctx = NULL;
|
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->data_len = params->data_len;
|
||||||
s_image_ctx->truncate = params->truncate;
|
s_image_ctx->truncate = params->truncate;
|
||||||
s_image_ctx->otc_mtu = 256; /* Default L2CAP OTC MTU; updated on CHANNEL_OPEN */
|
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).
|
/* Step 1: Select the target object via OLCP Go To (over GATT).
|
||||||
* Per the OTS spec, the Current Object must be selected *before* the
|
* 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);
|
free(s_image_ctx);
|
||||||
s_image_ctx = NULL;
|
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: {
|
case BLE_OTS_CLIENT_EVT_OACP_RESPONSE: {
|
||||||
const ble_ots_client_oacp_response_t *oacp = (const ble_ots_client_oacp_response_t *)param;
|
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) {
|
if (oacp->request_opcode == 0x06 && oacp->result_code == 0x01) {
|
||||||
/* OACP Write success — send the first chunk of data.
|
/* OACP Write success — fill the CoC TX slot. One send_data() call
|
||||||
* One send_data() call becomes one L2CAP SDU, so it must not
|
* is one L2CAP SDU and must stay within otc_mtu. */
|
||||||
* exceed the negotiated OTC MTU (max SDU size) or ble_l2cap_send
|
|
||||||
* rejects it with BLE_HS_EBADDATA. Chunk by otc_mtu. */
|
|
||||||
ESP_LOGD(TAG, "transfer_image: OACP Write accepted, sending data");
|
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_done = false;
|
||||||
s_image_ctx->otc_mtu : (uint16_t)s_image_ctx->data_len;
|
int rc = image_transfer_pump(conn_id);
|
||||||
s_image_ctx->sent_offset = chunk_len;
|
|
||||||
int rc = ble_ots_client_send_data(conn_id, s_image_ctx->data, chunk_len);
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
ESP_LOGE(TAG, "transfer_image: send_data failed; rc=%d", rc);
|
ESP_LOGE(TAG, "transfer_image: send_data failed; rc=%d", rc);
|
||||||
image_transfer_finish(conn_id, ESP_FAIL);
|
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: {
|
case BLE_OTS_CLIENT_EVT_DATA_SENT: {
|
||||||
const ble_ots_client_data_sent_t *sent = (const ble_ots_client_data_sent_t *)param;
|
const ble_ots_client_data_sent_t *sent = (const ble_ots_client_data_sent_t *)param;
|
||||||
if (sent->remaining == 0) {
|
if (sent->remaining == 0) {
|
||||||
/* Transfer complete */
|
if (s_image_pumping) {
|
||||||
ESP_LOGI(TAG, "transfer_image: image transfer complete for index %d",
|
s_image_done = true;
|
||||||
s_image_ctx->image_index);
|
} else {
|
||||||
image_transfer_finish(conn_id, ESP_OK);
|
ESP_LOGI(TAG, "transfer_image: image transfer complete for index %d",
|
||||||
} else {
|
s_image_ctx->image_index);
|
||||||
/* Send the next chunk, capped at the negotiated OTC MTU (one SDU). */
|
image_transfer_finish(conn_id, ESP_OK);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user