fix(nimble): Fix vulnerabilities in NimBLE examples

This commit is contained in:
Shreeyash
2026-02-06 17:47:06 +05:30
parent 41f15f5329
commit bd971c5d0e
43 changed files with 894 additions and 317 deletions
@@ -18,6 +18,7 @@
void send_heart_rate_indication(void); void send_heart_rate_indication(void);
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
void gatt_svr_subscribe_cb(struct ble_gap_event *event); void gatt_svr_subscribe_cb(struct ble_gap_event *event);
void gatt_svr_reset_heart_rate_subscription(void);
int gatt_svc_init(void); int gatt_svc_init(void);
#endif // GATT_SVR_H #endif // GATT_SVR_H
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -80,7 +80,7 @@ static void heart_rate_task(void *param) {
void app_main(void) { void app_main(void) {
/* Local variables */ /* Local variables */
int rc = 0; BaseType_t rc = 0;
esp_err_t ret; esp_err_t ret;
/* LED initialization */ /* LED initialization */
@@ -129,7 +129,17 @@ void app_main(void) {
nimble_host_config_init(); nimble_host_config_init();
/* Start NimBLE host task thread and return */ /* Start NimBLE host task thread and return */
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL); rc = xTaskCreate(nimble_host_task, "NimBLE Host", 4 * 1024, NULL,
xTaskCreate(heart_rate_task, "Heart Rate", 4*1024, NULL, 5, NULL); 5, NULL);
if (rc != pdPASS) {
ESP_LOGE(TAG, "failed to create NimBLE host task");
return;
}
rc = xTaskCreate(heart_rate_task, "Heart Rate", 4 * 1024, NULL, 5, NULL);
if (rc != pdPASS) {
ESP_LOGE(TAG, "failed to create heart rate task");
return;
}
return; return;
} }
@@ -186,6 +186,9 @@ static int gap_event_handler(struct ble_gap_event *event, void *arg) {
ESP_LOGI(TAG, "disconnected from peer; reason=%d", ESP_LOGI(TAG, "disconnected from peer; reason=%d",
event->disconnect.reason); event->disconnect.reason);
/* Reset heart rate subscription state */
gatt_svr_reset_heart_rate_subscription();
/* Restart advertising */ /* Restart advertising */
start_advertising(); start_advertising();
return rc; return rc;
@@ -23,7 +23,7 @@ static uint8_t heart_rate_chr_val[2] = {0};
static uint16_t heart_rate_chr_val_handle; static uint16_t heart_rate_chr_val_handle;
static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37); static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37);
static uint16_t heart_rate_chr_conn_handle = 0; static uint16_t heart_rate_chr_conn_handle = BLE_HS_CONN_HANDLE_NONE;
static bool heart_rate_chr_conn_handle_inited = false; static bool heart_rate_chr_conn_handle_inited = false;
static bool heart_rate_ind_status = false; static bool heart_rate_ind_status = false;
@@ -240,6 +240,12 @@ void gatt_svr_subscribe_cb(struct ble_gap_event *event) {
} }
} }
void gatt_svr_reset_heart_rate_subscription(void) {
heart_rate_chr_conn_handle = BLE_HS_CONN_HANDLE_NONE;
heart_rate_chr_conn_handle_inited = false;
heart_rate_ind_status = false;
}
/* /*
* GATT server initialization * GATT server initialization
* 1. Initialize GATT service * 1. Initialize GATT service
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -103,7 +103,7 @@ char *CategoryID_to_String(uint8_t CategoryID)
void ble_receive_apple_notification_source(uint8_t *message, uint16_t message_len) void ble_receive_apple_notification_source(uint8_t *message, uint16_t message_len)
{ {
if (!message || message_len < 5) { if (!message || message_len < 8) {
return; return;
} }
@@ -126,14 +126,19 @@ void ble_receive_apple_data_source(uint8_t *message, uint16_t message_len)
switch (Command_id) switch (Command_id)
{ {
case CommandIDGetNotificationAttributes: { case CommandIDGetNotificationAttributes: {
if (message_len < 5) {
ESP_LOGE(NimBLE_ANCS_TAG, "Invalid message length for attributes: %d",
message_len);
return;
}
uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24); uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24);
uint32_t remian_attr_len = message_len - 5; uint32_t remian_attr_len = message_len - 5;
uint8_t *attrs = &message[5]; uint8_t *attrs = &message[5];
ESP_LOGI(NimBLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID); ESP_LOGI(NimBLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID);
while(remian_attr_len > 0) { while(remian_attr_len >= 3) {
uint8_t AttributeID = attrs[0]; uint8_t AttributeID = attrs[0];
uint16_t len = attrs[1] | (attrs[2] << 8); uint16_t len = attrs[1] | (attrs[2] << 8);
if(len > (remian_attr_len -3)) { if(len > remian_attr_len - 3) {
ESP_LOGE(NimBLE_ANCS_TAG, "data error"); ESP_LOGE(NimBLE_ANCS_TAG, "data error");
break; break;
} }
+17 -11
View File
@@ -72,7 +72,7 @@ static uint8_t ext_adv_pattern_1[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x18, 0x11, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x18, 0x11,
0x0e, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'a', 'n', 'c', 's', '-', 'e', 0x0d, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'a', 'n', 'c', 's', '-', 'e',
}; };
#endif #endif
@@ -338,14 +338,14 @@ ext_ble_ancs_advertise(void)
} }
/* use defaults for non-set params */ /* use defaults for non-set params */
memset (&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
/* enable connectable advertising */ /* enable connectable, scannable advertising */
params.connectable = 1; params.connectable = 1;
params.scannable = 1;
/* advertise using random addr */ /* advertise using configured addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
params.tx_power = 127; params.tx_power = 127;
@@ -359,8 +359,6 @@ ext_ble_ancs_advertise(void)
ble_ancs_gap_event, NULL); ble_ancs_gap_event, NULL);
assert (rc == 0); assert (rc == 0);
/* in this case only scan response is allowed */
/* get mbuf for scan rsp data */ /* get mbuf for scan rsp data */
data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
assert(data); assert(data);
@@ -369,7 +367,7 @@ ext_ble_ancs_advertise(void)
rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
assert(rc == 0); assert(rc == 0);
rc = ble_gap_ext_adv_set_data(instance, data); rc = ble_gap_ext_adv_rsp_set_data(instance, data);
assert (rc == 0); assert (rc == 0);
/* start advertising */ /* start advertising */
@@ -578,10 +576,18 @@ ble_ancs_gap_event(struct ble_gap_event *event, void *arg)
notificationUID, sizeof(p_attr)/sizeof(ble_noti_attr_list_t), p_attr); notificationUID, sizeof(p_attr)/sizeof(ble_noti_attr_list_t), p_attr);
} }
} else if (event->notify_rx.attr_handle == data_source_handle) { } else if (event->notify_rx.attr_handle == data_source_handle) {
uint16_t new_len = data_buffer.len + event->notify_rx.om->om_len;
if (new_len > sizeof(data_buffer.buffer)) {
MODLOG_DFLT(ERROR, "data_buffer overflow: %d + %d > %d",
data_buffer.len, event->notify_rx.om->om_len,
(int)sizeof(data_buffer.buffer));
data_buffer.len = 0;
return 0;
}
memcpy(&data_buffer.buffer[data_buffer.len], memcpy(&data_buffer.buffer[data_buffer.len],
event->notify_rx.om->om_data, event->notify_rx.om->om_data,
event->notify_rx.om->om_len); event->notify_rx.om->om_len);
data_buffer.len += event->notify_rx.om->om_len; data_buffer.len = new_len;
if (event->notify_rx.om->om_len == (MTU_size - 3)) { if (event->notify_rx.om->om_len == (MTU_size - 3)) {
esp_timer_start_periodic(periodic_timer, 500000); esp_timer_start_periodic(periodic_timer, 500000);
@@ -69,14 +69,16 @@ int blecent_on_subscribe(uint16_t conn_handle,
MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d " MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d "
"attr_handle=%d\n", "attr_handle=%d\n",
error->status, conn_handle, attr->handle); error->status, conn_handle, attr->handle);
subscribe_all++; if (error->status == 0) {
if (subscribe_all == 4){ subscribe_all++;
struct ble_cs_initiator_procedure_start_params param; if (subscribe_all == 4){
memset(&param,0,sizeof param); struct ble_cs_initiator_procedure_start_params param;
param.conn_handle=conn_handle; memset(&param,0,sizeof param);
param.cb=blecs_gap_event; param.conn_handle=conn_handle;
param.cb_arg=NULL; param.cb=blecs_gap_event;
ble_cs_initiator_procedure_start(&param); param.cb_arg=NULL;
ble_cs_initiator_procedure_start(&param);
}
} }
return 0; return 0;
@@ -161,13 +163,16 @@ blecent_on_custom_read(uint16_t conn_handle,
MODLOG_DFLT(INFO, "Read complete RAS FEATURES; status=%d conn_handle=%d", error->status, conn_handle); MODLOG_DFLT(INFO, "Read complete RAS FEATURES; status=%d conn_handle=%d", error->status, conn_handle);
if (error->status == 0) { if (error->status == 0) {
uint32_t var; uint32_t var = 0;
int rc = 0; int rc = 0;
if (attr->om && OS_MBUF_PKTLEN(attr->om) > 0) { uint16_t pktlen = (attr->om ? OS_MBUF_PKTLEN(attr->om) : 0);
os_mbuf_copydata(attr->om, 0, OS_MBUF_PKTLEN(attr->om), &var); if (attr->om && pktlen > 0 && pktlen <= sizeof(var)) {
} else { os_mbuf_copydata(attr->om, 0, pktlen, &var);
MODLOG_DFLT(ERROR, "Error: No data in the attribute\n"); }
} else {
MODLOG_DFLT(ERROR, "Error: Invalid data length=%u in the attribute\n", pktlen);
return -1;
}
MODLOG_DFLT(INFO, " attr_handle = %u value = %" PRIu32, attr->handle, var); MODLOG_DFLT(INFO, " attr_handle = %u value = %" PRIu32, attr->handle, var);
if (var & REAL_TIME_RANGING_DATA_BIT) { if (var & REAL_TIME_RANGING_DATA_BIT) {
@@ -400,8 +405,8 @@ static int blecs_gap_event(struct ble_cs_event *event, void *arg){
return 0; return 0;
} }
if (event->subev_result.num_steps_reported) { if (event->subev_result.num_steps_reported) {
memcpy(&local_cs_steps_data, event->subev_result.steps, sizeof local_cs_steps_data); size_t copy_size = event->subev_result.num_steps_reported * sizeof(struct cs_steps_data);
memcpy(&local_cs_steps_data, event->subev_result.steps, MIN(copy_size, sizeof(local_cs_steps_data)));
} }
nap=event->subev_result.num_antenna_paths; nap=event->subev_result.num_antenna_paths;
most_recent_local_ranging_counter=event->subev_result.procedure_counter; most_recent_local_ranging_counter=event->subev_result.procedure_counter;
@@ -486,6 +491,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
return 0; return 0;
} }
connh = event->connect.conn_handle; connh = event->connect.conn_handle;
subscribe_all = 0;
rc = ble_gap_security_initiate(event->connect.conn_handle); rc = ble_gap_security_initiate(event->connect.conn_handle);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(INFO, "Security could not be initiated, rc = %d\n", rc); MODLOG_DFLT(INFO, "Security could not be initiated, rc = %d\n", rc);
@@ -580,7 +586,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
uint16_t ranging_counter; uint16_t ranging_counter;
os_mbuf_copydata(event->notify_rx.om, 0, sizeof(uint16_t), &ranging_counter); os_mbuf_copydata(event->notify_rx.om, 0, sizeof(uint16_t), &ranging_counter);
most_recent_peer_ranging_counter=ranging_counter; most_recent_peer_ranging_counter=ranging_counter;
most_recent_local_ranging_counter=0; most_recent_local_ranging_counter= ranging_counter;
if (most_recent_peer_ranging_counter!=most_recent_local_ranging_counter) { if (most_recent_peer_ranging_counter!=most_recent_local_ranging_counter) {
MODLOG_DFLT(INFO, "Ranging counter mismatch : %" PRId32 " , %" PRId32, MODLOG_DFLT(INFO, "Ranging counter mismatch : %" PRId32 " , %" PRId32,
@@ -601,14 +607,26 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
} else if (ble_svc_ras_od_val_handle == event->notify_rx.attr_handle) { } else if (ble_svc_ras_od_val_handle == event->notify_rx.attr_handle) {
MODLOG_DFLT(INFO, "Received On Demand Ranging Data\n"); MODLOG_DFLT(INFO, "Received On Demand Ranging Data\n");
struct segment ras_segment;
int notif_len = OS_MBUF_PKTLEN(event->notify_rx.om); int notif_len = OS_MBUF_PKTLEN(event->notify_rx.om);
os_mbuf_copydata(event->notify_rx.om, 0,notif_len , &ras_segment);
MODLOG_DFLT(INFO, "Received On Demand Ranging Data , len = %d\n",notif_len); #define RAS_OD_RX_BUF_MAX 512
uint8_t * data= (uint8_t *)ras_segment.data; if (notif_len >= sizeof(struct segment_header) && notif_len <= RAS_OD_RX_BUF_MAX) {
for(int i=0;i<notif_len-sizeof(struct segment_header);i++){ uint8_t ras_buf[RAS_OD_RX_BUF_MAX];
MODLOG_DFLT(INFO, "data[%d] = %d\n",i,data[i]); int rc_copy = os_mbuf_copydata(event->notify_rx.om, 0, notif_len, ras_buf);
if (rc_copy == 0) {
uint8_t *data = ras_buf + sizeof(struct segment_header);
size_t data_len = notif_len - sizeof(struct segment_header);
MODLOG_DFLT(INFO,
"Received On Demand Ranging Data , len = %d\n",
notif_len);
for (size_t i = 0; i < data_len; i++) {
MODLOG_DFLT(INFO, "data[%d] = %d\n", (int)i, data[i]);
}
}
} }
#undef RAS_OD_RX_BUF_MAX
/* /*
handle the incoming notification handle the incoming notification
@@ -116,8 +116,9 @@ static int blecs_gap_event(struct ble_cs_event *event, void *arg)
most_recent_local_ranging_counter=event->subev_result.procedure_counter; most_recent_local_ranging_counter=event->subev_result.procedure_counter;
} else if(event->subev_result.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) { } else if(event->subev_result.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_PARTIAL) {
ranging_subevent.type=BLE_CS_EVENT_SUBEVET_RESULT; memset(&ranging_subevent, 0, sizeof(ranging_subevent));
ranging_subevent.subev_result= event->subev_result; ranging_subevent.type = BLE_CS_EVENT_SUBEVET_RESULT;
ranging_subevent.subev_result = event->subev_result;
idx++; idx++;
if (idx==1) { if (idx==1) {
most_recent_local_ranging_counter=event->subev_result.procedure_counter; most_recent_local_ranging_counter=event->subev_result.procedure_counter;
@@ -133,6 +134,7 @@ static int blecs_gap_event(struct ble_cs_event *event, void *arg)
MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Aborted\n"); MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Aborted\n");
} else if ( event->subev_result_continue.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_COMPLETE) { } else if ( event->subev_result_continue.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_COMPLETE) {
MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Complete\n"); MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Complete\n");
ranging_subevent.type = BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE;
ranging_subevent.subev_result_continue = event->subev_result_continue; ranging_subevent.subev_result_continue = event->subev_result_continue;
/* To /* To
* Get total number of CS procedure from CS enable event and then accordigly indicate to most recent ranging counter * Get total number of CS procedure from CS enable event and then accordigly indicate to most recent ranging counter
@@ -81,7 +81,11 @@ uint8_t antenna_signal_index[4] = {CTE_ANT0_IDX, CTE_ANT1_IDX, CTE_ANT2_IDX, CTE
int ble_direction_finding_antenna_init(uint8_t* gpio_array,uint8_t gpio_array_len){ int ble_direction_finding_antenna_init(uint8_t* gpio_array,uint8_t gpio_array_len){
int rc; int rc;
// GPIO configuration // GPIO configuration
uint32_t gpio_pin_maks = 0; uint64_t gpio_pin_maks = 0;
if (!gpio_array || gpio_array_len > 4 || gpio_array_len == 0) {
return ESP_ERR_INVALID_ARG;
}
for (int i = 0; i < gpio_array_len; i++){ for (int i = 0; i < gpio_array_len; i++){
gpio_pin_maks |= (1ULL << gpio_array[i]); gpio_pin_maks |= (1ULL << gpio_array[i]);
} }
@@ -95,6 +99,7 @@ int ble_direction_finding_antenna_init(uint8_t* gpio_array,uint8_t gpio_array_le
rc = gpio_config(&gpio_conf); rc = gpio_config(&gpio_conf);
if(rc != 0) { if(rc != 0) {
ESP_LOGE("DF","config fault GPIO failed"); ESP_LOGE("DF","config fault GPIO failed");
return rc;
} }
// gpio bind signal // gpio bind signal
for (int i = 0; i < gpio_array_len; i++){ for (int i = 0; i < gpio_array_len; i++){
@@ -19,8 +19,8 @@
static const char *tag = "NimBLE_CTS_CENT"; static const char *tag = "NimBLE_CTS_CENT";
static int ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg); static int ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg);
static char *day_of_week[7] = { static char *day_of_week[8] = {
"Unknown" "Unknown",
"Monday", "Monday",
"Tuesday", "Tuesday",
"Wednesday", "Wednesday",
@@ -33,10 +33,14 @@ void ble_store_config_init(void);
static void ble_cts_cent_scan(void); static void ble_cts_cent_scan(void);
void printtime(struct ble_svc_cts_curr_time ctime) { void printtime(struct ble_svc_cts_curr_time ctime) {
uint8_t dow = ctime.et_256.d_d_t.day_of_week;
const char *dow_str = (dow >= 1 && dow <= 7) ? day_of_week[dow] : day_of_week[0];
if (dow >= 8) {
dow = 0;
}
ESP_LOGI(tag, "Date : %d/%d/%d %s", ctime.et_256.d_d_t.d_t.day, ESP_LOGI(tag, "Date : %d/%d/%d %s", ctime.et_256.d_d_t.d_t.day,
ctime.et_256.d_d_t.d_t.month, ctime.et_256.d_d_t.d_t.month,
ctime.et_256.d_d_t.d_t.year, ctime.et_256.d_d_t.d_t.year, dow_str);
day_of_week[ctime.et_256.d_d_t.day_of_week]);
ESP_LOGI(tag, "hours : %d minutes : %d ", ESP_LOGI(tag, "hours : %d minutes : %d ",
ctime.et_256.d_d_t.d_t.hours, ctime.et_256.d_d_t.d_t.hours,
ctime.et_256.d_d_t.d_t.minutes); ctime.et_256.d_d_t.d_t.minutes);
@@ -55,6 +59,7 @@ ble_cts_cent_on_read(uint16_t conn_handle,
struct ble_gatt_attr *attr, struct ble_gatt_attr *attr,
void *arg) void *arg)
{ {
int rc = 0;
struct ble_svc_cts_curr_time ctime; /* store the read time */ struct ble_svc_cts_curr_time ctime; /* store the read time */
MODLOG_DFLT(INFO, "Read Current time complete; status=%d conn_handle=%d\n", MODLOG_DFLT(INFO, "Read Current time complete; status=%d conn_handle=%d\n",
error->status, conn_handle); error->status, conn_handle);
@@ -66,8 +71,12 @@ ble_cts_cent_on_read(uint16_t conn_handle,
goto err; goto err;
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
ble_hs_mbuf_to_flat(attr->om, &ctime, sizeof(ctime), NULL); rc = ble_hs_mbuf_to_flat(attr->om, &ctime, sizeof(ctime), NULL);
printtime(ctime); if (rc == 0 && ctime.et_256.d_d_t.day_of_week <= 7) {
printtime(ctime);
} else {
MODLOG_DFLT(WARN, "Invalid CTS time data; rc=%d day_of_week=%d\n", rc, ctime.et_256.d_d_t.day_of_week);
}
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
@@ -234,7 +243,7 @@ ext_ble_cts_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* Search if cts UUID is advertised */ /* Search if cts UUID is advertised */
if (disc->data[offset + 1] == 0x03) { if (disc->data[offset + 1] == 0x03) {
int temp = 2; int temp = 2;
while(temp < disc->data[offset + 1]) { while (temp < ad_struct_len) {
if(disc->data[offset + temp] == 0x05 && if(disc->data[offset + temp] == 0x05 &&
disc->data[offset + temp + 1] == 0x18) { disc->data[offset + temp + 1] == 0x18) {
return 1; return 1;
@@ -491,7 +500,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg)
#else #else
#if MYNEWT_VAL(BLE_GATTC) #if MYNEWT_VAL(BLE_GATTC)
/*** Go for service discovery after encryption has been successfully enabled ***/ /*** Go for service discovery after encryption has been successfully enabled ***/
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->enc_change.conn_handle,
ble_cts_cent_on_disc_complete, NULL); ble_cts_cent_on_disc_complete, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -510,7 +519,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg)
event->cache_assoc.status, event->cache_assoc.status,
(event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED"); (event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED");
/* Perform service discovery */ /* Perform service discovery */
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->cache_assoc.conn_handle,
blecent_on_disc_complete, NULL); blecent_on_disc_complete, NULL);
if(rc != 0) { if(rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,6 +16,7 @@
#include <time.h> #include <time.h>
#include "sys/time.h" #include "sys/time.h"
static const char *TAG = "CTS_GATT_SVR";
void void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
@@ -85,7 +86,21 @@ int fetch_current_time(struct ble_svc_cts_curr_time *ctime) {
int set_current_time(struct ble_svc_cts_curr_time ctime) { int set_current_time(struct ble_svc_cts_curr_time ctime) {
time_t now; time_t now;
struct tm timeinfo; struct tm timeinfo;
struct timeval tv_now; struct timeval tv_now = {0};
/* Basic sanity validation for incoming time fields */
if (ctime.et_256.d_d_t.d_t.year < 1900 ||
ctime.et_256.d_d_t.d_t.year > 9999 ||
ctime.et_256.d_d_t.d_t.month < 1 || ctime.et_256.d_d_t.d_t.month > 12 ||
ctime.et_256.d_d_t.d_t.day < 1 || ctime.et_256.d_d_t.d_t.day > 31 ||
ctime.et_256.d_d_t.d_t.hours > 23 ||
ctime.et_256.d_d_t.d_t.minutes > 59 ||
ctime.et_256.d_d_t.d_t.seconds > 59 ||
ctime.et_256.d_d_t.day_of_week < 1 || ctime.et_256.d_d_t.day_of_week > 7) {
ESP_LOGE(TAG, "Invalid current time parameters");
return -1;
}
/* fill date_time */ /* fill date_time */
timeinfo.tm_year= ctime.et_256.d_d_t.d_t.year - 1900 ; timeinfo.tm_year= ctime.et_256.d_d_t.d_t.year - 1900 ;
timeinfo.tm_mon = ctime.et_256.d_d_t.d_t.month - 1; timeinfo.tm_mon = ctime.et_256.d_d_t.d_t.month - 1;
@@ -95,7 +110,12 @@ int set_current_time(struct ble_svc_cts_curr_time ctime) {
timeinfo.tm_sec = ctime.et_256.d_d_t.d_t.seconds; timeinfo.tm_sec = ctime.et_256.d_d_t.d_t.seconds;
timeinfo.tm_wday = ctime.et_256.d_d_t.day_of_week - 1; timeinfo.tm_wday = ctime.et_256.d_d_t.day_of_week - 1;
now = mktime(&timeinfo); now = mktime(&timeinfo);
if (now == (time_t)-1) {
ESP_LOGE(TAG, "Failed to convert current time");
return -1;
}
tv_now.tv_sec = now; tv_now.tv_sec = now;
tv_now.tv_usec = 0;
settimeofday(&tv_now, NULL); settimeofday(&tv_now, NULL);
/* set the last updated */ /* set the last updated */
gettimeofday(&last_updated, NULL); gettimeofday(&last_updated, NULL);
@@ -102,8 +102,21 @@ enc_adv_data_cent_on_read(uint16_t conn_handle,
value_ead.km = (struct key_material *) malloc (sizeof(struct key_material)); value_ead.km = (struct key_material *) malloc (sizeof(struct key_material));
if (value_ead.km == NULL) {
MODLOG_DFLT(ERROR, "Failed to allocate memory for key material");
goto err;
}
memset(value_ead.km, 0, sizeof(struct key_material)); memset(value_ead.km, 0, sizeof(struct key_material));
/* Validate mbuf has enough data before copying */
if (attr->om == NULL || OS_MBUF_PKTLEN(attr->om) < (BLE_EAD_KEY_SIZE + BLE_EAD_IV_SIZE)) {
MODLOG_DFLT(ERROR, "Invalid mbuf or insufficient data size");
free(value_ead.km);
value_ead.km = NULL;
goto err;
}
os_mbuf_copydata(attr->om, 0, BLE_EAD_KEY_SIZE, &value_ead.km->session_key); os_mbuf_copydata(attr->om, 0, BLE_EAD_KEY_SIZE, &value_ead.km->session_key);
os_mbuf_copydata(attr->om, BLE_EAD_KEY_SIZE, BLE_EAD_IV_SIZE, &value_ead.km->iv); os_mbuf_copydata(attr->om, BLE_EAD_KEY_SIZE, BLE_EAD_IV_SIZE, &value_ead.km->iv);
@@ -120,8 +133,17 @@ enc_adv_data_cent_on_read(uint16_t conn_handle,
MODLOG_DFLT(INFO, "Writing of session key, iv, and peer addr to NVS success"); MODLOG_DFLT(INFO, "Writing of session key, iv, and peer addr to NVS success");
} }
if (value_ead.km != NULL) {
free(value_ead.km);
value_ead.km = NULL;
}
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
if (value_ead.km != NULL) {
free(value_ead.km);
value_ead.km = NULL;
}
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
@@ -244,6 +266,10 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
switch (op) { switch (op) {
case BLE_GAP_ENC_ADV_DATA: case BLE_GAP_ENC_ADV_DATA:
enc_data = (uint8_t *) malloc (sizeof(uint8_t) * len); enc_data = (uint8_t *) malloc (sizeof(uint8_t) * len);
if (enc_data == NULL) {
MODLOG_DFLT(ERROR, "Failed to allocate enc_data");
return 0;
}
memcpy(enc_data, data + offset + 2, len); memcpy(enc_data, data + offset + 2, len);
memcpy(&key_ead.peer_addr.val, peer_addr, PEER_ADDR_VAL_SIZE); memcpy(&key_ead.peer_addr.val, peer_addr, PEER_ADDR_VAL_SIZE);
@@ -262,6 +288,7 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
MODLOG_DFLT(INFO, "Decryption of adv data done successfully"); MODLOG_DFLT(INFO, "Decryption of adv data done successfully");
} else { } else {
MODLOG_DFLT(INFO, "Decryption of adv data failed"); MODLOG_DFLT(INFO, "Decryption of adv data failed");
free(enc_data);
return 0; return 0;
} }
@@ -272,6 +299,7 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
MODLOG_DFLT(INFO, "0x%02X ", temp[i]); MODLOG_DFLT(INFO, "0x%02X ", temp[i]);
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
free(enc_data);
return 1; return 1;
default: default:
@@ -68,8 +68,8 @@ enc_adv_data_prph_print_conn_desc(struct ble_gap_conn_desc *desc)
desc->sec_state.bonded); desc->sec_state.bonded);
} }
static void static int
enc_adv_data_prph_encrypt_set(uint8_t * out_encrypted_adv_data, enc_adv_data_prph_encrypt_set(uint8_t *out_encrypted_adv_data,
const unsigned encrypted_adv_data_len) const unsigned encrypted_adv_data_len)
{ {
int rc; int rc;
@@ -85,13 +85,13 @@ enc_adv_data_prph_encrypt_set(uint8_t * out_encrypted_adv_data,
print_bytes(unencrypted_adv_data, unencrypted_adv_data_len); print_bytes(unencrypted_adv_data, unencrypted_adv_data_len);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
rc = ble_ead_encrypt(km.session_key, km.iv, unencrypted_adv_data, unencrypted_adv_data_len, encrypted_adv_data); rc = ble_ead_encrypt(km.session_key, km.iv, unencrypted_adv_data,
if (rc == 0) { unencrypted_adv_data_len, encrypted_adv_data);
MODLOG_DFLT(INFO, "Encryption of adv data done successfully"); if (rc != 0) {
} else { MODLOG_DFLT(ERROR, "Encryption of adv data failed; rc=%d", rc);
MODLOG_DFLT(INFO, "Encryption of adv data failed"); return rc;
return;
} }
MODLOG_DFLT(INFO, "Encryption of adv data done successfully");
MODLOG_DFLT(INFO, "Data after encryption:"); MODLOG_DFLT(INFO, "Data after encryption:");
print_bytes(encrypted_adv_data, encrypted_adv_data_len); print_bytes(encrypted_adv_data, encrypted_adv_data_len);
@@ -99,6 +99,7 @@ enc_adv_data_prph_encrypt_set(uint8_t * out_encrypted_adv_data,
/** Contains Randomiser ## Encrypted Advertising Data ## MIC */ /** Contains Randomiser ## Encrypted Advertising Data ## MIC */
memcpy(out_encrypted_adv_data, encrypted_adv_data, encrypted_adv_data_len); memcpy(out_encrypted_adv_data, encrypted_adv_data, encrypted_adv_data_len);
return 0;
} }
/** /**
@@ -111,7 +112,6 @@ enc_adv_data_prph_advertise(void)
{ {
struct ble_gap_adv_params params; struct ble_gap_adv_params params;
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
uint8_t own_addr_type;
int rc; int rc;
const unsigned encrypted_adv_data_len = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern)); const unsigned encrypted_adv_data_len = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern));
@@ -149,7 +149,10 @@ enc_adv_data_prph_advertise(void)
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
/** Getting the encrypted advertising data */ /** Getting the encrypted advertising data */
enc_adv_data_prph_encrypt_set(encrypted_adv_data, encrypted_adv_data_len); rc = enc_adv_data_prph_encrypt_set(encrypted_adv_data, encrypted_adv_data_len);
if (rc != 0) {
return;
}
fields.enc_adv_data = encrypted_adv_data; fields.enc_adv_data = encrypted_adv_data;
fields.enc_adv_data_len = encrypted_adv_data_len; fields.enc_adv_data_len = encrypted_adv_data_len;
@@ -21,9 +21,20 @@ static bool client_connect = 0;
static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle, static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) { struct ble_gatt_access_ctxt *ctxt, void *arg) {
int rc = 0;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
static uint8_t data[1] = {42}; // Example value static uint8_t data[1] = {42}; // Example value
os_mbuf_append(ctxt->om, data, sizeof(data)); os_mbuf_append(ctxt->om, data, sizeof(data));
if (rc != 0) {
return rc;
}
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
static uint8_t dsc_data[1] = {0x01}; // Example descriptor value
return os_mbuf_append(ctxt->om, dsc_data, sizeof(dsc_data));
}
else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
MODLOG_DFLT(INFO, "Characteristic written");
return 0;
} }
return 0; return 0;
} }
@@ -162,6 +162,10 @@ ble_htp_cent_on_read(uint16_t conn_handle,
uint16_t value; uint16_t value;
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Lost peer for conn_handle=%d\n", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16), BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16),
@@ -330,7 +334,7 @@ ext_ble_htp_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* Conversion */ /* Conversion */
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
test_addr[i] = (uint8_t )peer_addr[i]; test_addr[i] = (uint8_t )peer_addr[5 - i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
@@ -341,23 +345,23 @@ ext_ble_htp_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* The device has to advertise support for the Health thermometer /* The device has to advertise support for the Health thermometer
* service (0x1809). * service (0x1809).
*/ */
do { while (offset < disc->length_data) {
ad_struct_len = disc->data[offset]; ad_struct_len = disc->data[offset];
if (!ad_struct_len) { if (ad_struct_len == 0 || offset + ad_struct_len + 1 > disc->length_data) {
break; break;
} }
/* Search if HTP UUID is advertised */ /* Search if HTP UUID is advertised */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (disc->data[offset + 1] == 0x03) {
if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x09 ) { if (disc->data[offset + 2] == 0x09 && disc->data[offset + 3] == 0x18) {
return 1; return 1;
} }
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
}
} while ( offset < disc->length_data );
return 0; return 0;
} }
@@ -605,7 +609,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg)
#else #else
#if MYNEWT_VAL(BLE_GATTC) #if MYNEWT_VAL(BLE_GATTC)
/*** Go for service discovery after encryption has been successfully enabled ***/ /*** Go for service discovery after encryption has been successfully enabled ***/
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->enc_change.conn_handle,
ble_htp_cent_on_disc_complete, NULL); ble_htp_cent_on_disc_complete, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -624,7 +628,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg)
event->cache_assoc.status, event->cache_assoc.status,
(event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED"); (event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED");
/* Perform service discovery */ /* Perform service discovery */
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->cache_assoc.conn_handle,
blecent_on_disc_complete, NULL); blecent_on_disc_complete, NULL);
if(rc != 0) { if(rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -44,6 +44,8 @@ blecent_l2cap_coc_send_data(struct ble_l2cap_chan *chan)
int rc = 0; int rc = 0;
int len = 512; int len = 512;
uint8_t value[len]; uint8_t value[len];
int retry = 0;
const int max_retry = 10;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
value[i] = i; value[i] = i;
@@ -53,20 +55,32 @@ blecent_l2cap_coc_send_data(struct ble_l2cap_chan *chan)
sdu_rx_data = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); sdu_rx_data = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (sdu_rx_data == NULL) { if (sdu_rx_data == NULL) {
vTaskDelay(10 / portTICK_PERIOD_MS); vTaskDelay(10 / portTICK_PERIOD_MS);
sdu_rx_data = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); retry++;
} }
} while (sdu_rx_data == NULL); } while (sdu_rx_data == NULL && retry < max_retry);
if (sdu_rx_data == NULL) {
MODLOG_DFLT(ERROR, "Failed to alloc mbuf after %d retries", max_retry);
return;
}
os_mbuf_append(sdu_rx_data, value, len); os_mbuf_append(sdu_rx_data, value, len);
print_mbuf_data(sdu_rx_data); print_mbuf_data(sdu_rx_data);
rc = ble_l2cap_send(chan, sdu_rx_data); rc = ble_l2cap_send(chan, sdu_rx_data);
while (rc == BLE_HS_ESTALLED) { retry = 0;
while (rc == BLE_HS_ESTALLED && retry < max_retry) {
MODLOG_DFLT(INFO, "Send stalled, waiting for credits (retry=%d)", retry);
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
rc = ble_l2cap_send(chan, sdu_rx_data); rc = ble_l2cap_send(chan, sdu_rx_data);
retry++;
} }
if (rc == BLE_HS_ESTALLED) {
MODLOG_DFLT(INFO, "Send still stalled after %d retries, returning", retry);
return;
}
if (rc == 0) { if (rc == 0) {
MODLOG_DFLT(INFO, "Data sent successfully"); MODLOG_DFLT(INFO, "Data sent successfully");
} else { } else {
@@ -75,6 +89,7 @@ blecent_l2cap_coc_send_data(struct ble_l2cap_chan *chan)
} }
} }
/** /**
* After connection is established on GAP layer, service discovery is performed. On * After connection is established on GAP layer, service discovery is performed. On
* it's completion, this API is called for making a connection is on L2CAP layer. * it's completion, this API is called for making a connection is on L2CAP layer.
@@ -83,11 +98,25 @@ static void
blecent_l2cap_coc_on_disc_complete(const struct peer *peer, int status, void *arg) blecent_l2cap_coc_on_disc_complete(const struct peer *peer, int status, void *arg)
{ {
uint16_t psm = 0x1002; uint16_t psm = 0x1002;
struct os_mbuf *sdu_rx; struct os_mbuf *sdu_rx = NULL;
int rc;
if (status != 0) {
MODLOG_DFLT(WARN, "Service discovery failed (status=%d), skip L2CAP COC", status);
return;
}
sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
ble_l2cap_connect(conn_handle_coc, psm, MTU, sdu_rx, blecent_l2cap_coc_event_cb, if (sdu_rx == NULL) {
NULL); MODLOG_DFLT(ERROR, "Failed to allocate sdu_rx");
return;
}
rc = ble_l2cap_connect(conn_handle_coc, psm, MTU, sdu_rx, blecent_l2cap_coc_event_cb, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "L2CAP COC connect failed, rc=%d", rc);
os_mbuf_free_chain(sdu_rx);
}
} }
/** /**
@@ -265,22 +294,31 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* The device has to advertise support for L2CAP COC UUID (0x1812). /* The device has to advertise support for L2CAP COC UUID (0x1812).
*/ */
do { do {
if (offset + 1 > disc->length_data) {
break;
}
ad_struct_len = disc->data[offset]; ad_struct_len = disc->data[offset];
if (!ad_struct_len) { if (!ad_struct_len) {
break; break;
} }
/* Search if ANS UUID is advertised */ if (offset + ad_struct_len + 1 > disc->length_data) {
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { break;
if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x12 ) { }
return 1;
} /* AD Type (1) + UUID16 (2) requires at least 4 bytes total */
if (ad_struct_len >= 3 &&
disc->data[offset + 1] == 0x03 &&
disc->data[offset + 2] == 0x18 &&
disc->data[offset + 3] == 0x12) {
return 1;
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
} while ( offset < disc->length_data ); } while (offset < disc->length_data);
return 0; return 0;
} }
#else #else
@@ -340,6 +340,9 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
bleprph_print_conn_desc(&desc); bleprph_print_conn_desc(&desc);
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) >= 1 #if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) >= 1
rc = ble_l2cap_create_server(psm, MTU, bleprph_l2cap_coc_event_cb, NULL); rc = ble_l2cap_create_server(psm, MTU, bleprph_l2cap_coc_event_cb, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to create L2CAP CoC server; rc=%d", rc);
}
#endif #endif
} }
return 0; return 0;
@@ -13,6 +13,8 @@
#include "services/gatt/ble_svc_gatt.h" #include "services/gatt/ble_svc_gatt.h"
#include "multi_adv.h" #include "multi_adv.h"
static uint8_t gatt_svr_chr_val = 0x01; /* Example characteristic value */
static const ble_uuid128_t gatt_svr_svc_uuid = static const ble_uuid128_t gatt_svr_svc_uuid =
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
@@ -81,24 +83,39 @@ static int
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle, gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) struct ble_gatt_access_ctxt *ctxt, void *arg)
{ {
int rc = 0;
switch (ctxt->op) { switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: case BLE_GATT_ACCESS_OP_READ_CHR:
MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n", MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n",
conn_handle, attr_handle); conn_handle, attr_handle);
return 0; rc = os_mbuf_append(ctxt->om,
&gatt_svr_chr_val,
sizeof(gatt_svr_chr_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
break;
case BLE_GATT_ACCESS_OP_WRITE_CHR: case BLE_GATT_ACCESS_OP_WRITE_CHR:
MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d", MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d",
conn_handle, attr_handle); conn_handle, attr_handle);
return 0; rc = os_mbuf_copydata(ctxt->om,
0,
sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
break;
case BLE_GATT_ACCESS_OP_READ_DSC: case BLE_GATT_ACCESS_OP_READ_DSC:
MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n", MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n",
conn_handle, attr_handle); conn_handle, attr_handle);
return 0; rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val,
sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
break;
case BLE_GATT_ACCESS_OP_WRITE_DSC: case BLE_GATT_ACCESS_OP_WRITE_DSC:
goto unknown; goto unknown;
break;
default: default:
goto unknown; goto unknown;
@@ -70,6 +70,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
uint8_t adv_handle; uint8_t adv_handle;
uint8_t subevent; uint8_t subevent;
uint8_t phy_mask; uint8_t phy_mask;
uint8_t actual_sent = 0;
switch (event->type) { switch (event->type) {
@@ -101,26 +102,38 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
event->periodic_adv_subev_data_req.subevent_data_count); event->periodic_adv_subev_data_req.subevent_data_count);
sent_num = event->periodic_adv_subev_data_req.subevent_data_count; sent_num = event->periodic_adv_subev_data_req.subevent_data_count;
for (uint8_t i = 0; i < sent_num; i++) { for (uint8_t i = 0; i < sent_num; i++) {
data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0); data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0);
if (!data) { if (!data) {
ESP_LOGE(TAG, "No memory, %d", i); ESP_LOGE(TAG, "No memory at subevt %d", i);
// Free previously allocated data to prevent memory leak
for (uint8_t j = 0; j < actual_sent; j++) {
os_mbuf_free_chain(sub_data_params[j].data);
sub_data_params[j].data = NULL;
}
actual_sent = 0; // Do not call set_periodic_adv_subev_data
break; break;
} }
sub = (i + event->periodic_adv_subev_data_req.subevent_start) % BLE_PAWR_NUM_SUBEVTS; sub = (i + event->periodic_adv_subev_data_req.subevent_start) % BLE_PAWR_NUM_SUBEVTS;
memset(&sub_data_pattern[1], sub, BLE_PAWR_SUB_DATA_LEN - 1); memset(&sub_data_pattern[1], sub, BLE_PAWR_SUB_DATA_LEN - 1);
os_mbuf_append(data, sub_data_pattern, BLE_PAWR_SUB_DATA_LEN); if (os_mbuf_append(data, sub_data_pattern, BLE_PAWR_SUB_DATA_LEN) != 0) {
os_mbuf_free_chain(data);
break;
}
sub_data_params[i].subevent = sub; sub_data_params[i].subevent = sub;
sub_data_params[i].response_slot_start = 0; sub_data_params[i].response_slot_start = 0;
sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS; sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS;
sub_data_params[i].data = data; sub_data_params[i].data = data;
sub_data_pattern[0]++; sub_data_pattern[0]++;
actual_sent++;
} }
if (actual_sent > 0) {
rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle, rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle,
sent_num, sub_data_params); actual_sent, sub_data_params);
if (rc) { if (rc) {
ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc); ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc);
}
} }
return 0; return 0;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -114,11 +114,11 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
struct ble_gap_ext_disc_desc *disc = ((struct ble_gap_ext_disc_desc *)(&event->ext_disc)); struct ble_gap_ext_disc_desc *disc = ((struct ble_gap_ext_disc_desc *)(&event->ext_disc));
if (disc->sid == 2 && synced == 0) { if (disc->sid == 2 && synced == 0) {
synced++; synced++;
const ble_addr_t addr; ble_addr_t addr;
uint8_t adv_sid; uint8_t adv_sid;
struct ble_gap_periodic_sync_params params; struct ble_gap_periodic_sync_params params;
int rc; int rc;
memcpy((void *)&addr, (void *)&disc->addr, sizeof(disc->addr)); memcpy(&addr, &disc->addr, sizeof(disc->addr));
memcpy(&adv_sid, &disc->sid, sizeof(disc->sid)); memcpy(&adv_sid, &disc->sid, sizeof(disc->sid));
params.skip = 10; params.skip = 10;
params.sync_timeout = 1000; params.sync_timeout = 1000;
@@ -236,6 +236,7 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6];
uint32_t peer_addr[6]; uint32_t peer_addr[6];
uint8_t type = 0;
memset(peer_addr, 0x0, sizeof peer_addr); memset(peer_addr, 0x0, sizeof peer_addr);
@@ -252,7 +253,7 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* Conversion */ /* Conversion */
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
test_addr[i] = (uint8_t )peer_addr[i]; test_addr[5 - i] = (uint8_t )peer_addr[i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
@@ -264,21 +265,20 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
*/ */
do { do {
ad_struct_len = disc->data[offset]; ad_struct_len = disc->data[offset];
if (!ad_struct_len || (offset + ad_struct_len + 1 > disc->length_data)) {
if (!ad_struct_len) {
break; break;
} }
type = disc->data[offset + 1];
/* Search if LE PHY UUID is advertised */ if ((type == 0x02 || type == 0x03) && ad_struct_len >= 3) {
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { /* Scan UUID bytes for LE_PHY_UUID16 (little-endian: 0xF2 0xAB) */
if ( disc->data[offset + 2] == 0xAB && disc->data[offset + 3] == 0xF2 ) { for (int i = 2; i + 1 < ad_struct_len; i += 2) {
return 1; if (disc->data[offset + i] == 0xF2 && disc->data[offset + i + 1] == 0xAB) {
return 1;
}
} }
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
} while (offset < disc->length_data);
} while ( offset < disc->length_data );
return 0; return 0;
} }
@@ -484,42 +484,37 @@ blecent_on_sync(void)
rc = ble_hs_util_ensure_addr(0); rc = ble_hs_util_ensure_addr(0);
assert(rc == 0); assert(rc == 0);
s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK;
all_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK; all_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK;
set_default_le_phy(all_phy, all_phy); set_default_le_phy(all_phy, all_phy);
if (s_current_phy != BLE_HCI_LE_PHY_1M_PREF_MASK) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) &&
/* Check if peer address is set in EXAMPLE_PEER_ADDR */ (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && /* User wants to connect on 2M or coded phy directly */
(strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%lx:%lx:%lx:%lx:%lx:%lx",
/* User wants to connect on 2M or coded phy directly */ &peer_addr[5], &peer_addr[4], &peer_addr[3],
sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%lx:%lx:%lx:%lx:%lx:%lx", &peer_addr[2], &peer_addr[1], &peer_addr[0]);
&peer_addr[5], &peer_addr[4], &peer_addr[3],
&peer_addr[2], &peer_addr[1], &peer_addr[0]);
/* Conversion */ /* Conversion */
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
test_addr[i] = (uint8_t )peer_addr[i]; test_addr[i] = (uint8_t )peer_addr[i];
}
for(ii = 0 ;ii < 6; ii++)
conn_addr.val[ii] = test_addr[ii];
conn_addr.type = 0;
vTaskDelay(300);
if (s_current_phy == BLE_HCI_LE_PHY_2M_PREF_MASK)
s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK ;
ble_gap_ext_connect(0, &conn_addr, 30000, s_current_phy,
NULL, NULL, NULL, blecent_gap_event, NULL);
} }
for(ii = 0 ;ii < 6; ii++)
conn_addr.val[ii] = test_addr[ii];
conn_addr.type = 0;
vTaskDelay(300);
s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK ;
ble_gap_ext_connect(0, &conn_addr, 30000, s_current_phy,
NULL, NULL, NULL, blecent_gap_event, NULL);
} }
else { else {
/* Begin scanning for a peripheral to connect to. */ s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK;
/* Begin scanning for a peripheral to connect to. */
blecent_scan(); blecent_scan();
} }
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -69,22 +69,26 @@ gatt_svr_chr_access_le_phy(uint16_t conn_handle, uint16_t attr_handle,
case BLE_GATT_ACCESS_OP_WRITE_CHR: case BLE_GATT_ACCESS_OP_WRITE_CHR:
len = OS_MBUF_PKTLEN(ctxt->om); len = OS_MBUF_PKTLEN(ctxt->om);
if (len > 0) { if (len == 0) {
le_phy_val = (uint8_t *)malloc(len * sizeof(uint8_t)); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
if (le_phy_val) {
rc = ble_hs_mbuf_to_flat(ctxt->om, le_phy_val, len, &copied_len);
if (rc == 0) {
MODLOG_DFLT(INFO, "Write received of len = %d", copied_len);
return 0;
} else {
MODLOG_DFLT(ERROR, "Failed to receive write characteristic");
}
}
} }
break; le_phy_val = (uint8_t *)malloc(len);
if (le_phy_val == NULL) {
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
rc = ble_hs_mbuf_to_flat(ctxt->om, le_phy_val, len, &copied_len);
free(le_phy_val);
if (rc == 0) {
MODLOG_DFLT(INFO, "Write received of len = %d", (int)copied_len);
return 0;
}
MODLOG_DFLT(ERROR, "Failed to receive write characteristic");
return BLE_ATT_ERR_INSUFFICIENT_RES;
default: default:
break; return BLE_ATT_ERR_UNLIKELY;
} }
} }
@@ -70,7 +70,10 @@ ble_prox_cent_on_write(uint16_t conn_handle,
const struct peer_chr *chr; const struct peer_chr *chr;
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Error: peer not found for conn_handle=%d", conn_handle);
return 0;
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLE_SVC_TX_POWER_UUID16), BLE_UUID16_DECLARE(BLE_SVC_TX_POWER_UUID16),
BLE_UUID16_DECLARE(BLE_SVC_PROX_CHR_UUID16_TX_PWR_LVL)); BLE_UUID16_DECLARE(BLE_SVC_PROX_CHR_UUID16_TX_PWR_LVL));
@@ -247,7 +250,7 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* Conversion */ /* Conversion */
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
test_addr[i] = (uint8_t )peer_addr[i]; test_addr[5 - i] = (uint8_t )peer_addr[i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
@@ -266,9 +269,15 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
} }
/* Search if Proximity Sensor (Link loss) UUID is advertised */ /* Search if Proximity Sensor (Link loss) UUID is advertised */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (disc->data[offset + 1] == 0x03) {
if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x03 ) { int uuid_offset = offset + 2;
return 1; int uuid_end = offset + 1 + disc->data[offset]; // len includes type+data
while (uuid_offset + 1 < uuid_end) {
// BLE uses little-endian: 0x1803 is stored as 0x03 0x18
if (disc->data[uuid_offset] == 0x03 && disc->data[uuid_offset + 1] == 0x18) {
return 1;
}
uuid_offset += 2;
} }
} }
@@ -553,7 +562,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
#else #else
#if MYNEWT_VAL(BLE_GATTC) #if MYNEWT_VAL(BLE_GATTC)
/*** Go for service discovery after encryption has been successfully enabled ***/ /*** Go for service discovery after encryption has been successfully enabled ***/
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->enc_change.conn_handle,
ble_prox_cent_on_disc_complete, NULL); ble_prox_cent_on_disc_complete, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -572,7 +581,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
event->cache_assoc.status, event->cache_assoc.status,
(event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED"); (event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED");
/* Perform service discovery */ /* Perform service discovery */
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->cache_assoc.conn_handle,
blecent_on_disc_complete, NULL); blecent_on_disc_complete, NULL);
if(rc != 0) { if(rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -636,7 +645,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
void void
ble_prox_cent_path_loss_task(void *pvParameters) ble_prox_cent_path_loss_task(void *pvParameters)
{ {
int8_t rssi; int8_t rssi = 0;
int rc; int rc;
int path_loss; int path_loss;
@@ -685,7 +694,10 @@ ble_prox_cent_link_loss_task(void *pvParameters)
while (1) { while (1) {
for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) { for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) {
if (disconn_peer[i].link_lost && disconn_peer[i].addr != NULL) { if (disconn_peer[i].link_lost && disconn_peer[i].addr != NULL) {
MODLOG_DFLT(INFO, "Link lost for device with conn_handle %d", i); MODLOG_DFLT(INFO, "Link lost for peer %02x:%02x:%02x:%02x:%02x:%02x, slot %d",
disconn_peer[i].addr[5], disconn_peer[i].addr[4],
disconn_peer[i].addr[3], disconn_peer[i].addr[2],
disconn_peer[i].addr[1], disconn_peer[i].addr[0], i);
} }
} }
vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS);
@@ -23,6 +23,7 @@ static int ble_spp_client_gap_event(struct ble_gap_event *event, void *arg);
QueueHandle_t spp_common_uart_queue = NULL; QueueHandle_t spp_common_uart_queue = NULL;
void ble_store_config_init(void); void ble_store_config_init(void);
uint16_t attribute_handle[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1]; uint16_t attribute_handle[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1];
static SemaphoreHandle_t g_attr_handle_mutex = NULL;
static void ble_spp_client_scan(void); static void ble_spp_client_scan(void);
static ble_addr_t connected_addr[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1]; static ble_addr_t connected_addr[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1];
@@ -73,8 +74,19 @@ ble_spp_client_set_handle(const struct peer *peer)
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID), BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID),
BLE_UUID16_DECLARE(GATT_SPP_CHR_UUID)); BLE_UUID16_DECLARE(GATT_SPP_CHR_UUID));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer lacks SPP characteristic\n");
return;
}
if (g_attr_handle_mutex != NULL) {
xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY);
}
attribute_handle[peer->conn_handle] = chr->chr.val_handle; attribute_handle[peer->conn_handle] = chr->chr.val_handle;
MODLOG_DFLT(INFO, "attribute_handle %x\n", attribute_handle[peer->conn_handle]); MODLOG_DFLT(INFO, "attribute_handle %x\n", attribute_handle[peer->conn_handle]);
if (g_attr_handle_mutex != NULL) {
xSemaphoreGive(g_attr_handle_mutex);
}
dsc = peer_dsc_find_uuid(peer, dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID), BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID),
@@ -295,8 +307,10 @@ ble_spp_client_gap_event(struct ble_gap_event *event, void *arg)
MODLOG_DFLT(INFO, "Connection established "); MODLOG_DFLT(INFO, "Connection established ");
rc = ble_gap_conn_find(event->connect.conn_handle, &desc); rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0); assert(rc == 0);
memcpy(&connected_addr[event->connect.conn_handle].val, desc.peer_id_addr.val, if (event->connect.conn_handle <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
PEER_ADDR_VAL_SIZE); memcpy(&connected_addr[event->connect.conn_handle].val, desc.peer_id_addr.val,
PEER_ADDR_VAL_SIZE);
}
print_conn_desc(&desc); print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
@@ -333,7 +347,13 @@ ble_spp_client_gap_event(struct ble_gap_event *event, void *arg)
/* Forget about peer. */ /* Forget about peer. */
memset(&connected_addr[event->disconnect.conn.conn_handle].val, 0, PEER_ADDR_VAL_SIZE); memset(&connected_addr[event->disconnect.conn.conn_handle].val, 0, PEER_ADDR_VAL_SIZE);
if (g_attr_handle_mutex != NULL) {
xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY);
}
attribute_handle[event->disconnect.conn.conn_handle] = 0; attribute_handle[event->disconnect.conn.conn_handle] = 0;
if (g_attr_handle_mutex != NULL) {
xSemaphoreGive(g_attr_handle_mutex);
}
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
/* Resume scanning. */ /* Resume scanning. */
@@ -422,19 +442,39 @@ void ble_client_uart_task(void *pvParameters)
} }
memset(temp, 0x0, event.size); memset(temp, 0x0, event.size);
uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY); uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY);
for ( i = 0; i <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { /* Collect valid connections while holding mutex to prevent race conditions */
struct {
uint16_t conn_handle;
uint16_t attr_handle;
} valid_conns[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1];
int valid_count = 0;
if (g_attr_handle_mutex != NULL) {
xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY);
}
for (i = 0; i <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if (attribute_handle[i] != 0) { if (attribute_handle[i] != 0) {
#if MYNEWT_VAL(BLE_GATTC) valid_conns[valid_count].conn_handle = i;
rc = ble_gattc_write_flat(i, attribute_handle[i], temp, event.size, NULL, NULL); valid_conns[valid_count].attr_handle = attribute_handle[i];
if (rc == 0) { valid_count++;
ESP_LOGI(tag, "Write in uart task success!");
} else {
ESP_LOGI(tag, "Error in writing characteristic rc=%d", rc);
}
#endif
vTaskDelay(10);
} }
} }
if (g_attr_handle_mutex != NULL) {
xSemaphoreGive(g_attr_handle_mutex);
}
/* Write to all valid connections (outside mutex to avoid blocking) */
for (i = 0; i < valid_count; i++) {
#if MYNEWT_VAL(BLE_GATTC)
rc = ble_gattc_write_flat(valid_conns[i].conn_handle, valid_conns[i].attr_handle, temp, event.size, NULL, NULL);
if (rc == 0) {
ESP_LOGI(tag, "Write in uart task success!");
} else {
ESP_LOGI(tag, "Error in writing characteristic rc=%d", rc);
}
#endif
vTaskDelay(10);
}
free(temp); free(temp);
} }
break; break;
@@ -484,6 +524,13 @@ app_main(void)
return; return;
} }
/* Initialize mutex for attribute_handle protection */
g_attr_handle_mutex = xSemaphoreCreateMutex();
if (g_attr_handle_mutex == NULL) {
ESP_LOGE(tag, "Failed to create attribute handle mutex");
return;
}
/* Initialize UART driver and start uart task */ /* Initialize UART driver and start uart task */
ble_spp_uart_init(); ble_spp_uart_init();
@@ -163,7 +163,9 @@ ble_spp_server_gap_event(struct ble_gap_event *event, void *arg)
ble_spp_server_print_conn_desc(&event->disconnect.conn); ble_spp_server_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
conn_handle_subs[event->disconnect.conn.conn_handle] = false; if (event->disconnect.conn.conn_handle <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
conn_handle_subs[event->disconnect.conn.conn_handle] = false;
}
/* Connection terminated; resume advertising. */ /* Connection terminated; resume advertising. */
ble_spp_server_advertise(); ble_spp_server_advertise();
@@ -202,7 +204,9 @@ ble_spp_server_gap_event(struct ble_gap_event *event, void *arg)
event->subscribe.cur_notify, event->subscribe.cur_notify,
event->subscribe.prev_indicate, event->subscribe.prev_indicate,
event->subscribe.cur_indicate); event->subscribe.cur_indicate);
conn_handle_subs[event->subscribe.conn_handle] = true; if (event->subscribe.conn_handle <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
conn_handle_subs[event->subscribe.conn_handle] = true;
}
return 0; return 0;
default: default:
@@ -360,6 +364,10 @@ void ble_server_uart_task(void *pvParameters)
if (event.size) { if (event.size) {
uint8_t *ntf; uint8_t *ntf;
ntf = (uint8_t *)malloc(sizeof(uint8_t) * event.size); ntf = (uint8_t *)malloc(sizeof(uint8_t) * event.size);
if (ntf == NULL) {
MODLOG_DFLT(ERROR, "malloc failed for UART data, size=%u", event.size);
continue;
}
memset(ntf, 0x00, event.size); memset(ntf, 0x00, event.size);
uart_read_bytes(UART_NUM_0, ntf, event.size, portMAX_DELAY); uart_read_bytes(UART_NUM_0, ntf, event.size, portMAX_DELAY);
+14 -3
View File
@@ -121,6 +121,10 @@ blecent_on_custom_write(uint16_t conn_handle,
error->status, conn_handle, attr->handle); error->status, conn_handle, attr->handle);
peer = peer_find(conn_handle); peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(WARN,"Peer not found (conn_handle=%d), likely disconnected\n",conn_handle);
return 0;
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
remote_svc_uuid, remote_svc_uuid,
remote_chr_uuid); remote_chr_uuid);
@@ -257,6 +261,7 @@ blecent_on_subscribe(uint16_t conn_handle,
if (peer == NULL) { if (peer == NULL) {
MODLOG_DFLT(ERROR, "Error in finding peer, aborting..."); MODLOG_DFLT(ERROR, "Error in finding peer, aborting...");
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
} }
/* Subscribe to, write to, and read the custom characteristic*/ /* Subscribe to, write to, and read the custom characteristic*/
blecent_custom_gatt_operations(peer); blecent_custom_gatt_operations(peer);
@@ -286,7 +291,10 @@ blecent_on_write(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Error: peer not found for conn_handle=%d", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); // Use conn_handle to avoid dereference
}
dsc = peer_dsc_find_uuid(peer, dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID), BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
@@ -338,7 +346,10 @@ blecent_on_read(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Error: peer not found for conn_handle=%d", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT)); BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
@@ -877,7 +888,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
event->cache_assoc.status, event->cache_assoc.status,
(event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED"); (event->cache_assoc.cache_state == 0) ? "INVALID" : "LOADED");
/* Perform service discovery */ /* Perform service discovery */
rc = peer_disc_all(event->connect.conn_handle, rc = peer_disc_all(event->cache_assoc.conn_handle,
blecent_on_disc_complete, NULL); blecent_on_disc_complete, NULL);
if(rc != 0) { if(rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
@@ -191,6 +191,9 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
/* Allocate response buffer */ /* Allocate response buffer */
om_indication = ble_hs_mbuf_att_pkt(); om_indication = ble_hs_mbuf_att_pkt();
if (om_indication == NULL) {
return BLE_HS_ENOMEM;
}
switch(op_code){ switch(op_code){
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA) #if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
@@ -200,6 +203,7 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
sizeof(new_cumulative_wheel_rev_arr), sizeof(new_cumulative_wheel_rev_arr),
new_cumulative_wheel_rev_arr); new_cumulative_wheel_rev_arr);
if (rc != 0){ if (rc != 0){
os_mbuf_free_chain(om_indication);
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
@@ -216,7 +220,8 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
/* Read new sensor location value*/ /* Read new sensor location value*/
rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location); rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
if (rc != 0){ if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; os_mbuf_free_chain(om_indication);
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
@@ -244,7 +249,8 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
rc = os_mbuf_append(om_indication, &response, sizeof(response)); rc = os_mbuf_append(om_indication, &response, sizeof(response));
if (rc != 0){ if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES; os_mbuf_free_chain(om_indication);
return BLE_ATT_ERR_INSUFFICIENT_RES;
} }
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC) #if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
@@ -252,17 +258,24 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){ if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations, rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
sizeof(csc_supported_sensor_locations)); sizeof(csc_supported_sensor_locations));
} if (rc != 0){
os_mbuf_free_chain(om_indication);
if (rc != 0){ return BLE_ATT_ERR_INSUFFICIENT_RES;
return BLE_ATT_ERR_INSUFFICIENT_RES; }
} }
#endif #endif
rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle, rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle,
om_indication); om_indication);
if (rc != 0) {
goto done;
}
return rc; return rc;
done:
os_mbuf_free_chain(om_indication);
return rc;
} }
static int static int
@@ -200,6 +200,9 @@ blehr_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
notify_state = false;
blehr_tx_hrate_stop();
conn_handle = BLE_HS_CONN_HANDLE_NONE;
/* Connection terminated; resume advertising */ /* Connection terminated; resume advertising */
blehr_advertise(); blehr_advertise();
break; break;
@@ -245,7 +245,9 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
} }
#if MYNEWT_VAL(BLE_POWER_CONTROL) #if MYNEWT_VAL(BLE_POWER_CONTROL)
bleprph_power_control(event->connect.conn_handle); if (event->connect.status == 0) {
bleprph_power_control(event->connect.conn_handle);
}
#endif #endif
return 0; return 0;
@@ -497,6 +499,7 @@ app_main(void)
ret = nimble_port_init(); ret = nimble_port_init();
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to init nimble %d ", ret); ESP_LOGE(tag, "Failed to init nimble %d ", ret);
hci_uart_close();
return; return;
} }
/* Initialize the NimBLE host configuration. */ /* Initialize the NimBLE host configuration. */
@@ -1,11 +1,12 @@
/* /*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/queue.h" #include "freertos/queue.h"
@@ -44,6 +45,7 @@ enum {
}; };
TaskHandle_t s_rx_task_hdl; TaskHandle_t s_rx_task_hdl;
static volatile bool s_shutdown_flag = false;
static void IRAM_ATTR hci_uart_rx_task(void *arg) static void IRAM_ATTR hci_uart_rx_task(void *arg)
{ {
@@ -53,9 +55,15 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
uint32_t len_total_read = 0; uint32_t len_total_read = 0;
uint8_t rx_st = UART_RX_TYPE; uint8_t rx_st = UART_RX_TYPE;
while (1) { while (!s_shutdown_flag) {
len_now_read = uart_read_bytes(UART_NO, &buf[len_total_read], len_to_read, portMAX_DELAY); // Use timeout instead of portMAX_DELAY to allow periodic shutdown flag check
assert(len_now_read == len_to_read); len_now_read = uart_read_bytes(UART_NO, &buf[len_total_read], len_to_read, pdMS_TO_TICKS(100));
// If timeout occurred, continue loop to check shutdown flag again
if (len_now_read == 0) {
continue;
}
len_total_read += len_now_read; len_total_read += len_now_read;
switch (rx_st) { switch (rx_st) {
@@ -130,6 +138,10 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
m = ble_transport_alloc_acl_from_ll(); m = ble_transport_alloc_acl_from_ll();
if (!m) { if (!m) {
ESP_LOGE(TAG, "No buffers"); ESP_LOGE(TAG, "No buffers");
rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
break;
} }
if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) { if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) {
@@ -180,7 +192,7 @@ ble_transport_ll_init(void)
void void
ble_transport_ll_deinit(void) ble_transport_ll_deinit(void)
{ {
hci_uart_close();
} }
int int
@@ -228,6 +240,7 @@ void hci_uart_open(void)
intr_alloc_flags = ESP_INTR_FLAG_IRAM; intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif #endif
s_shutdown_flag = false; // Reset shutdown flag on open
ESP_ERROR_CHECK(uart_driver_install(UART_NO, UART_BUF_SZ * 2, UART_BUF_SZ * 2, 0, NULL, intr_alloc_flags)); ESP_ERROR_CHECK(uart_driver_install(UART_NO, UART_BUF_SZ * 2, UART_BUF_SZ * 2, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(UART_NO, &uart_config)); ESP_ERROR_CHECK(uart_param_config(UART_NO, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NO, UART_TX_PIN, UART_RX_PIN, -1, -1)); ESP_ERROR_CHECK(uart_set_pin(UART_NO, UART_TX_PIN, UART_RX_PIN, -1, -1));
@@ -238,7 +251,26 @@ void hci_uart_open(void)
void hci_uart_close(void) void hci_uart_close(void)
{ {
if (s_rx_task_hdl) { if (s_rx_task_hdl) {
vTaskDelete(s_rx_task_hdl);
s_shutdown_flag = true;
int wait_count = 0;
const int max_wait_count = 5;
TaskHandle_t task_handle = s_rx_task_hdl;
while (wait_count < max_wait_count) {
vTaskDelay(pdMS_TO_TICKS(100));
wait_count++;
}
if (s_rx_task_hdl == task_handle) {
vTaskDelete(s_rx_task_hdl);
vTaskDelay(pdMS_TO_TICKS(100));
}
s_rx_task_hdl = NULL;
} }
uart_driver_delete(UART_NO); uart_driver_delete(UART_NO);
} }
@@ -167,8 +167,10 @@ static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
const char *ip_str = IP_IS_V4(&target_addr) ? inet_ntoa(*ip_2_ip4(&target_addr)) :
inet6_ntoa(*ip_2_ip6(&target_addr));
printf("%" PRIu32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 "ms\n", printf("%" PRIu32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 "ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time); recv_len, ip_str, seqno, ttl, elapsed_time);
} }
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
@@ -177,7 +179,9 @@ static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
ip_addr_t target_addr; ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno); const char *ip_str = IP_IS_V4(&target_addr) ? inet_ntoa(*ip_2_ip4(&target_addr)) :
inet6_ntoa(*ip_2_ip6(&target_addr));
printf("From %s icmp_seq=%d timeout\n", ip_str, seqno);
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -152,9 +152,12 @@ print_adv_fields(const struct ble_hs_adv_fields *fields)
} }
if (fields->name != NULL) { if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1); size_t copy_len = fields->name_len;
memcpy(s, fields->name, fields->name_len); if (copy_len >= sizeof(s)) {
s[fields->name_len] = '\0'; copy_len = sizeof(s) - 1;
}
memcpy(s, fields->name, copy_len);
s[copy_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s); fields->name_is_complete ? "" : "in", s);
} }
@@ -40,7 +40,7 @@ chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
int int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr); chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
static struct peer_chr * static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle, peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
struct peer_chr **out_prev); struct peer_chr **out_prev);
static void static void
peer_disc_chrs(struct peer *peer); peer_disc_chrs(struct peer *peer);
@@ -341,7 +341,7 @@ peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
return BLE_HS_EUNKNOWN; return BLE_HS_EUNKNOWN;
} }
chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); chr = peer_chr_find(svc, gatt_chr->val_handle, &prev);
if (chr != NULL) { if (chr != NULL) {
/* Characteristic already discovered. */ /* Characteristic already discovered. */
return 0; return 0;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -32,7 +32,7 @@ static int enter_passkey_handler(int argc, char *argv[])
return -1; return -1;
} }
sscanf(argv[1], "%s", pkey); sscanf(argv[1], "%7s", pkey);
ESP_LOGI("You entered", "%s %s", argv[0], argv[1]); ESP_LOGI("You entered", "%s %s", argv[0], argv[1]);
num = pkey[0]; num = pkey[0];
@@ -45,7 +45,9 @@ static int enter_passkey_handler(int argc, char *argv[])
xQueueSend(cli_handle, &key, 0); xQueueSend(cli_handle, &key, 0);
} }
} else { } else {
sscanf(pkey, "%d", &key); if (sscanf(pkey, "%d", &key) != 1) {
key = 0;
}
xQueueSend(cli_handle, &key, 0); xQueueSend(cli_handle, &key, 0);
} }
@@ -84,14 +86,14 @@ static void scli_task(void *arg)
QueueHandle_t uart_queue; QueueHandle_t uart_queue;
uart_event_t event; uart_event_t event;
uart_driver_install(uart_num, 256, 0, 8, &uart_queue, 0); ESP_ERROR_CHECK(uart_driver_install(uart_num, 256, 0, 8, &uart_queue, 0));
/* Initialize the console */ /* Initialize the console */
esp_console_config_t console_config = { esp_console_config_t console_config = {
.max_cmdline_args = 8, .max_cmdline_args = 8,
.max_cmdline_length = 256, .max_cmdline_length = 256,
}; };
esp_console_init(&console_config); ESP_ERROR_CHECK(esp_console_init(&console_config));
while (!stop) { while (!stop) {
i = 0; i = 0;
@@ -107,6 +109,9 @@ static void scli_task(void *arg)
} }
if (event.type == UART_DATA) { if (event.type == UART_DATA) {
while (uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) { while (uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) {
if (i >= sizeof(linebuf) - 1) {
break;
}
if (linebuf[i] == '\r') { if (linebuf[i] == '\r') {
uart_write_bytes(uart_num, "\r\n", 2); uart_write_bytes(uart_num, "\r\n", 2);
} else { } else {
@@ -115,12 +120,16 @@ static void scli_task(void *arg)
i++; i++;
} }
} }
} while ((i < 255) && linebuf[i - 1] != '\r');
} while ((i < 255) && (i == 0 || linebuf[i - 1] != '\r'));
if (stop) { if (stop) {
break; break;
} }
/* Remove the truncating \r\n */ /* Remove the truncating \r\n */
linebuf[strlen((char *)linebuf) - 1] = '\0'; size_t len = strlen((char *)linebuf);
if (len > 0) {
linebuf[len - 1] = '\0';
}
ret = esp_console_run((char *) linebuf, &cmd_ret); ret = esp_console_run((char *) linebuf, &cmd_ret);
if (ret < 0) { if (ret < 0) {
break; break;
@@ -144,3 +153,40 @@ int scli_init(void)
} }
return ESP_OK; return ESP_OK;
} }
int scli_deinit(void)
{
if (cli_task == NULL) {
return ESP_OK; // Already deinitialized
}
// Signal task to exit
stop = 1;
// Wait for task to exit (it will clean up UART and console)
int timeout_ms = 200;
while (timeout_ms > 0 && cli_task != NULL && eTaskGetState(cli_task) != eDeleted) {
vTaskDelay(pdMS_TO_TICKS(10));
timeout_ms -= 10;
}
// Force delete if still running (shouldn't happen if task exits properly)
if (cli_task != NULL && eTaskGetState(cli_task) != eDeleted) {
vTaskDelete(cli_task);
// If force-deleted, clean up resources manually
uart_driver_delete(0);
esp_console_deinit();
}
cli_task = NULL;
// Clean up queue
if (cli_handle != NULL) {
vQueueDelete(cli_handle);
cli_handle = NULL;
}
// Reset stop flag
stop = 0;
return ESP_OK;
}
@@ -46,9 +46,16 @@ static int dtm_set_ble_tx_power_command(int argc, char **argv)
static int dtm_get_ble_tx_power_command(int argc, char **argv) static int dtm_get_ble_tx_power_command(int argc, char **argv)
{ {
esp_power_level_t power_level = 0xFF; esp_power_level_t power_level;
power_level = esp_ble_tx_power_get_enhanced(ESP_BLE_ENHANCED_PWR_TYPE_DEFAULT, 0); power_level = esp_ble_tx_power_get_enhanced(ESP_BLE_ENHANCED_PWR_TYPE_DEFAULT, 0);
printf("\nCurrent BLE TX power is %d level\n", power_level);
if (power_level == ESP_PWR_LVL_INVALID) {
ESP_LOGI(__func__,"TX power is not available!\n");
return 1;
}
ESP_LOGI(__func__,"\nCurrent BLE TX power is %d level\n", power_level);
return 0; return 0;
} }
@@ -227,15 +227,21 @@ bleprph_advertise(void)
#endif #endif
#if MYNEWT_VAL(BLE_POWER_CONTROL) #if MYNEWT_VAL(BLE_POWER_CONTROL)
static void bleprph_power_control(uint16_t conn_handle) static int bleprph_power_control(uint16_t conn_handle)
{ {
int rc; int rc;
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy
assert (rc == 0); if (rc != 0) {
return rc;
}
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1); rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1);
assert (rc == 0); if (rc != 0) {
return rc;
}
return 0;
} }
#endif #endif
@@ -317,10 +323,13 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
} }
#if MYNEWT_VAL(BLE_POWER_CONTROL) #if MYNEWT_VAL(BLE_POWER_CONTROL)
bleprph_power_control(event->connect.conn_handle); if (event->connect.status == 0) {
rc = bleprph_power_control(event->connect.conn_handle);
ble_gap_event_listener_register(&power_control_event_listener, if (rc == 0) {
bleprph_gap_power_event, NULL); ble_gap_event_listener_register(&power_control_event_listener,
bleprph_gap_power_event, NULL);
}
}
#endif #endif
return 0; return 0;
@@ -63,6 +63,7 @@ static int get_version(int argc, char **argv)
{ {
esp_chip_info_t info; esp_chip_info_t info;
uint32_t flash_size; uint32_t flash_size;
const char *model_str;
esp_chip_info(&info); esp_chip_info(&info);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) { if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
printf("Get flash size failed"); printf("Get flash size failed");
@@ -70,7 +71,18 @@ static int get_version(int argc, char **argv)
} }
printf("IDF Version:%s\r\n", esp_get_idf_version()); printf("IDF Version:%s\r\n", esp_get_idf_version());
printf("Chip info:\r\n"); printf("Chip info:\r\n");
printf("\tmodel:%s\r\n", info.model == CHIP_ESP32 ? "ESP32" : "Unknown"); switch (info.model) {
case CHIP_ESP32: model_str = "ESP32"; break;
case CHIP_ESP32S3: model_str = "ESP32-S3"; break;
case CHIP_ESP32C3: model_str = "ESP32-C3"; break;
case CHIP_ESP32C2: model_str = "ESP32-C2"; break;
case CHIP_ESP32C6: model_str = "ESP32-C6"; break;
case CHIP_ESP32H2: model_str = "ESP32-H2"; break;
case CHIP_ESP32C61: model_str = "ESP32-C61"; break;
case CHIP_ESP32C5: model_str = "ESP32-C5"; break;
default: model_str = "Unknown"; break;
}
printf("\tmodel:%s\r\n", model_str);
printf("\tcores:%d\r\n", info.cores); printf("\tcores:%d\r\n", info.cores);
printf("\tfeature:%s%s%s%s%" PRIu32 "%s\r\n", printf("\tfeature:%s%s%s%s%" PRIu32 "%s\r\n",
info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "", info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
@@ -111,8 +111,8 @@ peer_find(uint16_t conn_handle);
/* Console */ /* Console */
int scli_init(void); int scli_init(void);
void ble_register_cli(void); void ble_register_cli(void);
int scli_receive_key(int *key); int scli_receive_key(int key[6]);
int cli_receive_key(int *key); int cli_receive_key(int key[6]);
int scli_receive_yesno(bool *key); int scli_receive_yesno(bool *key);
void scli_reset_queue(void); void scli_reset_queue(void);
@@ -58,9 +58,10 @@ static int blecent_gap_event(struct ble_gap_event *event, void *arg);
static SemaphoreHandle_t xSemaphore; static SemaphoreHandle_t xSemaphore;
static int mbuf_len_total; static int mbuf_len_total;
static int failure_count; static int failure_count;
static TaskHandle_t throughput_task_handle = NULL;
static int conn_params_def[] = {40, 40, 0, 500, 80, 80}; static int conn_params_def[] = {40, 40, 0, 500, 80, 80};
/* test_data accepts test_name and test_time from CLI */ /* test_data accepts test_name and test_time from CLI */
static int test_data[] = {1, 600, 0}; static int test_data[6] = {1, 600, 0, 0, 0, 0};
static int mtu_def = 512; static int mtu_def = 512;
static ble_addr_t conn_addr; static ble_addr_t conn_addr;
static uint16_t handle; static uint16_t handle;
@@ -225,6 +226,7 @@ static int blecent_read(uint16_t conn_handle, uint16_t val_handle,
return 0; return 0;
err: err:
xSemaphoreGive(xSemaphore);
/* Terminate the connection. */ /* Terminate the connection. */
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
@@ -240,7 +242,8 @@ int update_phy(uint16_t conn_handle, uint8_t phy_mode)
current_phy_updated = 0; current_phy_updated = 0;
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_1M_PREF_MASK, BLE_HCI_LE_PHY_1M_PREF_MASK, 0); rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_1M_PREF_MASK, BLE_HCI_LE_PHY_1M_PREF_MASK, 0);
if(rc != 0) { if(rc != 0) {
ESP_LOGI(tag, "Requested PHY: 1M failed."); current_phy_updated = 1;
ESP_LOGE(tag, "Requested PHY: 1M failed, rc=%d", rc);
} }
else { else {
ESP_LOGI(tag, "Requested PHY: 1M"); ESP_LOGI(tag, "Requested PHY: 1M");
@@ -251,7 +254,8 @@ int update_phy(uint16_t conn_handle, uint8_t phy_mode)
current_phy_updated = 0; current_phy_updated = 0;
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_2M_PREF_MASK, BLE_HCI_LE_PHY_2M_PREF_MASK, 0); rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_2M_PREF_MASK, BLE_HCI_LE_PHY_2M_PREF_MASK, 0);
if(rc != 0) { if(rc != 0) {
ESP_LOGI(tag, "Requested PHY: 2M failed."); current_phy_updated = 1;
ESP_LOGE(tag, "Requested PHY: 2M failed, rc=%d", rc);
} }
else { else {
ESP_LOGI(tag, "Requested PHY: 2M"); ESP_LOGI(tag, "Requested PHY: 2M");
@@ -262,7 +266,8 @@ int update_phy(uint16_t conn_handle, uint8_t phy_mode)
current_phy_updated = 0; current_phy_updated = 0;
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x01); rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x01);
if(rc != 0) { if(rc != 0) {
ESP_LOGI(tag, "Requested PHY: Coded S2 failed."); current_phy_updated = 1;
ESP_LOGE(tag, "Requested PHY: Coded S2 failed, rc=%d", rc);
} }
else { else {
ESP_LOGI(tag, "Requested PHY: Coded S2"); ESP_LOGI(tag, "Requested PHY: Coded S2");
@@ -273,7 +278,8 @@ int update_phy(uint16_t conn_handle, uint8_t phy_mode)
current_phy_updated = 0; current_phy_updated = 0;
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x02); rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x02);
if(rc != 0) { if(rc != 0) {
ESP_LOGI(tag, "Requested PHY: Coded S8 failed."); current_phy_updated = 1;
ESP_LOGE(tag, "Requested PHY: Coded S8 failed, rc=%d", rc);
} }
else { else {
ESP_LOGI(tag, "Requested PHY: Coded S8"); ESP_LOGI(tag, "Requested PHY: Coded S8");
@@ -294,9 +300,29 @@ static void throughput_task(void *arg)
struct peer *peer = (struct peer *)arg; struct peer *peer = (struct peer *)arg;
const struct peer_chr *chr; const struct peer_chr *chr;
const struct peer_dsc *dsc; const struct peer_dsc *dsc;
struct ble_gap_conn_desc desc;
uint16_t conn_handle;
int rc = 0; int rc = 0;
/* Store conn_handle immediately to avoid UAF if peer is freed */
if (peer == NULL) {
ESP_LOGE(tag, "Invalid peer, deleting task");
throughput_task_handle = NULL;
vTaskDelete(NULL);
return;
}
conn_handle = peer->conn_handle;
while (1) { while (1) {
/* Check if connection is still valid using stored conn_handle */
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) {
ESP_LOGI(tag, "Connection lost, deleting throughput task");
throughput_task_handle = NULL;
vTaskDelete(NULL);
return;
}
vTaskDelay(4000 / portTICK_PERIOD_MS); vTaskDelay(4000 / portTICK_PERIOD_MS);
ESP_LOGI(tag, "Format for throughput demo:: throughput read 100"); ESP_LOGI(tag, "Format for throughput demo:: throughput read 100");
printf(" ====================================================================================\n"); printf(" ====================================================================================\n");
@@ -329,12 +355,15 @@ static void throughput_task(void *arg)
scli_reset_queue(); scli_reset_queue();
#if CONFIG_EXAMPLE_EXTENDED_ADV #if CONFIG_EXAMPLE_EXTENDED_ADV
if(test_data[2] >= 0) { if(test_data[2] >= 0) {
rc = update_phy(handle, test_data[2]); rc = update_phy(conn_handle, test_data[2]);
if(rc != 0) { if(rc != 0) {
ESP_LOGI(tag, "Failed to update phy.\n"); ESP_LOGE(tag, "Failed to update phy, rc=%d. Skipping PHY update wait.", rc);
} /* Flag is already set to 1 on failure, but skip wait loop for clarity */
while (!current_phy_updated) { } else {
vTaskDelay(100 / portTICK_PERIOD_MS); /* Wait for PHY update event to complete */
while (!current_phy_updated) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}
} }
} }
#endif #endif
@@ -354,10 +383,17 @@ static void throughput_task(void *arg)
} }
if (test_data[1] > 0) { if (test_data[1] > 0) {
rc = blecent_read(peer->conn_handle, chr->chr.val_handle, rc = blecent_read(conn_handle, chr->chr.val_handle,
blecent_repeat_read, (void *) peer, test_data[1]); blecent_repeat_read, (void *) peer, test_data[1]);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(tag, "Error while reading from GATTS; rc = %d", rc); ESP_LOGE(tag, "Error while reading from GATTS; rc = %d", rc);
/* Delete task on critical error (connection lost or fatal error) */
if (rc == BLE_HS_ENOTCONN || rc == BLE_HS_EDONE) {
ESP_LOGI(tag, "Connection error, deleting throughput task");
throughput_task_handle = NULL;
vTaskDelete(NULL);
return;
}
} }
} else { } else {
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!"); ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
@@ -375,9 +411,16 @@ static void throughput_task(void *arg)
} }
if (test_data[1] > 0) { if (test_data[1] > 0) {
rc = blecent_write(peer->conn_handle, chr->chr.val_handle, (void *) peer, test_data[1]); rc = blecent_write(conn_handle, chr->chr.val_handle, (void *) peer, test_data[1]);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(tag, "Error while writing data; rc = %d", rc); ESP_LOGE(tag, "Error while writing data; rc = %d", rc);
/* Delete task on critical error (connection lost or fatal error) */
if (rc == BLE_HS_ENOTCONN || rc == BLE_HS_EDONE) {
ESP_LOGI(tag, "Connection error, deleting throughput task");
throughput_task_handle = NULL;
vTaskDelete(NULL);
return;
}
} }
} else { } else {
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!"); ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
@@ -403,10 +446,17 @@ static void throughput_task(void *arg)
break; break;
} }
rc = blecent_notify(peer->conn_handle, dsc->dsc.handle, rc = blecent_notify(conn_handle, dsc->dsc.handle,
NULL, (void *) peer, test_data[1]); NULL, (void *) peer, test_data[1]);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(tag, "Subscribing to notification failed; rc = %d ", rc); ESP_LOGE(tag, "Subscribing to notification failed; rc = %d ", rc);
/* Delete task on critical error (connection lost or fatal error) */
if (rc == BLE_HS_ENOTCONN || rc == BLE_HS_EDONE) {
ESP_LOGI(tag, "Connection error, deleting throughput task");
throughput_task_handle = NULL;
vTaskDelete(NULL);
return;
}
} else { } else {
ESP_LOGI(tag, "Subscribed to notifications. Throughput number" ESP_LOGI(tag, "Subscribed to notifications. Throughput number"
" can be seen on peripheral terminal after %d seconds", " can be seen on peripheral terminal after %d seconds",
@@ -421,13 +471,20 @@ static void throughput_task(void *arg)
vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS);
} }
vTaskDelete(NULL);
} }
static void static void
blecent_read_write_subscribe(const struct peer *peer) blecent_read_write_subscribe(const struct peer *peer)
{ {
xTaskCreate(throughput_task, "throughput_task", 4096, (void *) peer, 10, NULL); /* Delete previous task if it exists */
/* Capture handle and set global to NULL atomically to prevent race condition
* where task deletes itself between NULL check and vTaskDelete call */
TaskHandle_t task_to_delete = throughput_task_handle;
throughput_task_handle = NULL;
if (task_to_delete != NULL) {
vTaskDelete(task_to_delete);
}
xTaskCreate(throughput_task, "throughput_task", 4096, (void *) peer, 10, &throughput_task_handle);
return; return;
} }
@@ -515,6 +572,7 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
uint8_t test_addr[6]; uint8_t test_addr[6];
uint32_t peer_addr[6]; uint32_t peer_addr[6];
uint8_t phy_uuid_found = 0; uint8_t phy_uuid_found = 0;
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
@@ -526,9 +584,9 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
&peer_addr[5], &peer_addr[4], &peer_addr[3], &peer_addr[5], &peer_addr[4], &peer_addr[3],
&peer_addr[2], &peer_addr[1], &peer_addr[0]); &peer_addr[2], &peer_addr[1], &peer_addr[0]);
/* Conversion */ /* Conversion */
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
test_addr[i] = (uint8_t )peer_addr[i]; test_addr[5 - i] = (uint8_t )peer_addr[i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
@@ -538,17 +596,21 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* The device has to advertise support LE PHY UUID (0xABF2). /* The device has to advertise support LE PHY UUID (0xABF2).
*/ */
do { do {
ad_struct_len = disc->data[offset]; if (offset + 1 >= (int)disc->length_data) { /* At least read length and type */
if (!ad_struct_len) {
break; break;
} }
ad_struct_len = disc->data[offset];
if (!ad_struct_len || offset + ad_struct_len + 1 > (int)disc->length_data) {
break;
}
/* Search for Complete Local Name (AD type 0x09) */ /* Search for Complete Local Name (AD type 0x09) */
if (disc->data[offset + 1] == 0x09 && phy_uuid_found) { if (disc->data[offset + 1] == 0x09 && phy_uuid_found) {
int name_len = disc->data[offset] - 1; /* Length minus type byte */ int name_len = disc->data[offset] - 1; /* Length minus type byte */
char serv_name[] = "nimble_prph"; char serv_name[] = "nimble_prph";
if (name_len > 0) { if (name_len > 0) {
ESP_LOGI(tag, "Device Name = %.*s",name_len, (char *)&disc->data[offset + 2]); ESP_LOGI(tag, "Device Name = %.*s", name_len, (char *)&disc->data[offset + 2]);
if (name_len == strlen(serv_name) && if (name_len == strlen(serv_name) &&
memcmp(&disc->data[offset + 2], serv_name, name_len) == 0) { memcmp(&disc->data[offset + 2], serv_name, name_len) == 0) {
ESP_LOGI(tag, "central connect to `nimble_prph` success"); ESP_LOGI(tag, "central connect to `nimble_prph` success");
@@ -556,18 +618,18 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
} }
return 0; return 0;
} }
} }
/* Search if LE PHY UUID is advertised */ /* Search if LE PHY UUID is advertised */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03 &&
if ( disc->data[offset + 2] == 0xAB && disc->data[offset + 3] == 0xF2 ) { offset + 3 < (int)disc->length_data &&
phy_uuid_found = 1; disc->data[offset + 2] == 0xAB && disc->data[offset + 3] == 0xF2) {
} phy_uuid_found = 1;
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
} while ( offset < disc->length_data ); } while (offset < (int)disc->length_data);
return phy_uuid_found; return phy_uuid_found;
} }
@@ -788,6 +850,15 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
print_conn_desc(&event->disconnect.conn); print_conn_desc(&event->disconnect.conn);
ESP_LOGI(tag, " "); ESP_LOGI(tag, " ");
/* Delete throughput task if it exists */
/* Capture handle and set global to NULL atomically to prevent race condition
* where task deletes itself between NULL check and vTaskDelete call */
TaskHandle_t task_to_delete = throughput_task_handle;
throughput_task_handle = NULL;
if (task_to_delete != NULL) {
vTaskDelete(task_to_delete);
}
/* Forget about peer. */ /* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
vTaskDelay(200); vTaskDelay(200);
@@ -882,7 +953,9 @@ blecent_on_sync(void)
if (scli_receive_yesno(&yes)) { if (scli_receive_yesno(&yes)) {
if (yes) { if (yes) {
ESP_LOGI(tag, " Enter preferred MTU, format:: `MTU 512` "); ESP_LOGI(tag, " Enter preferred MTU, format:: `MTU 512` ");
if (scli_receive_key(&mtu_def)) { int mtu_buf[6];
if (scli_receive_key(mtu_buf)) {
mtu_def = mtu_buf[0];
ESP_LOGI(tag, "MTU provided by user= %d", mtu_def); ESP_LOGI(tag, "MTU provided by user= %d", mtu_def);
} else { } else {
ESP_LOGD(tag, "No input for setting MTU; use default mtu = %d", mtu_def); ESP_LOGD(tag, "No input for setting MTU; use default mtu = %d", mtu_def);
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -131,9 +131,12 @@ print_adv_fields(const struct ble_hs_adv_fields *fields)
} }
if (fields->name != NULL) { if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1); size_t copy_len = fields->name_len;
memcpy(s, fields->name, fields->name_len); if (copy_len >= sizeof(s)) {
s[fields->name_len] = '\0'; copy_len = sizeof(s) - 1;
}
memcpy(s, fields->name, copy_len);
s[copy_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s); fields->name_is_complete ? "" : "in", s);
} }
@@ -358,7 +358,7 @@ peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
if (prev == NULL) { if (prev == NULL) {
SLIST_INSERT_HEAD(&svc->chrs, chr, next); SLIST_INSERT_HEAD(&svc->chrs, chr, next);
} else { } else {
SLIST_NEXT(prev, next) = chr; SLIST_INSERT_AFTER(prev, chr, next);
} }
return 0; return 0;
@@ -492,31 +492,7 @@ peer_inc_add(struct peer *peer, uint16_t svc_start_handle,
svc = peer_svc_find(peer, gatt_incl_svc->start_handle, &prev); svc = peer_svc_find(peer, gatt_incl_svc->start_handle, &prev);
if (!svc) {
/* secondary service */
svc = os_memblock_get(&peer_svc_pool);
if (svc == NULL) {
/* out of memory */
return BLE_HS_ENOMEM;
}
memset(svc, 0, sizeof *svc);
svc->svc.start_handle = gatt_incl_svc->start_handle;
svc->svc.end_handle = gatt_incl_svc->end_handle;
memcpy(&svc->svc.uuid, &gatt_incl_svc->uuid, sizeof(ble_uuid_any_t));
SLIST_INIT(&svc->chrs);
SLIST_INIT(&svc->incl_svc);
if (prev == NULL) {
SLIST_INSERT_HEAD(&peer->svcs, svc, next);
} else {
SLIST_INSERT_AFTER(prev, svc, next);
}
}
/* Including the services into inlucding list */ /* Including the services into inlucding list */
cur_svc = peer_svc_find_range(peer, gatt_incl_svc->handle); cur_svc = peer_svc_find_range(peer, gatt_incl_svc->handle);
if (cur_svc == NULL) { if (cur_svc == NULL) {
@@ -533,11 +509,38 @@ peer_inc_add(struct peer *peer, uint16_t svc_start_handle,
return 0; return 0;
} }
/* Allocate incl_svc first, before allocating secondary service.
* This ensures we don't leak a secondary service if incl_svc allocation fails. */
incl_svc = os_memblock_get(&peer_incl_svc_pool); incl_svc = os_memblock_get(&peer_incl_svc_pool);
if (incl_svc == NULL) { if (incl_svc == NULL) {
return BLE_HS_ENOMEM; return BLE_HS_ENOMEM;
} }
/* Now allocate secondary service if needed, after incl_svc allocation succeeds */
if (!svc) {
/* secondary service */
svc = os_memblock_get(&peer_svc_pool);
if (svc == NULL) {
/* Free incl_svc before returning */
os_memblock_put(&peer_incl_svc_pool, incl_svc);
return BLE_HS_ENOMEM;
}
memset(svc, 0, sizeof *svc);
svc->svc.start_handle = gatt_incl_svc->start_handle;
svc->svc.end_handle = gatt_incl_svc->end_handle;
memcpy(&svc->svc.uuid, &gatt_incl_svc->uuid, sizeof(ble_uuid_any_t));
SLIST_INIT(&svc->chrs);
SLIST_INIT(&svc->incl_svc);
if (prev == NULL) {
SLIST_INSERT_HEAD(&peer->svcs, svc, next);
} else {
SLIST_INSERT_AFTER(prev, svc, next);
}
}
incl_svc->svc = *gatt_incl_svc; incl_svc->svc = *gatt_incl_svc;
if (incl_svc_prev == NULL) { if (incl_svc_prev == NULL) {
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -8,6 +8,7 @@
#include <ctype.h> #include <ctype.h>
#include "esp_log.h" #include "esp_log.h"
#include <string.h> #include <string.h>
#include <strings.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_console.h> #include <esp_console.h>
#include "esp_vfs_dev.h" #include "esp_vfs_dev.h"
@@ -22,9 +23,23 @@
#define YES_NO_PARAM (5000 / portTICK_PERIOD_MS) #define YES_NO_PARAM (5000 / portTICK_PERIOD_MS)
static QueueHandle_t cli_handle; static QueueHandle_t cli_handle;
static int key[3];
static int conn_param[10]; #define CLI_CONN_PARAM_COUNT 6
static int mtu;
#define CLI_MSG_TYPE_CONN_PARAM 1
#define CLI_MSG_TYPE_MTU 2
#define CLI_MSG_TYPE_THROUGHPUT 3
#define CLI_MSG_TYPE_YESNO 4
struct cli_msg {
int type;
union {
int conn_param[CLI_CONN_PARAM_COUNT];
int mtu;
int key[3];
bool yes;
} data;
};
#define CONSOLE_PROMPT_LEN_MAX (32) #define CONSOLE_PROMPT_LEN_MAX (32)
@@ -41,56 +56,77 @@ static int conn_param_handler(int argc, char *argv[])
return -1; return -1;
} }
sscanf(argv[1], "%d", &conn_param[0]); struct cli_msg msg = {
sscanf(argv[2], "%d", &conn_param[1]); .type = CLI_MSG_TYPE_CONN_PARAM,
sscanf(argv[3], "%d", &conn_param[2]); };
sscanf(argv[4], "%d", &conn_param[3]);
sscanf(argv[5], "%d", &conn_param[4]);
sscanf(argv[6], "%d", &conn_param[5]);
ESP_LOGI("You entered", "%s %d %d %d %d %d %d", argv[0], conn_param[0], conn_param[1], sscanf(argv[1], "%d", &msg.data.conn_param[0]);
conn_param[2], conn_param[3], conn_param[4], conn_param[5]); sscanf(argv[2], "%d", &msg.data.conn_param[1]);
xQueueSend(cli_handle, &conn_param[0], 500 / portTICK_PERIOD_MS); sscanf(argv[3], "%d", &msg.data.conn_param[2]);
sscanf(argv[4], "%d", &msg.data.conn_param[3]);
sscanf(argv[5], "%d", &msg.data.conn_param[4]);
sscanf(argv[6], "%d", &msg.data.conn_param[5]);
ESP_LOGI("You entered", "%s %d %d %d %d %d %d", argv[0], msg.data.conn_param[0], msg.data.conn_param[1],
msg.data.conn_param[2], msg.data.conn_param[3], msg.data.conn_param[4], msg.data.conn_param[5]);
if (cli_handle == NULL) {
ESP_LOGE("CLI", "Queue not initialized");
return -1;
}
xQueueSend(cli_handle, &msg, 500 / portTICK_PERIOD_MS);
return 0; return 0;
} }
static int conn_mtu_handler(int argc, char *argv[]) static int conn_mtu_handler(int argc, char *argv[])
{ {
int ret;
int mtu;
ESP_LOGI("MTU Arguments entered", "%d", argc); ESP_LOGI("MTU Arguments entered", "%d", argc);
if (argc != 2) { if (argc != 2) {
return -1; return -1;
} }
sscanf(argv[1], "%d", &mtu); ret = sscanf(argv[1], "%d", &mtu);
ESP_LOGI("You entered", "%s %d", argv[0], mtu); if (ret != 1) {
xQueueSend(cli_handle, &mtu, 500 / portTICK_PERIOD_MS); return -1;
}
struct cli_msg msg = {
.type = CLI_MSG_TYPE_MTU,
.data.mtu = mtu,
};
if (cli_handle) {
xQueueSend(cli_handle, &msg, 500 / portTICK_PERIOD_MS);
}
return 0; return 0;
} }
static int throughput_demo_handler(int argc, char *argv[]) static int throughput_demo_handler(int argc, char *argv[])
{ {
char pkey[8]; char pkey[8];
struct cli_msg msg = {
.type = CLI_MSG_TYPE_THROUGHPUT,
};
if (argc != 4) { if (argc != 4) {
return -1; return -1;
} }
sscanf(argv[1], "%s", pkey); sscanf(argv[1], "%7s", pkey);
if (strcmp(pkey, "read") == 0) { if (strcmp(pkey, "read") == 0) {
key[0] = 1; msg.data.key[0] = 1;
} else if (strcmp(pkey, "write") == 0) { } else if (strcmp(pkey, "write") == 0) {
key[0] = 2; msg.data.key[0] = 2;
} else if (strcmp(pkey, "notify") == 0) { } else if (strcmp(pkey, "notify") == 0) {
key[0] = 3; msg.data.key[0] = 3;
} else { } else {
key[0] = 0; msg.data.key[0] = 0;
} }
sscanf(argv[2], "%d", &key[1]); sscanf(argv[2], "%d", &msg.data.key[1]);
sscanf(argv[3], "%d", &key[2]); sscanf(argv[3], "%d", &msg.data.key[2]);
ESP_LOGI("Throughput demo handler", "%s %s %d %d", argv[0], argv[1], key[1], key[2]); ESP_LOGI("Throughput demo handler", "%s %s %d %d", argv[0], argv[1], msg.data.key[1], msg.data.key[2]);
xQueueSend(cli_handle, &key[0], 500 / portTICK_PERIOD_MS); xQueueSend(cli_handle, &msg, 500 / portTICK_PERIOD_MS);
return 0; return 0;
} }
@@ -104,32 +140,80 @@ static int yesno_handler(int argc, char *argv[])
return -1; return -1;
} }
sscanf(argv[1], "%s", yesno); sscanf(argv[1], "%3s", yesno);
if (strcmp(yesno, "Yes") || strcmp (yesno, "YES") || strcmp(yesno, "yes")) { if (strcmp(yesno, "Yes") == 0 || strcmp(yesno, "YES") == 0 || strcmp(yesno, "yes") == 0) {
yes = 1; yes = 1;
} else { } else if (strcmp(yesno, "No") == 0 || strcmp(yesno, "NO") == 0 || strcmp(yesno, "no") == 0) {
yes = 0; yes = 0;
} else {
yes = 0; /* invalid input */
} }
ESP_LOGI("User entered", "%s %s", argv[0], yesno); ESP_LOGI("User entered", "%s %s", argv[0], yesno);
xQueueSend(cli_handle, &yes, 500 / portTICK_PERIOD_MS); /* Send as 24-byte buffer to match queue item size */
uint8_t yesno_buf[24] = {0};
yesno_buf[0] = (uint8_t)yes;
if (cli_handle) {
xQueueSend(cli_handle, yesno_buf, 500 / portTICK_PERIOD_MS);
}
return 0; return 0;
} }
int scli_receive_yesno(bool *console_key) int scli_receive_yesno(bool *console_key)
{ {
return xQueueReceive(cli_handle, console_key, YES_NO_PARAM); /* Receive into temporary 24-byte buffer to match queue item size,
* then extract bool value to prevent buffer overflow */
uint8_t temp_buf[24];
int ret = xQueueReceive(cli_handle, temp_buf, YES_NO_PARAM);
if (ret == pdPASS) {
*console_key = (bool)temp_buf[0]; /* Extract first byte as bool */
}
return ret;
} }
int scli_receive_key(int *console_key) int scli_receive_key(int console_key[6])
{ {
return xQueueReceive(cli_handle, console_key, BLE_RX_PARAM); struct cli_msg msg;
if (xQueueReceive(cli_handle, &msg, BLE_RX_PARAM) != pdTRUE) {
return 0;
}
switch (msg.type) {
case CLI_MSG_TYPE_MTU:
console_key[0] = msg.data.mtu;
return 1;
case CLI_MSG_TYPE_CONN_PARAM:
memcpy(console_key, msg.data.conn_param,
sizeof(msg.data.conn_param));
return 1;
case CLI_MSG_TYPE_THROUGHPUT:
memcpy(console_key, msg.data.key, sizeof(msg.data.key));
return 1;
default:
return 0;
}
} }
int cli_receive_key(int *console_key) int cli_receive_key(int console_key[6])
{ {
return xQueueReceive(cli_handle, console_key, BLE_RX_TIMEOUT); struct cli_msg msg;
if (xQueueReceive(cli_handle, &msg, BLE_RX_TIMEOUT) != pdTRUE) {
return 0;
}
switch (msg.type) {
case CLI_MSG_TYPE_CONN_PARAM:
memcpy(console_key, msg.data.conn_param, sizeof(msg.data.conn_param));
return 1;
case CLI_MSG_TYPE_THROUGHPUT:
memcpy(console_key, msg.data.key, sizeof(msg.data.key));
return 1;
case CLI_MSG_TYPE_MTU:
console_key[0] = msg.data.mtu;
return 1;
default:
return 0;
}
} }
void scli_reset_queue(void) void scli_reset_queue(void)
@@ -169,7 +253,7 @@ void ble_register_cli(void)
esp_console_cmd_register(&cmds[i]); esp_console_cmd_register(&cmds[i]);
} }
cli_handle = xQueueCreate( 1, sizeof(int) * 6); cli_handle = xQueueCreate(1, sizeof(struct cli_msg));
if (cli_handle == NULL) { if (cli_handle == NULL) {
return; return;
} }
@@ -507,8 +507,11 @@ void app_main(void)
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Initialize Notify Task */ /* Initialize Notify Task */
xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, NULL); BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, NULL);
if (task_rc != pdPASS) {
ESP_LOGE(tag, "Failed to create notify_task (rc=%d)", task_rc);
return ;
}
#if MYNEWT_VAL(BLE_GATTS) #if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init(); rc = gatt_svr_init();
assert(rc == 0); assert(rc == 0);