mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(nimble): Support NimBLE log compression and decompression over SPI
This commit is contained in:
@@ -15,7 +15,8 @@ if BLE_LOG_ENABLED
|
||||
|
||||
config BLE_LOG_LBM_TRANS_BUF_SIZE
|
||||
int "Total buffer memory per common LBM (bytes)"
|
||||
default 2048
|
||||
default 512 if BT_BLUEDROID_ENABLED
|
||||
default 1024 if BT_NIMBLE_ENABLED
|
||||
help
|
||||
Total buffer memory allocated for each common pool log buffer
|
||||
manager (LBM). This memory is divided equally among internal
|
||||
|
||||
@@ -677,6 +677,7 @@ static void spi_out_log_cb_dump(spi_out_log_cb_t *log_cb)
|
||||
}
|
||||
}
|
||||
|
||||
#if SPI_OUT_HOST_ENABLED || SPI_OUT_MESH_ENABLED || SPI_OUT_HCI_ENABLED || SPI_OUT_LE_AUDIO_ENABLED
|
||||
static void spi_out_update_task_mapping(int idx, void *ptr)
|
||||
{
|
||||
// It is a must to clear task handle after task deletion
|
||||
@@ -684,7 +685,6 @@ static void spi_out_update_task_mapping(int idx, void *ptr)
|
||||
entry->task_handle = NULL;
|
||||
}
|
||||
|
||||
#if SPI_OUT_HOST_ENABLED || SPI_OUT_MESH_ENABLED || SPI_OUT_HCI_ENABLED || SPI_OUT_LE_AUDIO_ENABLED
|
||||
static bool spi_out_get_task_mapping(task_map_t *map, size_t num,
|
||||
spi_out_log_cb_t **log_cb, uint8_t **str_buf)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ set(BLE_MESH_LOG_INDEX_HEADER "\"\"")
|
||||
set(BLE_MESH_TAGS "")
|
||||
set(BLE_MESH_TAGS_PRESERVE "")
|
||||
|
||||
# default config value for host module
|
||||
# default config value for host module (Bluedroid or NimBLE)
|
||||
set(HOST_CODE_PATH "")
|
||||
set(HOST_LOG_INDEX_HEADER "\"\"")
|
||||
set(BLE_HOST_TAGS "")
|
||||
@@ -41,6 +41,20 @@ if(CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE AND CONFIG_BT_BLUEDROID_ENABLED)
|
||||
"${CMAKE_CURRENT_LIST_DIR}/scripts/module_scripts/bluedroid/make_bluedroid_log_macro.py")
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/ble_host_bluedroid_tags.cmake)
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/ble_log/include/${HOST_LOG_INDEX_HEADER}")
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/ble_log/include/${HOST_LOG_INDEX_HEADER}" "")
|
||||
endif()
|
||||
list(APPEND LOG_COMPRESSED_MODULE_CODE_PATH ${HOST_CODE_PATH})
|
||||
elseif(CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE AND CONFIG_BT_NIMBLE_ENABLED)
|
||||
list(APPEND LOG_COMPRESSED_MODULE "BLE_HOST")
|
||||
|
||||
set(HOST_CODE_PATH "host/nimble/nimble/nimble/host")
|
||||
set(HOST_LOG_INDEX_HEADER "nimble_log_index.h")
|
||||
set(BLE_HOST_LOG_SCRIPT_PATH
|
||||
"${CMAKE_CURRENT_LIST_DIR}/scripts/module_scripts/nimble/make_nimble_log_macro.py")
|
||||
set(BLE_HOST_TAGS "MODLOG_DFLT, BLE_HS_LOG")
|
||||
set(BLE_HOST_TAGS_PRESERVE "")
|
||||
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/ble_log/include/${HOST_LOG_INDEX_HEADER}")
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/ble_log/include/${HOST_LOG_INDEX_HEADER}" "")
|
||||
endif()
|
||||
|
||||
@@ -160,9 +160,9 @@ if BLE_COMPRESSED_LOG_ENABLE
|
||||
endif
|
||||
|
||||
menuconfig BLE_HOST_COMPRESSED_LOG_ENABLE
|
||||
bool "Enable BLE Host log compression(Preview, only Bluedroid Host for now)"
|
||||
bool "Enable BLE Host log compression(Preview)"
|
||||
depends on BLE_COMPRESSED_LOG_ENABLE
|
||||
depends on BT_BLUEDROID_ENABLED
|
||||
depends on BT_BLUEDROID_ENABLED || BT_NIMBLE_ENABLED
|
||||
default n
|
||||
help
|
||||
Apply compression to host logs. Requires
|
||||
@@ -182,7 +182,7 @@ if BLE_COMPRESSED_LOG_ENABLE
|
||||
help
|
||||
Maximum output length for a single log
|
||||
|
||||
if BLE_HOST_COMPRESSED_LOG_ENABLE
|
||||
if BLE_HOST_COMPRESSED_LOG_ENABLE && BT_BLUEDROID_ENABLED
|
||||
menu "Select the BTM layer log tag to be compressed"
|
||||
config BLE_BLUEDROID_BTM_ERROR_LOG_COMPRESSION
|
||||
bool "Compress error log of Bluedroid host"
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
#if CONFIG_BLE_COMPRESSED_LOG_ENABLE
|
||||
|
||||
#define BLE_CP_DROP_LOG_PERIOD 256U
|
||||
|
||||
#define BLE_CP_TRY_PUSH(expr) do { \
|
||||
if ((expr) != 0) { \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BUF_NAME(name, idx) name##_buffer##idx
|
||||
#define BUF_MGMT_NAME(name) name##_log_buffer_mgmt
|
||||
|
||||
@@ -42,12 +50,18 @@ INIT_BUFFER_MGMT(mesh, LOG_CP_MAX_LOG_BUFFER_USED_SIMU);
|
||||
char * mesh_last_task_handle = NULL;
|
||||
#endif
|
||||
|
||||
#if CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE
|
||||
#if CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE && CONFIG_BT_BLUEDROID_ENABLED
|
||||
DECLARE_BUFFERS(host, CONFIG_BLE_HOST_COMPRESSED_LOG_BUFFER_LEN, LOG_CP_MAX_LOG_BUFFER_USED_SIMU);
|
||||
INIT_BUFFER_MGMT(host, LOG_CP_MAX_LOG_BUFFER_USED_SIMU);
|
||||
char * host_last_task_handle = NULL;
|
||||
#endif
|
||||
|
||||
#if CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE && CONFIG_BT_NIMBLE_ENABLED
|
||||
DECLARE_BUFFERS(nimble, CONFIG_BLE_HOST_COMPRESSED_LOG_BUFFER_LEN, LOG_CP_MAX_LOG_BUFFER_USED_SIMU);
|
||||
INIT_BUFFER_MGMT(nimble, LOG_CP_MAX_LOG_BUFFER_USED_SIMU);
|
||||
char * nimble_last_task_handle = NULL;
|
||||
#endif
|
||||
|
||||
/* The maximum number of supported parameters is 64 */
|
||||
#define LOG_HEADER(log_type, info) ((log_type << 6) | (info & 0x3f))
|
||||
|
||||
@@ -66,10 +80,15 @@ int ble_compressed_log_cb_get(uint8_t source, ble_cp_log_buffer_mgmt_t **mgmt)
|
||||
last_handle = &mesh_last_task_handle;
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE
|
||||
#if CONFIG_BLE_HOST_COMPRESSED_LOG_ENABLE && (CONFIG_BT_BLUEDROID_ENABLED || CONFIG_BT_NIMBLE_ENABLED)
|
||||
case BLE_COMPRESSED_LOG_OUT_SOURCE_HOST:
|
||||
#if CONFIG_BT_BLUEDROID_ENABLED
|
||||
buffer_mgmt = BUF_MGMT_NAME(host);
|
||||
last_handle = &host_last_task_handle;
|
||||
#elif CONFIG_BT_NIMBLE_ENABLED
|
||||
buffer_mgmt = BUF_MGMT_NAME(nimble);
|
||||
last_handle = &nimble_last_task_handle;
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -80,10 +99,18 @@ int ble_compressed_log_cb_get(uint8_t source, ble_cp_log_buffer_mgmt_t **mgmt)
|
||||
for (int i = 0; i < LOG_CP_MAX_LOG_BUFFER_USED_SIMU; i++) {
|
||||
if (ble_log_cas_acquire(&(buffer_mgmt[i].busy))) {
|
||||
*mgmt = &buffer_mgmt[i];
|
||||
ble_log_cp_push_u8(*mgmt, source);
|
||||
if (ble_log_cp_push_u8(*mgmt, source) != 0) {
|
||||
(*mgmt)->idx = 0;
|
||||
ble_log_cas_release(&((*mgmt)->busy));
|
||||
return -1;
|
||||
}
|
||||
if (*last_handle == NULL ||
|
||||
*last_handle != cur_handle) {
|
||||
ble_log_cp_push_u8(*mgmt, LOG_HEADER(LOG_TYPE_INFO, LOG_TYPE_INFO_TASK_SWITCH));
|
||||
if (ble_log_cp_push_u8(*mgmt, LOG_HEADER(LOG_TYPE_INFO, LOG_TYPE_INFO_TASK_SWITCH)) != 0) {
|
||||
(*mgmt)->idx = 0;
|
||||
ble_log_cas_release(&((*mgmt)->busy));
|
||||
return -1;
|
||||
}
|
||||
*last_handle = cur_handle;
|
||||
}
|
||||
return 0;
|
||||
@@ -108,8 +135,8 @@ int ble_log_compressed_hex_print_internal(ble_cp_log_buffer_mgmt_t *mgmt, uint32
|
||||
{
|
||||
uint8_t arg_type = 0;
|
||||
|
||||
ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_ARGS, args_cnt));
|
||||
ble_log_cp_push_u16(mgmt, log_index);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_ARGS, args_cnt)));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u16(mgmt, log_index));
|
||||
uint8_t size_info_idx = mgmt->idx;
|
||||
uint8_t *cur = &(mgmt->buffer)[mgmt->idx];
|
||||
uint8_t size_info = 0;
|
||||
@@ -117,20 +144,20 @@ int ble_log_compressed_hex_print_internal(ble_cp_log_buffer_mgmt_t *mgmt, uint32
|
||||
for (size_t i = 0; i < args_cnt; i++) {
|
||||
if (i % 2) {
|
||||
arg_type = va_arg(args, size_t);
|
||||
ble_log_cp_push_u8(mgmt, size_info|arg_type);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, size_info|arg_type));
|
||||
size_info = 0;
|
||||
cur++;
|
||||
} else {
|
||||
arg_type = va_arg(args, size_t);
|
||||
if (i == args_cnt - 1) {
|
||||
ble_log_cp_push_u8(mgmt, arg_type);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, arg_type << 4));
|
||||
} else {
|
||||
size_info = arg_type << 4;
|
||||
}
|
||||
}
|
||||
if (arg_type >= ARG_SIZE_TYPE_MAX) {
|
||||
printf("Found invalid arg type %08lx type %d", log_index, arg_type);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,27 +175,31 @@ int ble_log_compressed_hex_print_internal(ble_cp_log_buffer_mgmt_t *mgmt, uint32
|
||||
uint32_t u32v = va_arg(args, size_t);
|
||||
if (likely(u32v)) {
|
||||
if (u32v <= 0xff) {
|
||||
ble_log_cp_push_u8(mgmt, 3);
|
||||
ble_log_cp_push_u8(mgmt, u32v);
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU32, !(i%2));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, 3));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, u32v));
|
||||
BLE_CP_TRY_PUSH(
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU32, !(i%2))
|
||||
);
|
||||
break;
|
||||
} else if (u32v <= 0xffff) {
|
||||
ble_log_cp_push_u8(mgmt, 2);
|
||||
ble_log_cp_push_u16(mgmt, u32v);
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU32, !(i%2));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, 2));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u16(mgmt, u32v));
|
||||
BLE_CP_TRY_PUSH(
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU32, !(i%2))
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
ble_log_cp_push_u32(mgmt, u32v);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u32(mgmt, u32v));
|
||||
}
|
||||
} else {
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_AZU32, !(i%2));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_AZU32, !(i%2)));
|
||||
}
|
||||
break;
|
||||
case ARG_SIZE_TYPE_U64:
|
||||
uint64_t u64v = va_arg(args, uint64_t);
|
||||
if (likely(u64v)) {
|
||||
if (unlikely(u64v >> 48)) {
|
||||
ble_log_cp_push_u64(mgmt, u64v);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u64(mgmt, u64v));
|
||||
} else {
|
||||
uint32_t tmpv = 0;
|
||||
uint8_t lz = 0;
|
||||
@@ -179,35 +210,37 @@ int ble_log_compressed_hex_print_internal(ble_cp_log_buffer_mgmt_t *mgmt, uint32
|
||||
tmpv = u64v >> 32;
|
||||
}
|
||||
lz += __builtin_clz(tmpv) / 8;
|
||||
ble_log_cp_push_u8(mgmt, lz);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, lz));
|
||||
switch (8-lz) {
|
||||
case 5:
|
||||
ble_log_cp_push_u32(mgmt, (uint32_t)u64v);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u32(mgmt, (uint32_t)u64v));
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
ble_log_cp_push_u8(mgmt, (uint8_t)tmpv);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, (uint8_t)tmpv));
|
||||
break;
|
||||
case 6:
|
||||
ble_log_cp_push_u32(mgmt, (uint32_t)u64v);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u32(mgmt, (uint32_t)u64v));
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
ble_log_cp_push_u16(mgmt, (uint16_t)tmpv);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u16(mgmt, (uint16_t)tmpv));
|
||||
break;
|
||||
case 7:
|
||||
ble_log_cp_push_u32(mgmt, (uint32_t)u64v);
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u32(mgmt, (uint32_t)u64v));
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
ble_log_cp_push_u8(mgmt, (uint8_t)tmpv);
|
||||
ble_log_cp_push_u16(mgmt, (uint16_t)(tmpv >> 8));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u8(mgmt, (uint8_t)tmpv));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_push_u16(mgmt, (uint16_t)(tmpv >> 8)));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU64, !(i%2));
|
||||
BLE_CP_TRY_PUSH(
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_LZU64, !(i%2))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_AZU64, !(i%2));
|
||||
BLE_CP_TRY_PUSH(ble_log_cp_update_half_byte(mgmt, size_info_idx + i/2, ARG_SIZE_TYPE_AZU64, !(i%2)));
|
||||
}
|
||||
break;
|
||||
case ARG_SIZE_TYPE_STR:
|
||||
@@ -216,8 +249,7 @@ int ble_log_compressed_hex_print_internal(ble_cp_log_buffer_mgmt_t *mgmt, uint32
|
||||
break;
|
||||
default:
|
||||
printf("Invalid size %d\n", arg_type);
|
||||
assert(0);
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -231,7 +263,10 @@ int ble_log_compressed_hex_printv(uint8_t source, uint32_t log_index, size_t arg
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_log_compressed_hex_print_internal(mgmt, log_index, args_cnt, args);
|
||||
if (ble_log_compressed_hex_print_internal(mgmt, log_index, args_cnt, args) != 0) {
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
}
|
||||
ble_compressed_log_output(source, mgmt->buffer, mgmt->idx);
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
@@ -246,12 +281,19 @@ int ble_log_compressed_hex_print(uint8_t source, uint32_t log_index, size_t args
|
||||
}
|
||||
|
||||
if (args_cnt == 0) {
|
||||
ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_ARGS, 0));
|
||||
ble_log_cp_push_u16(mgmt, log_index);
|
||||
if (ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_ARGS, 0)) != 0 ||
|
||||
ble_log_cp_push_u16(mgmt, log_index) != 0) {
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
va_list args;
|
||||
va_start(args, args_cnt);
|
||||
ble_log_compressed_hex_print_internal(mgmt, log_index, args_cnt, args);
|
||||
if (ble_log_compressed_hex_print_internal(mgmt, log_index, args_cnt, args) != 0) {
|
||||
va_end(args);
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@@ -276,9 +318,13 @@ int ble_log_compressed_hex_print_buf(uint8_t source, uint32_t log_index, uint8_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_BUF, buf_idx));
|
||||
ble_log_cp_push_u16(mgmt, log_index);
|
||||
ble_log_cp_push_buf(mgmt, buf, len);
|
||||
if (ble_log_cp_push_u8(mgmt, LOG_HEADER(LOG_TYPE_HEX_BUF, buf_idx)) != 0 ||
|
||||
ble_log_cp_push_u16(mgmt, log_index) != 0 ||
|
||||
ble_log_cp_push_u16(mgmt, (uint16_t)len) != 0 ||
|
||||
ble_log_cp_push_buf(mgmt, buf, (uint16_t)len) != 0) {
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
}
|
||||
ble_compressed_log_output(source, mgmt->buffer, mgmt->idx);
|
||||
ble_compressed_log_buffer_free(mgmt);
|
||||
return 0;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#define BLE_LOG_SPI_BUS SPI2_HOST
|
||||
#define BLE_LOG_SPI_MAX_TRANSFER_SIZE (10240)
|
||||
#define BLE_LOG_SPI_TRANS_ITVL_MIN_US (30)
|
||||
#define BLE_LOG_SPI_DMA_ALIGN_BYTES (4U)
|
||||
#define BLE_LOG_SPI_ALIGN_LOG_PERIOD (256U)
|
||||
|
||||
/* VARIABLE */
|
||||
BLE_LOG_STATIC bool prph_inited = false;
|
||||
@@ -175,11 +177,26 @@ void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans)
|
||||
BLE_LOG_IRAM_ATTR void ble_log_prph_send_trans(ble_log_prph_trans_t *trans)
|
||||
{
|
||||
spi_transaction_t *spi_trans = (spi_transaction_t *)trans->ctx;
|
||||
uint16_t tx_len = trans->pos;
|
||||
|
||||
/*
|
||||
* SPI slave DMA requires transaction length to be 4-byte aligned.
|
||||
* Pad trailing bytes with zero to reduce transport loss on slave side.
|
||||
*/
|
||||
uint16_t aligned_len = (uint16_t)((tx_len + (BLE_LOG_SPI_DMA_ALIGN_BYTES - 1U)) &
|
||||
~(BLE_LOG_SPI_DMA_ALIGN_BYTES - 1U));
|
||||
if (aligned_len != tx_len) {
|
||||
uint16_t pad_len = (uint16_t)(aligned_len - tx_len);
|
||||
if (aligned_len <= trans->size) {
|
||||
BLE_LOG_MEMSET(trans->buf + tx_len, 0, pad_len);
|
||||
tx_len = aligned_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* CRITICAL:
|
||||
* Bytes to bits length conversion is required for tx, and rxlength must be
|
||||
* cleared regardless of whether it is used for rx as per SPI master driver */
|
||||
spi_trans->length = (trans->pos << 3);
|
||||
spi_trans->length = (tx_len << 3);
|
||||
spi_trans->rxlength = 0;
|
||||
if (spi_device_queue_trans(dev_handle, spi_trans, 0) != ESP_OK) {
|
||||
ble_log_lbm_t *lbm = (ble_log_lbm_t *)trans->owner;
|
||||
|
||||
Reference in New Issue
Block a user