From becb8eac7d23cb757da97e5b9824c936ba50967f Mon Sep 17 00:00:00 2001 From: penglei <1624952103@qq.com> Date: Wed, 3 Dec 2025 11:45:11 +0800 Subject: [PATCH] =?utf8?q?=E5=A2=9E=E5=8A=A0=E8=93=9D=E7=89=99=E6=89=AB?= =?utf8?q?=E6=8F=8F=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- code/sdk/zhihui/utils/custom_utils_at_cmd.c | 59 +++ code/sdk/zhihui/utilscpp/CMakeLists.txt | 2 + code/sdk/zhihui/utilscpp/bt_scan_test.cpp | 517 ++++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 code/sdk/zhihui/utilscpp/bt_scan_test.cpp diff --git a/code/sdk/zhihui/utils/custom_utils_at_cmd.c b/code/sdk/zhihui/utils/custom_utils_at_cmd.c index 02dce28ab..f283b8dce 100644 --- a/code/sdk/zhihui/utils/custom_utils_at_cmd.c +++ b/code/sdk/zhihui/utils/custom_utils_at_cmd.c @@ -1700,6 +1700,64 @@ static uint32_t custom_at_gpio(uint8_t *para, uint32_t paraLen, char *strCommonC return ERRCODE_SUCC; } +// 蓝牙扫描测试AT指令实现 +// 用法: +// AT^BTSCANTEST=1 - 开始扫描 +// AT^BTSCANTEST=2 - 停止扫描 +static uint32_t custom_bt_scan_test(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != CUSTOM_UTILS_SUCCESS) { + wstp_print("BT SCAN TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + + if (argc < 1) { + wstp_print("BT SCAN TEST ATCMD Set Parameter Failed argc %d\n", argc); + wstp_print("Usage: AT^BTSCANTEST=cmd\n"); + wstp_print(" cmd: 1=start scan, 2=stop scan\n"); + return ERRCODE_FAIL; + } + + uint8_t cmd = (uint8_t)atoi((char*)argv[0]); + + switch (cmd) { + case 1: // 开始扫描 + { + extern int custom_bt_scan_test_start(void); + int ret = custom_bt_scan_test_start(); + if (ret == 0) { + wstp_print("BT SCAN TEST START: Success\n"); + } else { + wstp_print("BT SCAN TEST START: Failed, ret=%d\n", ret); + } + } + break; + case 2: // 停止扫描 + { + extern int custom_bt_scan_test_stop(void); + int ret = custom_bt_scan_test_stop(); + if (ret == 0) { + wstp_print("BT SCAN TEST STOP: Success\n"); + } else { + wstp_print("BT SCAN TEST STOP: Failed, ret=%d\n", ret); + } + } + break; + default: + wstp_print("BT SCAN TEST ATCMD Unknown command %d\n", cmd); + wstp_print("Usage: AT^BTSCANTEST=cmd\n"); + wstp_print(" cmd: 1=start scan, 2=stop scan\n"); + return ERRCODE_FAIL; + } + + return ERRCODE_SUCC; +} + static uint32_t custom_factory_bt_test_start(uint8_t *para, uint32_t paraLen, char *strCommonCmd) { UNUSED(strCommonCmd); @@ -2230,6 +2288,7 @@ static const at_cmd_table_t g_custom_utils_cmd[] = { // { (char *)"AT^TPTEST", tp_test_control, (char *)" " }, { (char *)"AT^BTTESTSTART", custom_factory_bt_test_start, (char *)" " }, { (char *)"AT^BTTESTSTOP", custom_factory_bt_test_stop, (char *)" " }, + { (char *)"AT^BTSCANTEST", custom_bt_scan_test, (char *)" " }, // 录音相关AT指令 { (char *)"AT^AUDIOTEST", custom_audio_test, (char *)" " }, diff --git a/code/sdk/zhihui/utilscpp/CMakeLists.txt b/code/sdk/zhihui/utilscpp/CMakeLists.txt index 909995751..1c02c17c9 100644 --- a/code/sdk/zhihui/utilscpp/CMakeLists.txt +++ b/code/sdk/zhihui/utilscpp/CMakeLists.txt @@ -13,6 +13,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/spi_protocol_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/audio_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pcm_volume_calculator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/bt_scan_test.cpp + ) set(PUBLIC_HEADER diff --git a/code/sdk/zhihui/utilscpp/bt_scan_test.cpp b/code/sdk/zhihui/utilscpp/bt_scan_test.cpp new file mode 100644 index 000000000..ef9dfe8f6 --- /dev/null +++ b/code/sdk/zhihui/utilscpp/bt_scan_test.cpp @@ -0,0 +1,517 @@ +/** + * @file bt_scan_test.cpp + * @brief 蓝牙扫描测试程序 + * @description 扫描蓝牙设备并打印MAC地址和RSSI值 + */ + +#include "custom_utilscpp.h" +#include "debug_print.h" +#include "securec.h" +#include "media_thread_adapt.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 蓝牙GAP相关头文件 +#include "bts_br_gap.h" +#include "bts_def.h" + +// 蓝牙设备信息结构 +typedef struct { + uint8_t addr[BD_ADDR_LEN]; // MAC地址 + int rssi; // RSSI值 + char name[BD_NAME_LEN]; // 设备名称 + bool has_name; // 是否已获取名称 + bool has_rssi; // 是否已获取RSSI + bool is_new_device; // 是否是新发现的设备(用于控制打印) +} bt_device_info_t; + +// 蓝牙扫描测试上下文 +typedef struct { + bool is_running; // 扫描是否正在运行 + bt_device_info_t* devices; // 扫描到的设备列表 + uint32_t device_count; // 设备数量 + uint32_t device_capacity; // 设备列表容量 + MediaMutexHandle mutex; // 互斥锁 + + // 回调函数指针(保存原始回调,以便恢复) + gap_discovery_result_callback original_discovery_callback; + gap_read_remote_rssi_event_callback original_rssi_callback; + gap_discovery_state_changed_callback original_state_callback; + gap_remote_name_changed_callback original_name_callback; +} BtScanTestContext; + +static BtScanTestContext g_bt_scan_ctx = {0}; + +// 前向声明 +static void BtScanDiscoveryResultCallback(const bd_addr_t *bd_addr); +static void BtScanReadRemoteRssiCallback(const bd_addr_t *bd_addr, int rssi, int status); +static void BtScanDiscoveryStateChangedCallback(int status); +static void BtScanRemoteNameChangedCallback(const bd_addr_t *bd_addr, const unsigned char *deviceName, unsigned char length); +static int BtScanAddOrUpdateDevice(const bd_addr_t *bd_addr, int rssi, const char *name, bool has_name); +static void BtScanPrintDevices(void); + +/** + * @brief 添加或更新设备信息 + */ +static int BtScanAddOrUpdateDevice(const bd_addr_t *bd_addr, int rssi, const char *name, bool has_name) +{ + if (bd_addr == NULL) { + return -1; + } + + MediaMutexLock(g_bt_scan_ctx.mutex); + + // 查找是否已存在该设备 + int found_index = -1; + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + if (memcmp(g_bt_scan_ctx.devices[i].addr, bd_addr->addr, BD_ADDR_LEN) == 0) { + found_index = i; + break; + } + } + + if (found_index >= 0) { + // 更新现有设备 + bt_device_info_t *device = &g_bt_scan_ctx.devices[found_index]; + if (rssi != 0) { + device->rssi = rssi; + device->has_rssi = true; + } + if (has_name && name != NULL) { + strncpy_s(device->name, sizeof(device->name), name, sizeof(device->name) - 1); + device->has_name = true; + } + } else { + // 添加新设备 + if (g_bt_scan_ctx.device_count >= g_bt_scan_ctx.device_capacity) { + // 扩容 + uint32_t new_capacity = (g_bt_scan_ctx.device_capacity == 0) ? 32 : g_bt_scan_ctx.device_capacity * 2; + bt_device_info_t *new_devices = (bt_device_info_t*)realloc(g_bt_scan_ctx.devices, + new_capacity * sizeof(bt_device_info_t)); + if (new_devices == NULL) { + PRINT("[BtScan] Failed to reallocate device list\r\n"); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return -1; + } + g_bt_scan_ctx.devices = new_devices; + g_bt_scan_ctx.device_capacity = new_capacity; + } + + bt_device_info_t *device = &g_bt_scan_ctx.devices[g_bt_scan_ctx.device_count]; + memset_s(device, sizeof(bt_device_info_t), 0, sizeof(bt_device_info_t)); + memcpy_s(device->addr, BD_ADDR_LEN, bd_addr->addr, BD_ADDR_LEN); + device->rssi = rssi; + device->has_rssi = (rssi != 0); + if (has_name && name != NULL) { + strncpy_s(device->name, sizeof(device->name), name, sizeof(device->name) - 1); + device->has_name = true; + } else { + device->name[0] = '\0'; + device->has_name = false; + } + g_bt_scan_ctx.device_count++; + } + + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return 0; +} + +/** + * @brief 打印扫描到的设备列表 + */ +static void BtScanPrintDevices(void) +{ + MediaMutexLock(g_bt_scan_ctx.mutex); + + if (g_bt_scan_ctx.device_count == 0) { + PRINT("[BtScan] No devices found\r\n"); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return; + } + + PRINT("[BtScan] Found %u device(s):\r\n", g_bt_scan_ctx.device_count); + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + bt_device_info_t *device = &g_bt_scan_ctx.devices[i]; + PRINT("[BtScan] Device[%u]: MAC=%02X:%02X:%02X:%02X:%02X:%02X, RSSI=%d dBm", + i + 1, + device->addr[5], device->addr[4], device->addr[3], + device->addr[2], device->addr[1], device->addr[0], + device->has_rssi ? device->rssi : 0); + + if (device->has_name && strlen(device->name) > 0) { + PRINT(", Name=%s", device->name); + } + PRINT("\r\n"); + } + + MediaMutexUnLock(g_bt_scan_ctx.mutex); +} + +/** + * @brief 蓝牙设备发现回调 + */ +static void BtScanDiscoveryResultCallback(const bd_addr_t *bd_addr) +{ + if (bd_addr == NULL || !g_bt_scan_ctx.is_running) { + return; + } + + // 检查是否是新设备,以及是否已有名称 + MediaMutexLock(g_bt_scan_ctx.mutex); + bool is_new = true; + bool has_name_already = false; + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + if (memcmp(g_bt_scan_ctx.devices[i].addr, bd_addr->addr, BD_ADDR_LEN) == 0) { + is_new = false; + has_name_already = g_bt_scan_ctx.devices[i].has_name; + break; + } + } + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + if (is_new) { + PRINT("[BtScan] New device discovered: %02X:%02X:%02X:%02X:%02X:%02X\r\n", + bd_addr->addr[5], bd_addr->addr[4], bd_addr->addr[3], + bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0]); + } + + // 添加设备(暂时没有名称和RSSI) + BtScanAddOrUpdateDevice(bd_addr, 0, NULL, false); + + // 尝试获取设备名称(只在设备没有名称时才获取和打印) + if (!has_name_already) { + unsigned char nameLen = BD_NAME_LEN; + unsigned char deviceName[BD_NAME_LEN] = {0}; + bool getNameRet = gap_get_device_name(bd_addr, deviceName, &nameLen); + if (getNameRet && nameLen > 0) { + PRINT("[BtScan] Device name: %s\r\n", deviceName); + BtScanAddOrUpdateDevice(bd_addr, 0, (const char*)deviceName, true); + } + } + + // 请求RSSI值(只在设备还没有RSSI时请求) + MediaMutexLock(g_bt_scan_ctx.mutex); + bool need_rssi = true; + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + if (memcmp(g_bt_scan_ctx.devices[i].addr, bd_addr->addr, BD_ADDR_LEN) == 0) { + if (g_bt_scan_ctx.devices[i].has_rssi) { + need_rssi = false; + } + break; + } + } + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + if (need_rssi) { + gap_read_remote_rssi_value(bd_addr); + } + + // 调用原始回调(如果存在) + if (g_bt_scan_ctx.original_discovery_callback != NULL) { + g_bt_scan_ctx.original_discovery_callback(bd_addr); + } +} + +/** + * @brief 蓝牙RSSI读取回调 + */ +static void BtScanReadRemoteRssiCallback(const bd_addr_t *bd_addr, int rssi, int status) +{ + if (bd_addr == NULL || !g_bt_scan_ctx.is_running) { + return; + } + + if (status == 0) { + // 检查RSSI是否有显著变化(只在新设备或RSSI变化超过5dBm时打印) + MediaMutexLock(g_bt_scan_ctx.mutex); + bool should_print = false; + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + if (memcmp(g_bt_scan_ctx.devices[i].addr, bd_addr->addr, BD_ADDR_LEN) == 0) { + if (!g_bt_scan_ctx.devices[i].has_rssi || + abs(g_bt_scan_ctx.devices[i].rssi - rssi) > 5) { + should_print = true; + } + break; + } + } + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + if (should_print) { + printf("fac:[BtScan] Device: %02X:%02X:%02X:%02X:%02X:%02X, RSSI=%d dBm\r\n", + bd_addr->addr[5], bd_addr->addr[4], bd_addr->addr[3], + bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0], + rssi); + } + + // 更新设备RSSI + BtScanAddOrUpdateDevice(bd_addr, rssi, NULL, false); + } + // 不打印RSSI读取失败的信息,避免日志过多 + + // 调用原始回调(如果存在) + if (g_bt_scan_ctx.original_rssi_callback != NULL) { + g_bt_scan_ctx.original_rssi_callback(bd_addr, rssi, status); + } +} + +/** + * @brief 蓝牙扫描状态变化回调 + */ +static void BtScanDiscoveryStateChangedCallback(int status) +{ + PRINT("[BtScan] Discovery state changed: %d\r\n", status); + + if (g_bt_scan_ctx.is_running) { + // 只在扫描完成或状态变化时打印设备列表 + if (status == 0 || status == 1) { // 扫描完成或开始 + BtScanPrintDevices(); + } + + // 如果扫描结束,自动重新启动扫描(持续扫描) + if (status == 0) { // 扫描完成 + PRINT("[BtScan] Scan completed, restarting discovery...\r\n"); + usleep(500000); // 等待500ms后重新启动 + gap_br_start_discovery(); + } + } + + // 调用原始回调(如果存在) + if (g_bt_scan_ctx.original_state_callback != NULL) { + g_bt_scan_ctx.original_state_callback(status); + } +} + +/** + * @brief 蓝牙设备名称变化回调 + */ +static void BtScanRemoteNameChangedCallback(const bd_addr_t *bd_addr, const unsigned char *deviceName, unsigned char length) +{ + if (bd_addr == NULL || !g_bt_scan_ctx.is_running) { + return; + } + + if (deviceName != NULL && length > 0) { + char name[BD_NAME_LEN] = {0}; + strncpy_s(name, sizeof(name), (const char*)deviceName, + (length < sizeof(name)) ? length : sizeof(name) - 1); + + // 检查设备是否已有名称,避免重复打印 + MediaMutexLock(g_bt_scan_ctx.mutex); + bool has_name = false; + bool name_changed = false; + for (uint32_t i = 0; i < g_bt_scan_ctx.device_count; i++) { + if (memcmp(g_bt_scan_ctx.devices[i].addr, bd_addr->addr, BD_ADDR_LEN) == 0) { + has_name = g_bt_scan_ctx.devices[i].has_name; + if (has_name && strcmp(g_bt_scan_ctx.devices[i].name, name) != 0) { + name_changed = true; + } + break; + } + } + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + // 只在设备没有名称或名称发生变化时打印 + if (!has_name) { + PRINT("[BtScan] Device name: %02X:%02X:%02X:%02X:%02X:%02X, Name=%s\r\n", + bd_addr->addr[5], bd_addr->addr[4], bd_addr->addr[3], + bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0], + name); + } else if (name_changed) { + PRINT("[BtScan] Device name changed: %02X:%02X:%02X:%02X:%02X:%02X, New Name=%s\r\n", + bd_addr->addr[5], bd_addr->addr[4], bd_addr->addr[3], + bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0], + name); + } + + // 更新设备名称 + BtScanAddOrUpdateDevice(bd_addr, 0, name, true); + } + + // 调用原始回调(如果存在) + if (g_bt_scan_ctx.original_name_callback != NULL) { + g_bt_scan_ctx.original_name_callback(bd_addr, deviceName, length); + } +} + +/** + * @brief 启动蓝牙扫描测试 + * @return 0 成功, -1 失败 + */ +int custom_bt_scan_test_start(void) +{ + PRINT("[BtScan] Starting Bluetooth scan test...\r\n"); + + // 先启用蓝牙栈(使用 enable_bt_stack,对应 AT^BTENABLE 指令的功能) + // extern errcode_t enable_bt_stack(void); + + PRINT("[BtScan] Enabling Bluetooth stack (same as AT^BTENABLE)...\r\n"); + // errcode_t bt_enable_ret = enable_bt_stack(); + // if (bt_enable_ret != ERRCODE_SUCC && bt_enable_ret != 0) { + // PRINT("[BtScan] Warning: enable_bt_stack returned %d (0x%X)\r\n", + // bt_enable_ret, (unsigned int)bt_enable_ret); + // PRINT("[BtScan] Note: You may need to run AT^BTENABLE first\r\n"); + // } else { + // PRINT("[BtScan] Bluetooth stack enabled successfully\r\n"); + // } + + // 等待蓝牙栈初始化完成 + // usleep(2000000); // 2秒,确保蓝牙栈完全初始化 + PRINT("[BtScan] Waiting for Bluetooth stack ready...\r\n"); + + MediaMutexLock(g_bt_scan_ctx.mutex); + + if (g_bt_scan_ctx.is_running) { + PRINT("[BtScan] Scan already running\r\n"); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return -1; + } + + // 初始化互斥锁 + if (g_bt_scan_ctx.mutex == NULL) { + g_bt_scan_ctx.mutex = MediaMutexCreate(NULL); + if (g_bt_scan_ctx.mutex == NULL) { + PRINT("[BtScan] Failed to create mutex\r\n"); + return -1; + } + } + + // 清空设备列表 + if (g_bt_scan_ctx.devices != NULL) { + free(g_bt_scan_ctx.devices); + g_bt_scan_ctx.devices = NULL; + } + g_bt_scan_ctx.device_count = 0; + g_bt_scan_ctx.device_capacity = 0; + + // 注册回调函数 + gap_call_backs_t callbacks = {0}; + + // 保存原始回调(如果已注册) + // 注意:这里假设回调已经通过其他方式注册,我们需要获取并保存它们 + // 由于无法直接获取已注册的回调,我们直接设置我们的回调 + callbacks.discovery_result_callback = BtScanDiscoveryResultCallback; + callbacks.read_remote_rssi_event_callback = BtScanReadRemoteRssiCallback; + callbacks.discovery_state_changed_callback = BtScanDiscoveryStateChangedCallback; + callbacks.remote_name_changed_callback = BtScanRemoteNameChangedCallback; + + // 注册回调函数(返回值0表示成功) + int ret = gap_register_callbacks(&callbacks); + if (ret != 0) { + PRINT("[BtScan] Failed to register callbacks: %d\r\n", ret); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return -1; + } + + // 检查是否已经在扫描,如果是则先停止 + bool is_discovering = is_bt_discovering(BT_TRANSPORT_BR_EDR); + if (is_discovering) { + PRINT("[BtScan] Bluetooth is already discovering, canceling first...\r\n"); + gap_br_cancel_discovery(); + // 等待一小段时间让扫描完全停止 + usleep(200000); // 200ms + } + + // 设置扫描参数(扫描所有设备类型,最多20个设备) + // 参考 custom_factory_bt_scan_start 的实现 + errcode_t err = gap_br_set_inquiry_paramters(0, 20); + if (err != ERRCODE_SUCC) { + PRINT("[BtScan] Warning: Failed to set inquiry parameters: %d (0x%X)\r\n", + err, (unsigned int)err); + // 继续尝试启动扫描(参考其他代码实现,即使设置参数失败也继续) + } else { + PRINT("[BtScan] Inquiry parameters set successfully\r\n"); + } + + // 启动扫描(参考 custom_factory_bt_scan_start 的实现) + bool scan_ret = gap_br_start_discovery(); + if (!scan_ret) { + PRINT("[BtScan] Failed to start discovery\r\n"); + PRINT("[BtScan] Note: This may indicate Bluetooth stack is not fully ready\r\n"); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return -1; + } + + PRINT("[BtScan] Discovery started successfully\r\n"); + + g_bt_scan_ctx.is_running = true; + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + PRINT("[BtScan] Bluetooth scan started successfully\r\n"); + PRINT("[BtScan] Scanning for devices...\r\n"); + + return 0; +} + +/** + * @brief 停止蓝牙扫描测试 + * @return 0 成功, -1 失败 + */ +int custom_bt_scan_test_stop(void) +{ + PRINT("[BtScan] Stopping Bluetooth scan test...\r\n"); + + MediaMutexLock(g_bt_scan_ctx.mutex); + + if (!g_bt_scan_ctx.is_running) { + PRINT("[BtScan] Scan not running\r\n"); + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return 0; + } + + // 停止扫描 + bool ret = gap_br_cancel_discovery(); + if (!ret) { + PRINT("[BtScan] Failed to cancel discovery\r\n"); + } + + // 打印最终结果 + PRINT("[BtScan] Final scan results:\r\n"); + BtScanPrintDevices(); + + // 清理设备列表 + if (g_bt_scan_ctx.devices != NULL) { + free(g_bt_scan_ctx.devices); + g_bt_scan_ctx.devices = NULL; + } + g_bt_scan_ctx.device_count = 0; + g_bt_scan_ctx.device_capacity = 0; + + g_bt_scan_ctx.is_running = false; + MediaMutexUnLock(g_bt_scan_ctx.mutex); + + PRINT("[BtScan] Bluetooth scan stopped\r\n"); + + return 0; +} + +/** + * @brief 获取扫描测试状态 + * @return true 正在运行, false 未运行 + */ +bool custom_bt_scan_test_is_running(void) +{ + MediaMutexLock(g_bt_scan_ctx.mutex); + bool is_running = g_bt_scan_ctx.is_running; + MediaMutexUnLock(g_bt_scan_ctx.mutex); + return is_running; +} + +/** + * @brief 初始化蓝牙扫描测试模块 + */ +void custom_bt_scan_test_init(void) +{ + if (g_bt_scan_ctx.mutex == NULL) { + g_bt_scan_ctx.mutex = MediaMutexCreate(NULL); + } +} + +#ifdef __cplusplus +} +#endif + -- 2.17.1