From 754a529211dc729f4c1a44ee50809655a9cd8438 Mon Sep 17 00:00:00 2001 From: lmx Date: Wed, 3 Dec 2025 10:05:53 +0800 Subject: [PATCH] cun --- apps/earphone/include/app_config.h | 10 +- .../earphone/log_config/lib_btctrler_config.c | 1 + .../ble_handler}/ble_test.c | 0 .../ble_handler}/client_handler.c | 2963 +++++++++-------- .../ble_handler}/example/example.c | 0 .../ble_handler}/xtell.h | 2 + .../ble_handler}/xtell_app_main.c | 0 .../ble_handler}/xtell_handler.c | 2 +- cpu/br28/tools/app.bin | Bin 379672 -> 379304 bytes cpu/br28/tools/data_code.bin | Bin 39392 -> 39328 bytes cpu/br28/tools/download/earphone/jl_isd.bin | Bin 446464 -> 446464 bytes cpu/br28/tools/sdk.elf.resolution.txt | 29 +- 12 files changed, 1534 insertions(+), 1473 deletions(-) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/ble_test.c (100%) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/client_handler.c (58%) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/example/example.c (100%) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/xtell.h (95%) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/xtell_app_main.c (100%) rename apps/earphone/{xtell_Sensor => remote_control/ble_handler}/xtell_handler.c (99%) diff --git a/apps/earphone/include/app_config.h b/apps/earphone/include/app_config.h index 32d21ff..0c6517f 100644 --- a/apps/earphone/include/app_config.h +++ b/apps/earphone/include/app_config.h @@ -14,21 +14,25 @@ //#define CONFIG_DEBUG_LITE_ENABLE //轻量级打印开关, 默认关闭 #endif - +#define BLE_WIRELESS_CLIENT_EN 1 //开启则作为主设备,lmx //*********************************************************************************// // AI配置 // //*********************************************************************************// #define CONFIG_APP_BT_ENABLE -#define BLE_WIRELESS_CLIENT_EN 1 //作为主设备,lmx + #ifdef CONFIG_APP_BT_ENABLE #define TRANS_DATA_EN 0 #define RCSP_BTMATE_EN 0 +#if BLE_WIRELESS_CLIENT_EN == 0 +#define RCSP_ADV_EN 1 // lmx,打开这个,则作为ble从设备 +#else #define RCSP_ADV_EN 0 +#endif #define AI_APP_PROTOCOL 0 #define LL_SYNC_EN 0 #define TUYA_DEMO_EN 0 #else -#define TRANS_DATA_EN 1 +#define TRANS_DATA_EN 0 #define RCSP_BTMATE_EN 0 #define RCSP_ADV_EN 0 #define AI_APP_PROTOCOL 0 diff --git a/apps/earphone/log_config/lib_btctrler_config.c b/apps/earphone/log_config/lib_btctrler_config.c index d7f0ef2..2d8b681 100644 --- a/apps/earphone/log_config/lib_btctrler_config.c +++ b/apps/earphone/log_config/lib_btctrler_config.c @@ -231,6 +231,7 @@ const int config_btctler_le_roles = (LE_ADV | LE_SLAVE); const uint64_t config_btctler_le_features = LE_ENCRYPTION; #else + const int config_btctler_le_roles = (LE_ADV | LE_SLAVE); const uint64_t config_btctler_le_features = 0; #endif diff --git a/apps/earphone/xtell_Sensor/ble_test.c b/apps/earphone/remote_control/ble_handler/ble_test.c similarity index 100% rename from apps/earphone/xtell_Sensor/ble_test.c rename to apps/earphone/remote_control/ble_handler/ble_test.c diff --git a/apps/earphone/xtell_Sensor/client_handler.c b/apps/earphone/remote_control/ble_handler/client_handler.c similarity index 58% rename from apps/earphone/xtell_Sensor/client_handler.c rename to apps/earphone/remote_control/ble_handler/client_handler.c index c29a23c..879a2ca 100644 --- a/apps/earphone/xtell_Sensor/client_handler.c +++ b/apps/earphone/remote_control/ble_handler/client_handler.c @@ -1,3 +1,20 @@ +/** + ************************************************************************************************** + * 设备扫描与发现 + * 创建和管理连接 + * 服务和特征的发现 + * 数据读写操作 + * 连接状态管理和事件处理 + * 配对与绑定 + ************************************************************************************************** + */ + + +/* + ************************************************************************************************** + * 头文件包含 + ************************************************************************************************** + */ #include "system/app_core.h" #include "system/includes.h" @@ -15,209 +32,1561 @@ #include "le_common.h" #include "ble_user.h" -#if (TCFG_BLE_DEMO_SELECT == DEF_BLE_DEMO_WIRELESS_MIC_CLIENT) +#if (BLE_WIRELESS_CLIENT_EN == 1) // 作为主设备主动去连接其他蓝牙 -#define SUPPORT_TEST_BOX_BLE_MASTER_TEST_EN 0 -#define SHOW_RX_DATA_RATE 1 -#define SHOW_TX_DATA_RATE 1 +/* + ************************************************************************************************** + * 宏定义 + ************************************************************************************************** + */ +// 功能开关宏 +#define SUPPORT_TEST_BOX_BLE_MASTER_TEST_EN 0 // 是否支持测试盒BLE主设备测试 +#define SHOW_RX_DATA_RATE 1 // 是否显示接收数据速率 +#define SHOW_TX_DATA_RATE 1 // 是否显示发送数据速率 + +// 日志打印宏 #if LE_DEBUG_PRINT_EN -/* #define log_info printf */ -#define log_info(x, ...) printf("[LE_CLIENT]" x " ", ## __VA_ARGS__) -#define log_info_hexdump put_buf +#define log_info(x, ...) printf("[LE_CLIENT]" x " ", ## __VA_ARGS__) // 带前缀的信息打印 +#define log_info_hexdump put_buf // 16进制数据打印 #else -#define log_info(...) -#define log_info_hexdump(...) +#define log_info(...) // 禁用信息打印 +#define log_info_hexdump(...) // 禁用16进制数据打印 #endif +// ATT 协议相关宏 +#define ATT_LOCAL_MTU_SIZE (517) // 本地ATT最大传输单元(MTU)大小,需>=20 +#define ATT_SEND_CBUF_SIZE (200) // ATT发送数据循环缓冲区大小,需>=20 +#define ATT_RAM_BUFSIZE (ATT_CTRL_BLOCK_SIZE + ATT_LOCAL_MTU_SIZE + ATT_SEND_CBUF_SIZE) // 总ATT RAM缓冲区大小 -//------ -#define ATT_LOCAL_MTU_SIZE (517) //note: need >= 20 -#define ATT_SEND_CBUF_SIZE (200)//(256)//(512*10) //note: need >= 20,缓存大小,可修改 -#define ATT_RAM_BUFSIZE (ATT_CTRL_BLOCK_SIZE + ATT_LOCAL_MTU_SIZE + ATT_SEND_CBUF_SIZE) //note: -static u8 att_ram_buffer[ATT_RAM_BUFSIZE] __attribute__((aligned(4))); +// 搜索Profile缓冲区宏 +#define SEARCH_PROFILE_BUFSIZE (512) // 搜索Profile时使用的RAM缓冲区大小 +#define scan_buffer search_ram_buffer // 将scan_buffer定义为search_ram_buffer的别名 -#define SEARCH_PROFILE_BUFSIZE (512) //note: -static u8 search_ram_buffer[SEARCH_PROFILE_BUFSIZE] __attribute__((aligned(4))); -#define scan_buffer search_ram_buffer -//--------------- -//搜索类型 -#define SET_SCAN_TYPE SCAN_ACTIVE -//搜索 周期大小, 参数一:320 参数二:320 参数三:168 -#define SET_SCAN_INTERVAL 320//48//256//48 //(unit:0.625ms), 96没有干扰的情况下, 连接速度为60ms, 如需要降低功耗, 可以在+16的倍数 -//搜索 窗口大小 参数一:102 参数二:70 参数三:30 -#define SET_SCAN_WINDOW 70//48//24//48 //(unit:0.625ms) +// BLE扫描参数宏 (单位: 0.625ms) +#define SET_SCAN_TYPE SCAN_ACTIVE // 扫描类型:主动扫描 +#define SET_SCAN_INTERVAL 320 // 扫描间隔 (e.g., 320 * 0.625ms = 200ms) +#define SET_SCAN_WINDOW 70 // 扫描窗口 (e.g., 70 * 0.625ms = 43.75ms) -//连接周期 -#define SET_CONN_INTERVAL 3 //(unit:1.25ms) -//连接latency -#define SET_CONN_LATENCY 0 //(unit:conn_interval) -//连接超时 -#define SET_CONN_TIMEOUT 400 //(unit:10ms) +// BLE连接参数宏 +#define SET_CONN_INTERVAL 3 // 连接间隔 (单位: 1.25ms, e.g., 3 * 1.25ms = 3.75ms) +#define SET_CONN_LATENCY 0 // 连接延迟 (单位: conn_interval) +#define SET_CONN_TIMEOUT 400 // 连接超时 (单位: 10ms, e.g., 400 * 10ms = 4000ms) + +// 测试盒蓝牙名称宏 #if WIRELESS_TOOL_BLE_NAME_EN -#define TEST_BOX_BLE_NAME "W_MIC_01" -#define TEST_BOX_BLE_NAME_LEN (sizeof(TEST_BOX_BLE_NAME)-1) -static u8 *match_name; +#define TEST_BOX_BLE_NAME "W_MIC_01" // 测试盒默认蓝牙名称 +#define TEST_BOX_BLE_NAME_LEN (sizeof(TEST_BOX_BLE_NAME)-1) // 名称长度(不含结束符) #endif -//---------------------------------------------------------------------------- -static u8 scan_ctrl_en; -static u8 ble_work_state = 0; -static void (*app_recieve_callback)(void *priv, void *buf, u16 len) = NULL; -static void (*app_ble_state_callback)(void *priv, ble_state_e state) = NULL; -static void (*ble_resume_send_wakeup)(void) = NULL; -static u32 channel_priv; -static hci_con_handle_t con_handle; +// VM(非易失性存储)存储配对信息标签 +#define BLE_VM_HEAD_TAG (0xB95C) // VM数据头标签 +#define BLE_VM_TAIL_TAG (0x5CB9) // VM数据尾标签 -#define BLE_VM_HEAD_TAG (0xB95C) -#define BLE_VM_TAIL_TAG (0x5CB9) +// Passkey输入功能开关 +#define PASSKEY_ENTER_ENABLE 0 // 是否启用输入Passkey进行配对 + +/* + ************************************************************************************************** + * 类型定义 + ************************************************************************************************** + */ + +/** + * @brief 配对信息结构体,用于存储到VM + */ struct pair_info_t { - u16 head_tag; - u8 pair_flag; - u8 peer_address_info[7]; - u16 tail_tag; + u16 head_tag; // 数据头标签,用于校验 + u8 pair_flag; // 配对标志,1表示已配对 + u8 peer_address_info[7]; // 对端设备地址信息 (1字节类型 + 6字节地址) + u16 tail_tag; // 数据尾标签,用于校验 }; -static struct pair_info_t conn_pair_info; -static u8 pair_bond_enable = 0; -//------------------------------------------------------------------------------- +/** + * @brief 目标设备特征句柄集合 + */ typedef struct { - uint16_t read_handle; - uint16_t read_long_handle; - uint16_t write_handle; - uint16_t write_no_respond; - uint16_t notify_handle; - uint16_t indicate_handle; + uint16_t read_handle; // 读特征句柄 + uint16_t read_long_handle; // 长读特征句柄 + uint16_t write_handle; // 写特征句柄 + uint16_t write_no_respond; // 无响应写特征句柄 + uint16_t notify_handle; // 通知特征句柄 + uint16_t indicate_handle; // 指示特征句柄 } target_hdl_t; -//记录handle 使用 -static target_hdl_t target_handle; -static opt_handle_t opt_handle_table[OPT_HANDLE_MAX]; -static u8 opt_handle_used_cnt; -static u8 force_seach_onoff = 0; -static s8 force_seach_rssi = -127; -static u8 cur_peer_address_info[7]; -static u8 flag_specific_adv_name = 0; -static u8 flag_need_judge_rssi = 0; -static u8 flag_specific_sacn = 0; -static void bt_ble_create_connection(u8 *conn_addr, u8 addr_type); -static int bt_ble_scan_enable(void *priv, u32 en); -static int client_write_send(void *priv, u8 *data, u16 len); -static int client_operation_send(u16 handle, u8 *data, u16 len, u8 att_op_type); -#if WIRELESS_TOOL_BLE_NAME_EN -static ble_state_e get_ble_work_state(void); -#else +/* + ************************************************************************************************** + * 静态函数声明 + ************************************************************************************************** + */ +// 状态管理 static void set_ble_work_state(ble_state_e state); -#endif +static ble_state_e get_ble_work_state(void); + +// 扫描与连接 +static int bt_ble_scan_enable(void *priv, u32 en); +static void scanning_setup_init(void); +static void client_report_adv_data(adv_report_t *report_pt, u16 len); +static bool resolve_adv_report(u8 *adv_address, u8 data_length, u8 *data, s8 rssi); +static bool check_device_is_match(u8 info_type, u8 *data, int size); +static void client_create_connection(u8 *conn_addr, u8 addr_type); +static void bt_ble_create_connection(u8 *conn_addr, u8 addr_type); static void client_create_connection_cannel(void); -//--------------------------------------------------------------------------- -#if 1//default -//指定搜索uuid -//指定搜索uuid +static int client_create_connect_api(u8 *addr, u8 addr_type, u8 mode); +static int client_create_cannel_api(void); +static int ble_disconnect(void *priv); + +// Profile 发现与操作 +static void client_search_profile_start(void); +static void user_client_report_search_result(search_result_t *result_info); +static void check_target_uuid_match(search_result_t *result_info); +static void do_operate_search_handle(void); +static target_uuid_t *get_match_handle_target(u16 handle); + +// 数据收发 +static int client_operation_send(u16 handle, u8 *data, u16 len, u8 att_op_type); +static int client_write_send(void *priv, u8 *data, u16 len); +static int client_write_without_respond_send(void *priv, u8 *data, u16 len); +static int client_read_value_send(void *priv); +static int client_read_long_value_send(void *priv); +static int get_buffer_vaild_len(void *priv); +static void can_send_now_wakeup(void); + +// 事件与回调处理 +static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void cbk_sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void user_client_report_data_callback(att_data_report_t *report_data); +static void client_event_report(le_client_event_e event, u8 *packet, int size); +static void connection_update_complete_success(u8 *packet); +static int l2cap_connection_update_request_just(u8 *packet, hci_con_handle_t handle); + +// 配对与绑定 +static void conn_pair_vm_do(struct pair_info_t *info, u8 rw_flag); +static void device_bonding_init(void); +static void reset_passkey_cb(u32 *key); + +// 初始化与配置 +static int client_init_config(void *priv, const client_conn_cfg_t *cfg); +static int client_regiest_wakeup_send(void *priv, void *cbk); +static int client_regiest_recieve_cbk(void *priv, void *cbk); +static int client_regiest_state_cbk(void *priv, void *cbk); + +// 其他辅助函数 +static int client_force_search(u8 onoff, s8 rssi); +static void set_connection_data_length(u16 tx_octets, u16 tx_time); +static void set_connection_data_phy(u8 tx_phy, u8 rx_phy); +static u8 client_idle_query(void); + +// 默认示例相关 +#if 1 //default +static void default_report_data_deal(att_data_report_t *report_data, target_uuid_t *search_uuid); +static void default_test_write(void); +static void default_event_callback(le_client_event_e event, u8 *packet, int size); +#endif + +// 数据速率统计 +#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) +static void client_timer_handler(void); +static void client_timer_start(void); +#endif + +/* + ************************************************************************************************** + * 全局/静态变量定义 + ************************************************************************************************** + */ + +// --- 缓冲区 --- +static u8 att_ram_buffer[ATT_RAM_BUFSIZE] __attribute__((aligned(4))); // ATT协议栈RAM缓冲区 +static u8 search_ram_buffer[SEARCH_PROFILE_BUFSIZE] __attribute__((aligned(4))); // Profile搜索RAM缓冲区 + +// --- 状态变量 --- +static u8 scan_ctrl_en; // 扫描总开关,由上层应用控制 +static u8 ble_work_state = 0; // BLE工作状态机 +static hci_con_handle_t con_handle; // 当前连接句柄 +static u8 force_seach_onoff = 0; // 强制搜索开关(根据RSSI过滤) +static s8 force_seach_rssi = -127; // 强制搜索的RSSI阈值 +static u8 cur_peer_address_info[7]; // 当前连接的对端设备地址信息 +static u8 flag_specific_adv_name = 0; // 产测用:是否发现特定广播名称的标志 +static u8 flag_need_judge_rssi = 0; // 产测用:是否需要判断RSSI的标志 +static u8 flag_specific_sacn = 0; // 产测用:是否为特定扫描的标志 + +// --- 回调函数指针 --- +static void (*app_recieve_callback)(void *priv, void *buf, u16 len) = NULL; // 应用层数据接收回调 +static void (*app_ble_state_callback)(void *priv, ble_state_e state) = NULL; // 应用层状态变化回调 +static void (*ble_resume_send_wakeup)(void) = NULL; // 数据发送恢复唤醒回调 +static u32 channel_priv; // 回调函数私有参数 + +// --- 配对与绑定信息 --- +static struct pair_info_t conn_pair_info; // 连接配对信息 +static u8 pair_bond_enable = 0; // 本次连接是否需要绑定 + +// --- Profile句柄管理 --- +static target_hdl_t target_handle; // 存储发现的目标特征句柄 +static opt_handle_t opt_handle_table[OPT_HANDLE_MAX]; // 存储所有匹配到的特征操作句柄 +static u8 opt_handle_used_cnt; // 已使用的操作句柄数量 + +// --- 默认示例配置 --- +#if 1 //default +// 默认搜索的UUID表 static const target_uuid_t default_search_uuid_table[] = { - - // for uuid16 - // PRIMARY_SERVICE, ae30 - // CHARACTERISTIC, ae01, WRITE_WITHOUT_RESPONSE | DYNAMIC, - // CHARACTERISTIC, ae02, NOTIFY, - + // 服务UUID: 0xae30, 特征UUID: 0xae01, 操作类型: 无响应写 { .services_uuid16 = 0xae30, .characteristic_uuid16 = 0xae01, .opt_type = ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, }, - + // 服务UUID: 0xae30, 特征UUID: 0xae02, 操作类型: 通知 { .services_uuid16 = 0xae30, .characteristic_uuid16 = 0xae02, .opt_type = ATT_PROPERTY_NOTIFY, }, - - //for uuid128,sample - // PRIMARY_SERVICE, 0000F530-1212-EFDE-1523-785FEABCD123 - // CHARACTERISTIC, 0000F531-1212-EFDE-1523-785FEABCD123, NOTIFY, - // CHARACTERISTIC, 0000F532-1212-EFDE-1523-785FEABCD123, WRITE_WITHOUT_RESPONSE | DYNAMIC, - /* - { - .services_uuid16 = 0, - .services_uuid128 = {0x00,0x00,0xF5,0x30 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23} , - .characteristic_uuid16 = 0, - .characteristic_uuid128 = {0x00,0x00,0xF5,0x31 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23}, - .opt_type = ATT_PROPERTY_NOTIFY, - }, - - { - .services_uuid16 = 0, - .services_uuid128 = {0x00,0x00,0xF5,0x30 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23} , - .characteristic_uuid16 = 0, - .characteristic_uuid128 = {0x00,0x00,0xF5,0x32 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23}, - .opt_type = ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, - }, - */ - }; +// 默认匹配的设备名称 +static const u8 test_remoter_name1[] = "CM-22222"; +// 默认匹配规则 +static const client_match_cfg_t match_dev01 = { + .create_conn_mode = BIT(CLI_CREAT_BY_NAME), // 通过名称匹配 + .compare_data_len = sizeof(test_remoter_name1) - 1, // 名称长度 + .compare_data = test_remoter_name1, // 名称数据 + .bonding_flag = 0, // 不进行绑定 +}; -static void default_report_data_deal(att_data_report_t *report_data, target_uuid_t *search_uuid) +// 默认客户端连接配置 +static const client_conn_cfg_t client_conn_config_default = { + .match_dev_cfg[0] = &match_dev01, + .match_dev_cfg[1] = NULL, + .match_dev_cfg[2] = NULL, + .report_data_callback = default_report_data_deal, + .search_uuid_cnt = (sizeof(default_search_uuid_table) / sizeof(target_uuid_t)), + .search_uuid_table = default_search_uuid_table, + .security_en = 0, //不加密 + .event_callback = default_event_callback, +}; + +static u16 default_client_write_handle; // 默认示例的写句柄 +static u16 test_client_timer = 0; // 默认示例的定时器 +#endif + +// --- 全局配置指针 --- +static client_conn_cfg_t *client_config = (void *) &client_conn_config_default; // 指向当前客户端连接配置 + +// --- 连接更新参数 --- +static const struct conn_update_param_t connection_param_table[] = { + {16, 24, 0, 600}, + {12, 28, 0, 600}, + {8, 20, 0, 600}, + {50, 60, 0, 600}, +}; +static u8 send_param_index = 3; // 当前使用的连接参数索引 + +// --- 数据速率统计 --- +#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) +static u32 client_timer_handle = 0; // 速率统计定时器 +static u32 test_rx_data_count; // 接收字节计数 +static u32 test_tx_data_count; // 发送字节计数 +#endif + +// --- 其他 --- +#if WIRELESS_TOOL_BLE_NAME_EN +static u8 *match_name; // 动态配置的匹配名称 +#endif + +static const char *const phy_result[] = { // PHY速率类型字符串 + "None", "1M", "2M", "Coded", +}; + +// --- 接口操作结构体 --- +static const struct ble_client_operation_t client_operation = { + .scan_enable = bt_ble_scan_enable, + .disconnect = ble_disconnect, + .get_buffer_vaild = get_buffer_vaild_len, + .write_data = (void *)client_write_without_respond_send, + .read_do = (void *)client_read_value_send, + .regist_wakeup_send = client_regiest_wakeup_send, + .regist_recieve_cbk = client_regiest_recieve_cbk, + .regist_state_cbk = client_regiest_state_cbk, + .init_config = client_init_config, + .opt_comm_send = client_operation_send, + .set_force_search = client_force_search, + .create_connect = client_create_connect_api, + .create_connect_cannel = client_create_cannel_api, + .get_work_state = get_ble_work_state, +}; + +/* + ************************************************************************************************** + * 外部调用函数实现 + ************************************************************************************************** + */ + +/** + * @brief 获取客户端操作接口表 + * @return 指向ble_client_operation_t结构体的指针 + */ +struct ble_client_operation_t *ble_get_client_operation_table(void) { - log_info("report_data:%02x,%02x,%d,len(%d)", report_data->packet_type, - report_data->value_handle, report_data->value_offset, report_data->blob_length); + return (struct ble_client_operation_t *)&client_operation; +} - log_info_hexdump(report_data->blob, report_data->blob_length); +/** + * @brief BLE协议栈初始化函数,由协议栈内部调用 + */ +void ble_profile_init(void) +{ + log_info("ble profile init\n"); + le_device_db_init(); + ble_stack_gatt_role(1); // 设置为客户端角色 + /* setup ATT client */ + gatt_client_init(); + gatt_client_register_packet_handler(cbk_packet_handler); + + // 注册HCI事件回调 + hci_event_callback_set(&cbk_packet_handler); + le_l2cap_register_packet_handler(&cbk_packet_handler); + + ble_vendor_set_default_att_mtu(ATT_LOCAL_MTU_SIZE); +} + +/** + * @brief BLE客户端总初始化 + */ +void bt_ble_init(void) +{ + log_info("***** ble_init******\n"); + +#if WIRELESS_TOOL_BLE_NAME_EN + u8 *config_name; + u8 config_name_len; +#if 1 // 使用配置工具的蓝牙名 + extern const char *bt_get_local_name(); + config_name = (u8 *)(bt_get_local_name()); + config_name_len = strlen((const char *)config_name) + 1; + log_info("use config name, len=%d", config_name_len); + log_info_hexdump(config_name, config_name_len); +#else + config_name = (u8 *)TEST_BOX_BLE_NAME; + config_name_len = sizeof(TEST_BOX_BLE_NAME); +#endif + match_name = (u8 *)zalloc(config_name_len); + memcpy(match_name, config_name, config_name_len); + log_info("match_name: %s", match_name); +#endif + + set_ble_work_state(BLE_ST_INIT_OK); + conn_pair_vm_do(&conn_pair_info, 0); // 读取VM中的配对信息 + +#if !WIRELESS_PAIR_BONDING + device_bonding_init(); +#endif + + ble_module_enable(1); // 使能BLE模块 + +#if TCFG_WIFI_DETECT_ENABLE + extern void wifi_detect_set_master_first(u8 first); +#if TCFG_WIFI_DETCET_PRIOR + wifi_detect_set_master_first(0); +#else + wifi_detect_set_master_first(1); +#endif +#endif + +#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) + client_timer_start(); // 启动数据速率统计定时器 +#endif +} + +/** + * @brief BLE客户端退出 + */ +void bt_ble_exit(void) +{ + log_info("***** ble_exit******\n"); + // 可添加退出清理代码 +} + +/** + * @brief 应用层调用的断开连接接口 + */ +void ble_app_disconnect(void) +{ + ble_disconnect(NULL); +} + +/** + * @brief BLE模块使能/禁止 (兼容旧接口) + * @param enable 1:使能, 0:禁止 + */ +void bt_ble_adv_enable(u8 enable) +{ + ble_module_enable(enable); +} + +/** + * @brief BLE模块使能/禁止 + * @param en 1:使能, 0:禁止 + */ +void ble_module_enable(u8 en) +{ + log_info("mode_en:%d\n", en); + if (en) { + scan_ctrl_en = 1; + bt_ble_scan_enable(0, 1); // 开始扫描 + } else { + scan_ctrl_en = 0; + if (con_handle) { + ble_disconnect(NULL); // 如果已连接,则断开 + } else { + bt_ble_scan_enable(0, 0); // 如果未连接,则停止扫描 + } + } +} + +/** + * @brief 客户端发起连接参数更新请求 + */ +void client_send_conn_param_update(void) +{ + struct conn_update_param_t *param = (void *)&connection_param_table[send_param_index]; + log_info("client update param:-%d-%d-%d-%d-\n", param->interval_min, param->interval_max, param->latency, param->timeout); + if (con_handle) { + ble_op_conn_param_update(con_handle, param); + } +} + +/** + * @brief 清除绑定信息 + */ +void clear_bonding_info(void) +{ + log_info("client_clear_bonding_info\n"); + conn_pair_vm_do(&conn_pair_info, 0); + if (conn_pair_info.pair_flag) { + // 清空配对信息并写回VM + memset(&conn_pair_info, 0, sizeof(struct pair_info_t)); + conn_pair_vm_do(&conn_pair_info, 1); + +#if WIRELESS_PAIR_BONDING + if (get_ble_work_state() == BLE_ST_CREATE_CONN) { + client_create_connection_cannel(); + bt_ble_scan_enable(0, 1); + } else if (con_handle) { + ble_disconnect(NULL); + } +#endif + } +} + +/** + * @brief 填充MAC地址到RCSP广播(示例函数) + * @param mac_addr_buf 存储MAC地址的缓冲区 + */ +void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf) +{ +#if (MUTIl_CHARGING_BOX_EN) + u8 *mac_addr = get_chargebox_adv_addr(); + if (mac_addr) { + swapX(mac_addr, mac_addr_buf, 6); + } +#else + swapX(bt_get_mac_addr(), mac_addr_buf, 6); +#endif +} + +/* + ************************************************************************************************** + * 内部静态函数实现 + ************************************************************************************************** + */ + + +static void client_event_report(le_client_event_e event, u8 *packet, int size) +{ + if (client_config->event_callback) { + client_config->event_callback(event, packet, size); + } +} + +/** + * @brief 设置BLE工作状态 + * @param state 新的状态 + */ +static void set_ble_work_state(ble_state_e state) +{ + if (state != ble_work_state) { + log_info("ble_client_work_st:%x->%x\n", ble_work_state, state); + ble_work_state = state; + if (app_ble_state_callback) { + app_ble_state_callback((void *)channel_priv, state); + } + } +} + +/** + * @brief 获取当前BLE工作状态 + * @return ble_state_e 当前状态 + */ +static ble_state_e get_ble_work_state(void) +{ + return ble_work_state; +} + +/** + * @brief 读写VM中的配对信息 + * @param info 指向配对信息结构体的指针 + * @param rw_flag 0:读, 1:写 + */ +static void conn_pair_vm_do(struct pair_info_t *info, u8 rw_flag) +{ + int ret; + int vm_len = sizeof(struct pair_info_t); + + log_info("-conn_pair_info vm_do:%d\n", rw_flag); + if (rw_flag == 0) { // 读操作 + ret = syscfg_read(CFG_BLE_MODE_INFO, (u8 *)info, vm_len); + if (!ret) { + log_info("-null--\n"); + } + // 校验数据有效性 + if ((BLE_VM_HEAD_TAG == info->head_tag) && (BLE_VM_TAIL_TAG == info->tail_tag)) { + log_info("-exist--\n"); + log_info_hexdump((u8 *)info, vm_len); + } else { + // 无效则初始化 + memset(info, 0, vm_len); + info->head_tag = BLE_VM_HEAD_TAG; + info->tail_tag = BLE_VM_TAIL_TAG; + } + } else { // 写操作 + syscfg_write(CFG_BLE_MODE_INFO, (u8 *)info, vm_len); + } +} + +/** + * @brief 检查广播数据中的设备是否与预设规则匹配 + * @param info_type 匹配类型 (名称/地址/Tag) + * @param data 待比较的数据 + * @param size 数据长度 + * @return true:匹配, false:不匹配 + */ +static bool check_device_is_match(u8 info_type, u8 *data, int size) +{ + int i; + u8 conn_mode = BIT(info_type); + client_match_cfg_t *cfg; + + for (i = 0; i < CLIENT_MATCH_CONN_MAX; i++) { + cfg = client_config->match_dev_cfg[i]; + if (cfg == NULL) { + continue; + } + // 检查匹配模式和数据长度是否一致 + if (cfg->create_conn_mode == conn_mode && size == cfg->compare_data_len) { + log_info("match check\n"); + // 比较数据内容 + if (0 == memcmp(data, cfg->compare_data, cfg->compare_data_len)) { + log_info("match ok\n"); + pair_bond_enable = cfg->bonding_flag; // 设置绑定标志 + client_event_report(CLI_EVENT_MATCH_DEV, (u8 *)cfg, sizeof(client_match_cfg_t)); + return true; + } + } + } + return false; +} + +/** + * @brief 解析广播数据包 + * @param adv_address 广播设备地址 + * @param data_length 广播数据长度 + * @param data 广播数据内容 + * @param rssi 信号强度 + * @return true:找到目标设备, false:未找到 + */ +static bool resolve_adv_report(u8 *adv_address, u8 data_length, u8 *data, s8 rssi) +{ + u8 i, lenght, ad_type; + u8 *adv_data_pt; + u8 find_remoter = 0; + u32 tmp32; + + // 1. 按地址匹配 + if (check_device_is_match(CLI_CREAT_BY_ADDRESS, adv_address, 6)) { + find_remoter = 1; + } + + // 2. 解析广播数据字段 + adv_data_pt = data; + for (i = 0; i < data_length;) { + if (*adv_data_pt == 0) { + break; + } + + lenght = *adv_data_pt++; + if (lenght >= data_length || (lenght + i) >= data_length) { + printf("!!!error_adv_packet:"); + put_buf(data, data_length); + break; + } + + ad_type = *adv_data_pt++; + i += (lenght + 1); + + switch (ad_type) { + case HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME: + case HCI_EIR_DATATYPE_SHORTENED_LOCAL_NAME: + tmp32 = adv_data_pt[lenght - 1]; + adv_data_pt[lenght - 1] = 0; + log_info("remoter_name: %s,rssi:%d\n", adv_data_pt, rssi); + log_info_hexdump(adv_address, 6); + adv_data_pt[lenght - 1] = tmp32; + +#if SUPPORT_TEST_BOX_BLE_MASTER_TEST_EN +#if !WIRELESS_TOOL_BLE_NAME_EN +#define TEST_BOX_BLE_NAME "W_MIC_01" +#define TEST_BOX_BLE_NAME_LEN sizeof(TEST_BOX_BLE_NAME) + if (0 == memcmp(adv_data_pt, TEST_BOX_BLE_NAME, TEST_BOX_BLE_NAME_LEN)) { + find_remoter = 1; + } +#else + if (lenght == strlen((const char *)match_name) + 1) { + if (0 == memcmp(adv_data_pt, match_name, lenght - 1)) { + find_remoter = 1; + } + } +#endif +#endif + if (check_device_is_match(CLI_CREAT_BY_NAME, adv_data_pt, lenght - 1)) { + find_remoter = 1; + log_info("catch name ok\n"); + } + break; + + case HCI_EIR_DATATYPE_MANUFACTURER_SPECIFIC_DATA: + if (check_device_is_match(CLI_CREAT_BY_TAG, adv_data_pt, lenght - 1)) { + log_info("get_tag_string!\n"); + find_remoter = 1; + } + break; + + // 其他类型可在此添加解析 + default: + break; + } + + if (find_remoter) { + break; // 找到后即可退出循环 + } + adv_data_pt += (lenght - 1); + } + + return find_remoter; +} + +/** + * @brief 报告并处理接收到的广播数据 + * @param report_pt 广播报告 + * @param len 报告长度 + */ +static void client_report_adv_data(adv_report_t *report_pt, u16 len) +{ + bool find_remoter; + +#if WIRELESS_PAIR_BONDING + // 绑定模式下,优先匹配已绑定的设备 + if (conn_pair_info.pair_flag) { + if (report_pt->event_type != 1) { // 只处理可连接的广播 + return; + } + if (report_pt->address_type == conn_pair_info.peer_address_info[0] + && 0 == memcmp(&conn_pair_info.peer_address_info[1], report_pt->address, 6)) { + log_info("match bond_dev\n"); + find_remoter = 1; + } else { + return; // 不是绑定的设备,直接返回 + } + } else { + find_remoter = resolve_adv_report(report_pt->address, report_pt->length, report_pt->data, report_pt->rssi); + } +#else + find_remoter = resolve_adv_report(report_pt->address, report_pt->length, report_pt->data, report_pt->rssi); +#endif + + if (find_remoter) { + // 强制搜索模式下,检查RSSI + if (force_seach_onoff && force_seach_rssi > report_pt->rssi) { + log_info("match but rssi fail!!!:%d,%d\n", force_seach_rssi, report_pt->rssi); + return; + } + + log_info("rssi:%d\n", report_pt->rssi); + log_info("\n*********create_connection***********\n"); + log_info("***remote type %d,addr:", report_pt->address_type); + log_info_hexdump(report_pt->address, 6); + + bt_ble_scan_enable(0, 0); // 停止扫描 + client_create_connect_api(report_pt->address, report_pt->address_type, 0); // 发起连接 + log_info("*create_finish\n"); + } +} + +/** + * @brief 使能/禁止BLE扫描 + * @param priv 私有参数 (未使用) + * @param en 1:使能, 0:禁止 + * @return 0:成功, 非0:失败 + */ +static int bt_ble_scan_enable(void *priv, u32 en) +{ + ble_state_e next_state, cur_state; + + if (!scan_ctrl_en && en) { + return APP_BLE_OPERATION_ERROR; // 如果总开关关闭,则不允许开启 + } + + next_state = en ? BLE_ST_SCAN : BLE_ST_IDLE; + cur_state = get_ble_work_state(); + + // 检查当前状态是否允许切换 + switch (cur_state) { + case BLE_ST_SCAN: + case BLE_ST_IDLE: + case BLE_ST_INIT_OK: + case BLE_ST_NULL: + case BLE_ST_DISCONN: + case BLE_ST_CONNECT_FAIL: + case BLE_ST_SEND_CREATE_CONN_CANNEL: + break; + default: + return APP_BLE_OPERATION_ERROR; + } + + if (cur_state == next_state) { + return APP_BLE_NO_ERROR; // 状态未改变 + } + + log_info("scan_en:%d\n", en); + set_ble_work_state(next_state); + + if (en) { + scanning_setup_init(); // 设置扫描参数 + } + ble_op_scan_enable2(en, 0); + + return APP_BLE_NO_ERROR; +} + +/** + * @brief 初始化扫描参数 + */ +static void scanning_setup_init(void) +{ + ble_op_set_scan_param(SET_SCAN_TYPE, SET_SCAN_INTERVAL, SET_SCAN_WINDOW); +} + +/** + * @brief 创建连接的底层实现 + * @param conn_addr 对端设备地址 + * @param addr_type 地址类型 + */ +static void client_create_connection(u8 *conn_addr, u8 addr_type) +{ + if (get_ble_work_state() == BLE_ST_CREATE_CONN) { + log_info("already create conn!!!\n"); + return; + } + + struct create_conn_param_t *create_conn_par = (void *)scan_buffer; + create_conn_par->conn_interval = SET_CONN_INTERVAL; + create_conn_par->conn_latency = SET_CONN_LATENCY; + create_conn_par->supervision_timeout = SET_CONN_TIMEOUT; + memcpy(create_conn_par->peer_address, conn_addr, 6); + create_conn_par->peer_address_type = addr_type; + + set_ble_work_state(BLE_ST_CREATE_CONN); + log_info_hexdump(create_conn_par, sizeof(struct create_conn_param_t)); + ble_op_create_connection(create_conn_par); +} + +/** + * @brief 创建连接的封装 (兼容旧接口) + * @param conn_addr 对端设备地址 + * @param addr_type 地址类型 + */ +static void bt_ble_create_connection(u8 *conn_addr, u8 addr_type) +{ + client_create_connection(conn_addr, addr_type); +} + +/** + * @brief 取消正在创建的连接 + */ +static void client_create_connection_cannel(void) +{ + if (get_ble_work_state() == BLE_ST_CREATE_CONN) { + set_ble_work_state(BLE_ST_SEND_CREATE_CONN_CANNEL); + ble_op_create_connection_cancel(); + } +} + +/** + * @brief 断开当前BLE连接 + * @param priv 私有参数 (未使用) + * @return 0:成功, 非0:失败 + */ +static int ble_disconnect(void *priv) +{ + if (con_handle) { + if (BLE_ST_SEND_DISCONN != get_ble_work_state()) { + log_info(">>>ble send disconnect\n"); + set_ble_work_state(BLE_ST_SEND_DISCONN); + ble_op_disconnect(con_handle); + } else { + log_info(">>>ble wait disconnect...\n"); + } + return APP_BLE_NO_ERROR; + } else { + return APP_BLE_OPERATION_ERROR; + } +} + +/** + * @brief 检查发现的特征是否与目标UUID匹配 + * @param result_info 搜索结果 + */ +static void check_target_uuid_match(search_result_t *result_info) +{ + u32 i; + target_uuid_t *t_uuid; + + // 遍历预设的UUID表 + for (i = 0; i < client_config->search_uuid_cnt; i++) { + t_uuid = &client_config->search_uuid_table[i]; + + // 比较服务UUID + if (result_info->services.uuid16) { + if (result_info->services.uuid16 != t_uuid->services_uuid16) { + continue; + } + } else { + if (memcmp(result_info->services.uuid128, t_uuid->services_uuid128, 16)) { + continue; + } + } + + // 比较特征UUID + if (result_info->characteristic.uuid16) { + if (result_info->characteristic.uuid16 != t_uuid->characteristic_uuid16) { + continue; + } + } else { + if (memcmp(result_info->characteristic.uuid128, t_uuid->characteristic_uuid128, 16)) { + continue; + } + } + break; // 找到匹配 + } + + if (i >= client_config->search_uuid_cnt) { + return; // 未找到匹配 + } + + if (opt_handle_used_cnt >= OPT_HANDLE_MAX) { + log_info("opt_handle is full!!!\n"); + return; + } + + // 检查属性是否匹配 + if ((t_uuid->opt_type & result_info->characteristic.properties) != t_uuid->opt_type) { + log_info("properties not match!!!\n"); + return; + } + + log_info("match one uuid\n"); + + // 存储匹配到的句柄信息 + opt_handle_t *opt_get = &opt_handle_table[opt_handle_used_cnt++]; + opt_get->value_handle = result_info->characteristic.value_handle; + opt_get->search_uuid = t_uuid; + + switch (t_uuid->opt_type) { + case ATT_PROPERTY_READ: + target_handle.read_handle = result_info->characteristic.value_handle; + break; + case ATT_PROPERTY_WRITE_WITHOUT_RESPONSE: + target_handle.write_no_respond = result_info->characteristic.value_handle; + break; + case ATT_PROPERTY_WRITE: + target_handle.write_handle = result_info->characteristic.value_handle; + break; + case ATT_PROPERTY_NOTIFY: + target_handle.notify_handle = result_info->characteristic.value_handle; + break; + case ATT_PROPERTY_INDICATE: + target_handle.indicate_handle = result_info->characteristic.value_handle; + break; + default: + break; + } + + client_event_report(CLI_EVENT_MATCH_UUID, (u8 *)opt_get, sizeof(opt_handle_t)); +} + +/** + * @brief 在Profile搜索完成后,执行后续操作(如使能Notify/Indicate) + */ +static void do_operate_search_handle(void) +{ + u16 tmp_16; + u16 i, cur_opt_type; + opt_handle_t *opt_hdl_pt; + + log_info("opt_handle_used_cnt= %d\n", opt_handle_used_cnt); + log_info("find target_handle:"); + log_info_hexdump(&target_handle, sizeof(target_hdl_t)); + + if (0 == opt_handle_used_cnt) { + goto opt_end; + } + + // 遍历所有匹配到的句柄,并执行相应操作 + for (i = 0; i < opt_handle_used_cnt; i++) { + opt_hdl_pt = &opt_handle_table[i]; + cur_opt_type = opt_hdl_pt->search_uuid->opt_type; + switch ((u8)cur_opt_type) { + case ATT_PROPERTY_NOTIFY: + tmp_16 = 0x01; // 使能Notify + log_info("write_ntf_ccc:%04x\n", opt_hdl_pt->value_handle); + client_operation_send(opt_hdl_pt->value_handle + 1, (u8 *)&tmp_16, 2, ATT_OP_WRITE); + break; + case ATT_PROPERTY_INDICATE: + tmp_16 = 0x02; // 使能Indicate + log_info("write_ind_ccc:%04x\n", opt_hdl_pt->value_handle); + client_operation_send(opt_hdl_pt->value_handle + 1, (u8 *)&tmp_16, 2, ATT_OP_WRITE); + break; + // 其他操作可在此添加 + default: + break; + } + } + +opt_end: + set_ble_work_state(BLE_ST_SEARCH_COMPLETE); +} + +/** + * @brief 协议栈上报搜索结果的回调 + * @param result_info 搜索结果,为-1时表示搜索完成 + */ +void user_client_report_search_result(search_result_t *result_info) +{ + if (result_info == (void *) - 1) { + log_info("client_report_search_result finish!!!\n"); + do_operate_search_handle(); // 搜索完成,执行后续操作 + client_event_report(CLI_EVENT_SEARCH_PROFILE_COMPLETE, 0, 0); + return; + } + + log_info("\n*** services, uuid16:%04x,index=%d ***\n", result_info->services.uuid16, result_info->service_index); + log_info("{charactc, uuid16:%04x,index=%d,handle:%04x~%04x,value_handle=%04x}\n", + result_info->characteristic.uuid16, result_info->characteristic_index, + result_info->characteristic.start_handle, result_info->characteristic.end_handle, + result_info->characteristic.value_handle + ); + + check_target_uuid_match(result_info); // 检查是否是目标UUID +} + +/** + * @brief 协议栈上报对端设备数据的回调 + * @param report_data 数据报告 + */ +void user_client_report_data_callback(att_data_report_t *report_data) +{ +#if SHOW_RX_DATA_RATE + test_rx_data_count += report_data->blob_length; +#endif + + target_uuid_t *search_uuid = get_match_handle_target(report_data->value_handle); + + // 调用上层注册的回调函数 + if (client_config->report_data_callback) { + client_config->report_data_callback(report_data, search_uuid); + return; + } + + // 默认处理 switch (report_data->packet_type) { - case GATT_EVENT_NOTIFICATION: //notify + case GATT_EVENT_NOTIFICATION: + case GATT_EVENT_INDICATION: + case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: + case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT: break; - case GATT_EVENT_INDICATION://indicate - case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT://read - break; - case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT://read long - break; - default: break; } } -static const u8 test_remoter_name1[] = "CM-22222"; -// static const u8 test_remoter_name1[] = "AC637N_MX(BLE)";// -/* static const u8 test_remoter_name2[] = "AC630N_HID567(BLE)";// */ -static u16 default_client_write_handle; -static u16 test_client_timer = 0; -static const client_match_cfg_t match_dev01 = { - .create_conn_mode = BIT(CLI_CREAT_BY_NAME), - .compare_data_len = sizeof(test_remoter_name1) - 1, //去结束符 - .compare_data = test_remoter_name1, - .bonding_flag = 0, +/** + * @brief 开始搜索Profile + */ +static void client_search_profile_start(void) +{ + opt_handle_used_cnt = 0; + memset(&target_handle, 0, sizeof(target_hdl_t)); + user_client_init(con_handle, search_ram_buffer, SEARCH_PROFILE_BUFSIZE); + if (client_config->search_uuid_cnt) { + ble_op_search_profile_all(); // 搜索所有服务和特征 + } else { + user_client_set_search_complete(); // 如果没有指定UUID,直接结束 + } +} + +/** + * @brief 连接建立后,开始Profile相关流程 + * @param handle 连接句柄 + */ +static void client_profile_start(u16 handle) +{ + ble_op_att_send_init(handle, att_ram_buffer, ATT_RAM_BUFSIZE, ATT_LOCAL_MTU_SIZE); + set_ble_work_state(BLE_ST_CONNECT); + + if (0 == client_config->security_en) { + // 如果不要求加密,直接开始搜索Profile + client_search_profile_start(); + } +} + +/** + * @brief 协议栈事件总处理函数 + */ +static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) +{ + int mtu; + u8 status; + + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + + case ATT_EVENT_CAN_SEND_NOW: + can_send_now_wakeup(); + break; + + case HCI_EVENT_LE_META: + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE: + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + status = packet[3]; + if (status) { + log_info("LE_MASTER CREATE CONNECTION FAIL!!! %0x\n", status); + set_ble_work_state(BLE_ST_DISCONN); + break; + } + con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + log_info("HCI_SUBEVENT_LE_CONNECTION_COMPLETE : %0x\n", con_handle); + connection_update_complete_success(packet + 8); + client_profile_start(con_handle); + client_event_report(CLI_EVENT_CONNECTED, packet, size); + +#if WIRELESS_PAIR_BONDING + // 绑定模式下,保存地址信息 + memcpy(cur_peer_address_info, &packet[7], 7); + conn_pair_info.pair_flag = 1; + memcpy(&conn_pair_info.peer_address_info, cur_peer_address_info, 7); + conn_pair_info.head_tag = BLE_VM_HEAD_TAG; + conn_pair_info.tail_tag = BLE_VM_TAIL_TAG; + conn_pair_vm_do(&conn_pair_info, 1); +#else + if (pair_bond_enable) { + conn_pair_info.pair_flag = 1; + memcpy(&conn_pair_info.peer_address_info, &packet[7], 7); + conn_pair_vm_do(&conn_pair_info, 1); + pair_bond_enable = 0; + } +#endif + break; + + case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: + log_info("APP HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE\n"); + connection_update_complete_success(packet); + client_event_report(CLI_EVENT_CONNECTION_UPDATE, packet, size); + break; + + case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: + log_info("APP HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE"); + break; + + case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE: + log_info("APP HCI_SUBEVENT_LE_PHY_UPDATE %s\n", hci_event_le_meta_get_phy_update_complete_status(packet) ? "Fail" : "Succ"); + log_info("Tx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_tx_phy(packet)]); + log_info("Rx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_rx_phy(packet)]); + break; + } + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + log_info("HCI_EVENT_DISCONNECTION_COMPLETE: %0x\n", packet[5]); + con_handle = 0; + ble_op_att_send_init(con_handle, 0, 0, 0); + set_ble_work_state(BLE_ST_DISCONN); + client_event_report(CLI_EVENT_DISCONNECT, packet, size); + + // 断线后自动重连或重新扫描 +#if !WIRELESS_PAIR_BONDING + if (conn_pair_info.pair_flag) { + client_create_connect_api(0, 0, 1); // 尝试重连 + } else +#endif + { + bt_ble_scan_enable(0, 1); // 重新扫描 + } + break; + + case ATT_EVENT_MTU_EXCHANGE_COMPLETE: + mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; + log_info("ATT MTU = %u\n", mtu); + ble_op_att_set_send_mtu(mtu); + break; + + case GAP_EVENT_ADVERTISING_REPORT: + client_report_adv_data((void *)&packet[2], packet[1]); + break; + + case HCI_EVENT_ENCRYPTION_CHANGE: + log_info("HCI_EVENT_ENCRYPTION_CHANGE= %d\n", packet[2]); + if (client_config->security_en) { + // 加密完成后,开始搜索Profile + client_search_profile_start(); + } + break; + } + break; + } +} + +/** + * @brief SM (Security Manager) 事件处理 + */ +static void cbk_sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) +{ + sm_just_event_t *event = (void *)packet; + u32 tmp32; + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case SM_EVENT_JUST_WORKS_REQUEST: + sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); + log_info("Just Works Confirmed.\n"); + break; + case SM_EVENT_PASSKEY_DISPLAY_NUMBER: + memcpy(&tmp32, event->data, 4); + log_info("Passkey display: %06u.\n", tmp32); + break; + } + break; + } +} + +/** + * @brief 协议栈通知可以发送数据时的唤醒函数 + */ +static void can_send_now_wakeup(void) +{ + if (ble_resume_send_wakeup) { + ble_resume_send_wakeup(); + } +} + +/** + * @brief 获取ATT发送缓冲区剩余有效长度 + * @param priv 私有参数 (未使用) + * @return 剩余长度 + */ +static int get_buffer_vaild_len(void *priv) +{ + u32 vaild_len = 0; + ble_op_att_get_remain(&vaild_len); + return vaild_len; +} + +/** + * @brief 通用ATT操作发送函数 + * @param handle 特征值句柄 + * @param data 数据指针 + * @param len 数据长度 + * @param att_op_type ATT操作类型 + * @return 0:成功, 非0:失败 + */ +static int client_operation_send(u16 handle, u8 *data, u16 len, u8 att_op_type) +{ + int ret = APP_BLE_NO_ERROR; + if (!con_handle) { + return APP_BLE_OPERATION_ERROR; + } + if (!handle) { + log_info("handle is null\n"); + return APP_BLE_OPERATION_ERROR; + } + if (get_buffer_vaild_len(0) < len) { + return APP_BLE_BUFF_FULL; + } + + ret = ble_op_att_send_data(handle, data, len, att_op_type); + if (ret == BLE_BUFFER_FULL) { + ret = APP_BLE_BUFF_FULL; + } else { +#if SHOW_TX_DATA_RATE + test_tx_data_count += len; +#endif + } + return ret; +} + +/** + * @brief 发送写请求 (Write Request) + */ +static int client_write_send(void *priv, u8 *data, u16 len) +{ + return client_operation_send(target_handle.write_handle, data, len, ATT_OP_WRITE); +} + +/** + * @brief 发送无响应写请求 (Write Command) + */ +static int client_write_without_respond_send(void *priv, u8 *data, u16 len) +{ + return client_operation_send(target_handle.write_no_respond, data, len, ATT_OP_WRITE_WITHOUT_RESPOND); +} + +/** + * @brief 发送读请求 (Read Request) + */ +static int client_read_value_send(void *priv) +{ + u16 tmp_flag = 0x55A1; + return client_operation_send(target_handle.read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ); +} + +/** + * @brief 发送长读请求 (Read Long Request) + */ +static int client_read_long_value_send(void *priv) +{ + u16 tmp_flag = 0x55A2; + return client_operation_send(target_handle.read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ_LONG); +} + +/** + * @brief 注册数据发送恢复唤醒回调 + */ +static int client_regiest_wakeup_send(void *priv, void *cbk) +{ + ble_resume_send_wakeup = cbk; + return APP_BLE_NO_ERROR; +} + +/** + * @brief 注册应用层数据接收回调 + */ +static int client_regiest_recieve_cbk(void *priv, void *cbk) +{ + channel_priv = (u32)priv; + app_recieve_callback = cbk; + return APP_BLE_NO_ERROR; +} + +/** + * @brief 注册应用层状态变化回调 + */ +static int client_regiest_state_cbk(void *priv, void *cbk) +{ + channel_priv = (u32)priv; + app_ble_state_callback = cbk; + return APP_BLE_NO_ERROR; +} + +/** + * @brief 重新配置客户端搜索参数 + */ +static int client_init_config(void *priv, const client_conn_cfg_t *cfg) +{ + log_info("client_init_config\n"); + client_config = (client_conn_cfg_t *)cfg; // 重置配置 + return APP_BLE_NO_ERROR; +} + +/** + * @brief 设置强制搜索模式(根据RSSI过滤) + */ +static int client_force_search(u8 onoff, s8 rssi) +{ + force_seach_rssi = rssi; + if (force_seach_onoff != onoff) { + force_seach_onoff = onoff; + if (get_ble_work_state() == BLE_ST_CREATE_CONN) { + client_create_connection_cannel(); + } + } + return 0; +} + +/** + * @brief 创建连接的API接口 + * @param addr 地址 (mode=1时可为NULL) + * @param addr_type 地址类型 (mode=1时可忽略) + * @param mode 0:按地址连接, 1:按配对信息连接 + * @return 0:成功, 非0:失败 + */ +static int client_create_connect_api(u8 *addr, u8 addr_type, u8 mode) +{ + u8 cur_state = get_ble_work_state(); + switch (cur_state) { + case BLE_ST_SCAN: + case BLE_ST_IDLE: + case BLE_ST_INIT_OK: + case BLE_ST_NULL: + case BLE_ST_DISCONN: + case BLE_ST_CONNECT_FAIL: + case BLE_ST_SEND_CREATE_CONN_CANNEL: + break; + default: + return APP_BLE_OPERATION_ERROR; + } + + if (cur_state == BLE_ST_SCAN) { + log_info("stop scan\n"); + bt_ble_scan_enable(0, 0); + } + + if (mode == 1) { // 按配对信息连接 + if (conn_pair_info.pair_flag) { + log_info("pair to creat!\n"); + log_info_hexdump(conn_pair_info.peer_address_info, 7); + bt_ble_create_connection(&conn_pair_info.peer_address_info[1], conn_pair_info.peer_address_info[0]); + return 0; + } else { + log_info("no pair to creat!\n"); + return APP_BLE_OPERATION_ERROR; + } + } else { // 按地址连接 + log_info("addr to creat!\n"); + log_info_hexdump(addr, 7); + bt_ble_create_connection(addr, addr_type); + } + return 0; +} + +/** + * @brief 取消连接的API接口 + */ +static int client_create_cannel_api(void) +{ + if (get_ble_work_state() == BLE_ST_CREATE_CONN) { + client_create_connection_cannel(); + return 0; + } + return 1; +} + +/** + * @brief 重设Passkey的回调函数 + */ +static void reset_passkey_cb(u32 *key) +{ +#if 1 + u32 newkey = rand32(); + newkey &= 0xfffff; + if (newkey > 999999) { + newkey = newkey - 999999; + } + *key = newkey; + log_info("set new_key= %06u\n", *key); +#else + *key = 123456; // for debug +#endif +} + +/** + * @brief 初始化设备绑定相关配置 + */ +static void device_bonding_init(void) +{ + int i; + int cfg_bonding = 0; + client_match_cfg_t *cfg; + for (i = 0; i < CLIENT_MATCH_CONN_MAX; i++) { + cfg = client_config->match_dev_cfg[i]; + if (cfg == NULL) { + continue; + } + if (cfg->bonding_flag) { + cfg_bonding = 1; + } + } + if (!cfg_bonding) { + clear_bonding_info(); // 如果配置中没有需要绑定的设备,则清除本地绑定信息 + } +} + +/** + * @brief 查询客户端是否空闲(用于低功耗) + */ +static u8 client_idle_query(void) +{ + // 返回0表示不空闲,防止系统进入深眠 + return 0; +} + +// 注册低功耗目标 +REGISTER_LP_TARGET(client_user_target) = { + .name = "client_user_demo", + .is_idle = client_idle_query, }; -/* static const client_match_cfg_t match_dev02 = { */ -/* .create_conn_mode = BIT(CLI_CREAT_BY_NAME), */ -/* .compare_data_len = sizeof(test_remoter_name2) - 1, //去结束符 */ -/* .compare_data = test_remoter_name2, */ -/* .bonding_flag = 1, */ -/* }; */ +/** + * @brief 连接更新成功事件处理 + */ +static void connection_update_complete_success(u8 *packet) +{ + int conn_interval, conn_latency, conn_timeout; + conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); + conn_latency = hci_subevent_le_connection_update_complete_get_conn_latency(packet); + conn_timeout = hci_subevent_le_connection_update_complete_get_supervision_timeout(packet); + log_info("conn_interval = %d\n", conn_interval); + log_info("conn_latency = %d\n", conn_latency); + log_info("conn_timeout = %d\n", conn_timeout); +} +/** + * @brief L2CAP连接参数更新请求处理 + */ +int l2cap_connection_update_request_just(u8 *packet, hci_con_handle_t handle) +{ + log_info("slave request conn_update:\n-interval_min= %d,\n-interval_max= %d,\n-latency= %d,\n-timeout= %d\n", + little_endian_read_16(packet, 0), little_endian_read_16(packet, 2), + little_endian_read_16(packet, 4), little_endian_read_16(packet, 6)); + return 0; // 接受请求 +} + +/** + * @brief 根据句柄查找对应的目标UUID信息 + */ +static target_uuid_t *get_match_handle_target(u16 handle) +{ + for (int i = 0; i < opt_handle_used_cnt; i++) { + if (opt_handle_table[i].value_handle == handle) { + return opt_handle_table[i].search_uuid; + } + } + return NULL; +} + +/** + * @brief 设置数据包长度 + */ +static void set_connection_data_length(u16 tx_octets, u16 tx_time) +{ + if (con_handle) { + ble_op_set_data_length(con_handle, tx_octets, tx_time); + } +} + +/** + * @brief 设置PHY速率 + */ +static void set_connection_data_phy(u8 tx_phy, u8 rx_phy) +{ + if (0 == con_handle) { + return; + } + u8 all_phys = 0; + u16 phy_options = 0; + ble_op_set_ext_phy(con_handle, all_phys, tx_phy, rx_phy, phy_options); +} + +#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) +/** + * @brief 数据速率统计定时器处理函数 + */ +static void client_timer_handler(void) +{ + if (!con_handle) { + test_rx_data_count = 0; + test_tx_data_count = 0; + return; + } + if (test_rx_data_count) { + log_info("peer_rssi = %d\n", ble_vendor_get_peer_rssi(con_handle)); + log_info("%d bytes receive: %d.%02d KB/s \n", test_rx_data_count, test_rx_data_count / 1000, test_rx_data_count % 1000); + test_rx_data_count = 0; + } + if (test_tx_data_count) { + log_info("peer_rssi = %d\n", ble_vendor_get_peer_rssi(con_handle)); + log_info("%d bytes send: %d.%02d KB/s \n", test_tx_data_count, test_tx_data_count / 1000, test_tx_data_count % 1000); + test_tx_data_count = 0; + } +} + +/** + * @brief 启动数据速率统计定时器 + */ +static void client_timer_start(void) +{ + client_timer_handle = sys_timer_add(NULL, client_timer_handler, 1000); +} +#endif + +#if 1 //default +/** + * @brief 默认示例的数据上报处理 + */ +static void default_report_data_deal(att_data_report_t *report_data, target_uuid_t *search_uuid) +{ + log_info("report_data:%02x,%02x,%d,len(%d)", report_data->packet_type, + report_data->value_handle, report_data->value_offset, report_data->blob_length); + log_info_hexdump(report_data->blob, report_data->blob_length); + + switch (report_data->packet_type) { + case GATT_EVENT_NOTIFICATION: + case GATT_EVENT_INDICATION: + case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: + case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT: + break; + default: + break; + } +} + +/** + * @brief 默认示例的定时写数据函数 + */ static void default_test_write(void) { static u32 count = 0; count++; - int ret = client_operation_send(default_client_write_handle, &count, 16, ATT_OP_WRITE_WITHOUT_RESPOND); + int ret = client_operation_send(default_client_write_handle, (u8 *)&count, 16, ATT_OP_WRITE_WITHOUT_RESPOND); log_info("test_write:%x", ret); } +/** + * @brief 默认示例的事件回调处理 + */ static void default_event_callback(le_client_event_e event, u8 *packet, int size) { switch (event) { case CLI_EVENT_MATCH_DEV: { - client_match_cfg_t *match_dev = packet; + client_match_cfg_t *match_dev = (void *)packet; log_info("match_name:%s\n", match_dev->compare_data); } break; case CLI_EVENT_MATCH_UUID: { - opt_handle_t *opt_hdl = packet; + opt_handle_t *opt_hdl = (void *)packet; if (opt_hdl->search_uuid == &default_search_uuid_table[0]) { default_client_write_handle = opt_hdl->value_handle; log_info("match_uuid22\n"); @@ -248,1322 +1617,6 @@ static void default_event_callback(le_client_event_e event, u8 *packet, int size break; } } - - -static const client_conn_cfg_t client_conn_config_default = { - .match_dev_cfg[0] = &match_dev01, - .match_dev_cfg[1] = NULL, - .match_dev_cfg[2] = NULL, - /* .match_dev_cfg[1] = &match_dev02, */ - .report_data_callback = default_report_data_deal, - .search_uuid_cnt = (sizeof(default_search_uuid_table) / sizeof(target_uuid_t)), - .search_uuid_table = default_search_uuid_table, - .security_en = 0, - .event_callback = default_event_callback, -}; - -#endif - -//--------------------------------------------------------------------------- -static client_conn_cfg_t *client_config = (void *) &client_conn_config_default ; -//---------------------------------------------------------------------------- - - -static const struct conn_update_param_t connection_param_table[] = { - {16, 24, 0, 600},//11 - {12, 28, 0, 600},//3.7 - {8, 20, 0, 600}, - {50, 60, 0, 600}, -}; - -static u8 send_param_index = 3; - -static int client_create_connect_api(u8 *addr, u8 addr_type, u8 mode); -static int client_create_cannel_api(void); -//---------------------------------------------------------------------------- - -static void client_event_report(le_client_event_e event, u8 *packet, int size) -{ - if (client_config->event_callback) { - client_config->event_callback(event, packet, size); - } -} - -static bool check_device_is_match(u8 info_type, u8 *data, int size) -{ - int i; - u8 conn_mode = BIT(info_type); - client_match_cfg_t *cfg; - - /* log_info_hexdump(data,size); */ - - for (i = 0; i < CLIENT_MATCH_CONN_MAX; i++) { - cfg = client_config->match_dev_cfg[i]; - if (cfg == NULL) { - continue; - } - /* log_info("cfg = %08x\n",cfg); */ - /* log_info_hexdump(cfg,sizeof(client_match_cfg_t)); */ - if (cfg->create_conn_mode == conn_mode && size == cfg->compare_data_len) { - log_info("match check\n"); - /* log_info_hexdump(data, size); */ - /* log_info_hexdump(cfg->compare_data, size); */ - if (0 == memcmp(data, cfg->compare_data, cfg->compare_data_len)) { - log_info("match ok\n"); - pair_bond_enable = cfg->bonding_flag; - client_event_report(CLI_EVENT_MATCH_DEV, cfg, sizeof(client_match_cfg_t)); - return true; - } - } - } - return false; -} - -static void conn_pair_vm_do(struct pair_info_t *info, u8 rw_flag) -{ - /* return; */ - - int ret; - int vm_len = sizeof(struct pair_info_t); - - log_info("-conn_pair_info vm_do:%d\n", rw_flag); - if (rw_flag == 0) { - ret = syscfg_read(CFG_BLE_MODE_INFO, (u8 *)info, vm_len); - if (!ret) { - log_info("-null--\n"); - } - if ((BLE_VM_HEAD_TAG == info->head_tag) && (BLE_VM_TAIL_TAG == info->tail_tag)) { - log_info("-exist--\n"); - log_info_hexdump((u8 *)info, vm_len); - } else { - memset(info, 0, vm_len); - info->head_tag = BLE_VM_HEAD_TAG; - info->tail_tag = BLE_VM_TAIL_TAG; - } - } else { - syscfg_write(CFG_BLE_MODE_INFO, (u8 *)info, vm_len); - } -} -//------------------------------------------------------------ -static ble_state_e get_ble_work_state(void) -{ - return ble_work_state; -} - -void clear_bonding_info(void) -{ - log_info("client_clear_bonding_info\n"); - conn_pair_vm_do(&conn_pair_info, 0); - if (conn_pair_info.pair_flag) { - //del pair bond - memset(&conn_pair_info, 0, sizeof(struct pair_info_t)); - conn_pair_vm_do(&conn_pair_info, 1); - -#if WIRELESS_PAIR_BONDING - if (get_ble_work_state() == BLE_ST_CREATE_CONN) { - client_create_connection_cannel(); - bt_ble_scan_enable(0, 1); - } else if (con_handle) { - ble_disconnect(); - } -#endif - } -} - -//------------------------------------------------------------ -static void set_ble_work_state(ble_state_e state) -{ - if (state != ble_work_state) { - log_info("ble_client_work_st:%x->%x\n", ble_work_state, state); - ble_work_state = state; - if (app_ble_state_callback) { - app_ble_state_callback((void *)channel_priv, state); - } - } -} - - -//------------------------------------------------------------------------------- -static void check_target_uuid_match(search_result_t *result_info) -{ - u32 i; - target_uuid_t *t_uuid; - - for (i = 0; i < client_config->search_uuid_cnt; i++) { - t_uuid = &client_config->search_uuid_table[i]; - if (result_info->services.uuid16) { - if (result_info->services.uuid16 != t_uuid->services_uuid16) { - /* log_info("b1"); */ - continue; - } - } else { - if (memcmp(result_info->services.uuid128, t_uuid->services_uuid128, 16)) { - /* log_info("b2"); */ - continue; - } - } - - if (result_info->characteristic.uuid16) { - if (result_info->characteristic.uuid16 != t_uuid->characteristic_uuid16) { - /* log_info("b3"); */ - /* log_info("%d: %04x--%04x",result_info->characteristic.uuid16,t_uuid->characteristic_uuid16); */ - continue; - } - } else { - if (memcmp(result_info->characteristic.uuid128, t_uuid->characteristic_uuid128, 16)) { - /* log_info("b4"); */ - continue; - } - } - - break;//match one - } - - if (i >= client_config->search_uuid_cnt) { - return; - } - - if (opt_handle_used_cnt >= OPT_HANDLE_MAX) { - log_info("opt_handle is full!!!\n"); - return; - } - - if ((t_uuid->opt_type & result_info->characteristic.properties) != t_uuid->opt_type) { - log_info("properties not match!!!\n"); - return; - } - - log_info("match one uuid\n"); - - opt_handle_t *opt_get = &opt_handle_table[opt_handle_used_cnt++]; - opt_get->value_handle = result_info->characteristic.value_handle; - opt_get->search_uuid = t_uuid; - - switch (t_uuid->opt_type) { - case ATT_PROPERTY_READ: - target_handle.read_handle = result_info->characteristic.value_handle; - break; - - case ATT_PROPERTY_WRITE_WITHOUT_RESPONSE: - target_handle.write_no_respond = result_info->characteristic.value_handle; - break; - - case ATT_PROPERTY_WRITE: - target_handle.write_handle = result_info->characteristic.value_handle; - break; - - case ATT_PROPERTY_NOTIFY: - target_handle.notify_handle = result_info->characteristic.value_handle; - break; - - case ATT_PROPERTY_INDICATE: - target_handle.indicate_handle = result_info->characteristic.value_handle; - break; - - default: - break; - } - - client_event_report(CLI_EVENT_MATCH_UUID, opt_get, sizeof(opt_handle_t)); - -} - -//操作handle,完成 write ccc -static void do_operate_search_handle(void) -{ - u16 tmp_16; - u16 i, cur_opt_type; - opt_handle_t *opt_hdl_pt; - - log_info("opt_handle_used_cnt= %d\n", opt_handle_used_cnt); - - log_info("find target_handle:"); - log_info_hexdump(&target_handle, sizeof(target_hdl_t)); - - if (0 == opt_handle_used_cnt) { - goto opt_end; - } - - /* test_send_conn_update();//for test */ - - for (i = 0; i < opt_handle_used_cnt; i++) { - opt_hdl_pt = &opt_handle_table[i]; - cur_opt_type = opt_hdl_pt->search_uuid->opt_type; - switch ((u8)cur_opt_type) { - case ATT_PROPERTY_READ: - if (1) { - tmp_16 = 0x55A2;//fixed - log_info("read_long:%04x\n", opt_hdl_pt->value_handle); - client_operation_send(opt_hdl_pt->value_handle, (u8 *)&tmp_16, 2, ATT_OP_READ_LONG); - } else { - tmp_16 = 0x55A1;//fixed - log_info("read:%04x\n", opt_hdl_pt->value_handle); - client_operation_send(opt_hdl_pt->value_handle, (u8 *)&tmp_16, 2, ATT_OP_READ); - } - break; - - case ATT_PROPERTY_NOTIFY: - tmp_16 = 0x01;//fixed - log_info("write_ntf_ccc:%04x\n", opt_hdl_pt->value_handle); - client_operation_send(opt_hdl_pt->value_handle + 1, &tmp_16, 2, ATT_OP_WRITE); - break; - - case ATT_PROPERTY_INDICATE: - tmp_16 = 0x02;//fixed - log_info("write_ind_ccc:%04x\n", opt_hdl_pt->value_handle); - client_operation_send(opt_hdl_pt->value_handle + 1, &tmp_16, 2, ATT_OP_WRITE); - break; - - default: - break; - } - } - -opt_end: - set_ble_work_state(BLE_ST_SEARCH_COMPLETE); - -} - -//协议栈内部调用 -//return: 0--accept,1--reject -int l2cap_connection_update_request_just(u8 *packet, hci_con_handle_t handle) -{ - log_info("slave request conn_update:\n-interval_min= %d,\n-interval_max= %d,\n-latency= %d,\n-timeout= %d\n", - little_endian_read_16(packet, 0), little_endian_read_16(packet, 2), - little_endian_read_16(packet, 4), little_endian_read_16(packet, 6)); - return 0; - /* return 1; */ -} - -//协议栈内部调用 -void user_client_report_search_result(search_result_t *result_info) -{ - if (result_info == (void *) - 1) { - log_info("client_report_search_result finish!!!\n"); - do_operate_search_handle(); - client_event_report(CLI_EVENT_SEARCH_PROFILE_COMPLETE, 0, 0); - return; - } - - log_info("\n*** services, uuid16:%04x,index=%d ***\n", result_info->services.uuid16, result_info->service_index); - log_info("{charactc, uuid16:%04x,index=%d,handle:%04x~%04x,value_handle=%04x}\n", - result_info->characteristic.uuid16, result_info->characteristic_index, - result_info->characteristic.start_handle, result_info->characteristic.end_handle, - result_info->characteristic.value_handle - ); - - if (!result_info->services.uuid16) { - log_info("######services_uuid128:"); - log_info_hexdump(result_info->services.uuid128, 16); - } - - if (!result_info->characteristic.uuid16) { - log_info("######charact_uuid128:"); - log_info_hexdump(result_info->characteristic.uuid128, 16); - } - - check_target_uuid_match(result_info); -} - -#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) - -static u32 client_timer_handle = 0; -static u32 test_rx_data_count; -static u32 test_tx_data_count; - -static void client_timer_handler(void) -{ - if (!con_handle) { - test_rx_data_count = 0; - - test_tx_data_count = 0; - - return; - } - - if (test_rx_data_count) { - log_info("peer_rssi = %d\n", ble_vendor_get_peer_rssi(con_handle)); - /* log_info("\n-ble_data_rate: %d bps-\n", test_rx_data_count * 8); */ - log_info("%d bytes receive: %d.%02d KB/s \n", test_rx_data_count, test_rx_data_count / 1000, test_rx_data_count % 1000); - - test_rx_data_count = 0; - } - - if (test_tx_data_count) { - log_info("peer_rssi = %d\n", ble_vendor_get_peer_rssi(con_handle)); - log_info("%d bytes send: %d.%02d KB/s \n", test_tx_data_count, test_tx_data_count / 1000, test_tx_data_count % 1000); - - test_tx_data_count = 0; - } -} - -static void client_timer_start(void) -{ - client_timer_handle = sys_timer_add(NULL, client_timer_handler, 1000); -} - -#endif /* SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE */ - -static target_uuid_t *get_match_handle_target(u16 handle) -{ - for (int i = 0; i < opt_handle_used_cnt; i++) { - if (opt_handle_table[i].value_handle == handle) { - return opt_handle_table[i].search_uuid; - } - } - return NULL; -} - -//协议栈内部调用 -void user_client_report_data_callback(att_data_report_t *report_data) -{ - /* log_info("\n-report_data:type %02x,handle %04x,offset %d,len %d:",report_data->packet_type, */ - /* report_data->value_handle,report_data->value_offset,report_data->blob_length); */ - /* log_info_hexdump(report_data->blob,report_data->blob_length); */ - -#if SHOW_RX_DATA_RATE - test_rx_data_count += report_data->blob_length; -#endif /* SHOW_RX_DATA_RATE */ - - target_uuid_t *search_uuid = get_match_handle_target(report_data->value_handle); - - if (client_config->report_data_callback) { - client_config->report_data_callback(report_data, search_uuid); - return; - } - - switch (report_data->packet_type) { - case GATT_EVENT_NOTIFICATION://notify -// log_info("\n-notify_rx(%d):",report_data->blob_length); - case GATT_EVENT_INDICATION://indicate - case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT://read - case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT://read long - break; - default: - break; - } -} - -// PRIMARY_SERVICE,AE30 -/* static const u16 test_services_uuid16 = 0xae30; */ - -// PRIMARY_SERVICE, 0000F530-1212-EFDE-1523-785FEABCD123 -/* static const u8 test_services_uuid128[16] = {0x00,0x00,0xF5,0x30 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23}; */ - -static void client_search_profile_start(void) -{ - opt_handle_used_cnt = 0; - memset(&target_handle, 0, sizeof(target_hdl_t)); - user_client_init(con_handle, search_ram_buffer, SEARCH_PROFILE_BUFSIZE); - if (client_config->search_uuid_cnt) { - printf("[debug] %s, %d\n", __FUNCTION__, __LINE__); - ble_op_search_profile_all(); - } else { - printf("[debug] %s, %d\n", __FUNCTION__, __LINE__); - user_client_set_search_complete(); - } -} - -//------------------------------------------------------------ -static bool resolve_adv_report(u8 *adv_address, u8 data_length, u8 *data, s8 rssi) -{ - u8 i, lenght, ad_type; - u8 *adv_data_pt; - u8 find_remoter = 0; - /* u8 tmp_addr[6]; */ - u32 tmp32; - - if (check_device_is_match(CLI_CREAT_BY_ADDRESS, adv_address, 6)) { - find_remoter = 1; - } - - adv_data_pt = data; - for (i = 0; i < data_length;) { - if (*adv_data_pt == 0) { - /* log_info("analyze end\n"); */ - break; - } - - lenght = *adv_data_pt++; - - if (lenght >= data_length || (lenght + i) >= data_length) { - /*过滤非标准包格式*/ - printf("!!!error_adv_packet:"); - put_buf(data, data_length); - break; - } - - ad_type = *adv_data_pt++; - i += (lenght + 1); - - switch (ad_type) { - case HCI_EIR_DATATYPE_FLAGS: - /* log_info("flags:%02x\n",adv_data_pt[0]); */ - break; - - case HCI_EIR_DATATYPE_MORE_16BIT_SERVICE_UUIDS: - case HCI_EIR_DATATYPE_COMPLETE_16BIT_SERVICE_UUIDS: - case HCI_EIR_DATATYPE_MORE_32BIT_SERVICE_UUIDS: - case HCI_EIR_DATATYPE_COMPLETE_32BIT_SERVICE_UUIDS: - case HCI_EIR_DATATYPE_MORE_128BIT_SERVICE_UUIDS: - case HCI_EIR_DATATYPE_COMPLETE_128BIT_SERVICE_UUIDS: - /* log_info("service uuid:"); */ - /* log_info_hexdump(adv_data_pt, lenght - 1); */ - break; - - case HCI_EIR_DATATYPE_COMPLETE_LOCAL_NAME: - case HCI_EIR_DATATYPE_SHORTENED_LOCAL_NAME: - tmp32 = adv_data_pt[lenght - 1]; - adv_data_pt[lenght - 1] = 0;; - log_info("remoter_name: %s,rssi:%d\n", adv_data_pt, rssi); - log_info_hexdump(adv_address, 6); - adv_data_pt[lenght - 1] = tmp32; - //------- -#if 0 //无线麦产线通过识别特殊字符串进行近距离连接测试 - if (0 == memcmp(adv_data_pt, SPECIFIC_STRING, strlen(SPECIFIC_STRING))) { - flag_specific_adv_name = 1; - flag_need_judge_rssi = 1; - } - if (flag_specific_sacn) { - flag_need_judge_rssi = 1; - } - if (flag_need_judge_rssi) { - flag_need_judge_rssi = 0; - if (rssi + TCFG_WIRELESS_RSSI < 0) { - printf("rssi no satisfy,break"); - break; - } else { - printf("rssi satisfy,RSSI = %d", rssi); - if (flag_specific_adv_name) { - find_remoter = 1; - break; - } - } - } - - flag_specific_adv_name = 0; -#endif - -#if SUPPORT_TEST_BOX_BLE_MASTER_TEST_EN -#if !WIRELESS_TOOL_BLE_NAME_EN -#define TEST_BOX_BLE_NAME "W_MIC_01" -#define TEST_BOX_BLE_NAME_LEN sizeof(TEST_BOX_BLE_NAME) - printf("TEST_BOX_BLE_NAME_LEN=%d", TEST_BOX_BLE_NAME_LEN); - if (0 == memcmp(adv_data_pt, TEST_BOX_BLE_NAME, TEST_BOX_BLE_NAME_LEN)) { - find_remoter = 1; - break; - } -#else - put_buf(match_name, strlen(match_name)); - if (lenght == strlen(match_name) + 1) { - printf("TEST_BOX_BLE_NAME_LEN=%d", TEST_BOX_BLE_NAME_LEN); - if (0 == memcmp(adv_data_pt, match_name, lenght - 1)) { - find_remoter = 1; - break; - } - } -#endif -#endif - /* log_info("target name:%s", client_config->compare_data); */ - if (check_device_is_match(CLI_CREAT_BY_NAME, adv_data_pt, lenght - 1)) { - find_remoter = 1; - log_info("catch name ok\n"); - } - break; - - case HCI_EIR_DATATYPE_MANUFACTURER_SPECIFIC_DATA: - if (check_device_is_match(CLI_CREAT_BY_TAG, adv_data_pt, lenght - 1)) { - log_info("get_tag_string!\n"); - find_remoter = 1; - } - break; - - case HCI_EIR_DATATYPE_APPEARANCE_DATA: - /* log_info("get_class_type:%04x\n",little_endian_read_16(adv_data_pt,0)); */ - break; - - default: - /* log_info("unknow ad_type:"); */ - break; - } - - if (find_remoter) { - log_info_hexdump(adv_data_pt, lenght - 1); - } - adv_data_pt += (lenght - 1); - } - - return find_remoter; -} - -static void client_report_adv_data(adv_report_t *report_pt, u16 len) -{ - bool find_remoter; - - /* log_info("event_type,addr_type: %x,%x; ",report_pt->event_type,report_pt->address_type); */ - /* log_info_hexdump(report_pt->address,6); */ - - /* log_info("adv_data_display:"); */ - /* log_info_hexdump(report_pt->data,report_pt->length); */ - -// log_info("rssi:%d\n",report_pt->rssi); -#if WIRELESS_PAIR_BONDING - - if (conn_pair_info.pair_flag) { - if (report_pt->event_type != 1) { - return; - } - printf("report_add_type == %x", report_pt->address_type); - printf("conn_add_type == %x", conn_pair_info.peer_address_info[0]); - put_buf(report_pt->address, 6); - put_buf(&conn_pair_info.peer_address_info[1], 6); - if (report_pt->address_type == conn_pair_info.peer_address_info[0] - && 0 == memcmp(&conn_pair_info.peer_address_info[1], report_pt->address, 6)) { - log_info("match bond_dev\n"); - find_remoter = 1; - goto just_creat; - } else { - return; - } - } - find_remoter = resolve_adv_report(report_pt->address, report_pt->length, report_pt->data, report_pt->rssi); - -just_creat: - -#else - find_remoter = resolve_adv_report(report_pt->address, report_pt->length, report_pt->data, report_pt->rssi); -#endif - - if (find_remoter) { - if (force_seach_onoff && force_seach_rssi > report_pt->rssi) { - log_info("match but rssi fail!!!:%d,%d\n", force_seach_rssi, report_pt->rssi); - return; - } - - log_info("rssi:%d\n", report_pt->rssi); - log_info("\n*********create_connection***********\n"); - log_info("***remote type %d,addr:", report_pt->address_type); - log_info_hexdump(report_pt->address, 6); - bt_ble_scan_enable(0, 0); - client_create_connect_api(report_pt->address, report_pt->address_type, 0); - log_info("*create_finish\n"); - } -} - - -static void client_create_connection(u8 *conn_addr, u8 addr_type) -{ - struct create_conn_param_t *create_conn_par = scan_buffer; - if (get_ble_work_state() == BLE_ST_CREATE_CONN) { - log_info("already create conn!!!\n"); - return; - } - create_conn_par->conn_interval = SET_CONN_INTERVAL; - create_conn_par->conn_latency = SET_CONN_LATENCY; - create_conn_par->supervision_timeout = SET_CONN_TIMEOUT; - memcpy(create_conn_par->peer_address, conn_addr, 6); - create_conn_par->peer_address_type = addr_type; - - set_ble_work_state(BLE_ST_CREATE_CONN); - log_info_hexdump(create_conn_par, sizeof(struct create_conn_param_t)); - ble_op_create_connection(create_conn_par); -} - -static void bt_ble_create_connection(u8 *conn_addr, u8 addr_type) -{ - client_create_connection(conn_addr, addr_type); -} - -static void client_create_connection_cannel(void) -{ - if (get_ble_work_state() == BLE_ST_CREATE_CONN) { - set_ble_work_state(BLE_ST_SEND_CREATE_CONN_CANNEL); - ble_op_create_connection_cancel(); - } -} - -static int ble_disconnect(void *priv) -{ - if (con_handle) { - if (BLE_ST_SEND_DISCONN != get_ble_work_state()) { - log_info(">>>ble send disconnect\n"); - set_ble_work_state(BLE_ST_SEND_DISCONN); - ble_op_disconnect(con_handle); - } else { - log_info(">>>ble wait disconnect...\n"); - } - return APP_BLE_NO_ERROR; - } else { - return APP_BLE_OPERATION_ERROR; - } -} - -static void connection_update_complete_success(u8 *packet) -{ - int con_handle, conn_interval, conn_latency, conn_timeout; - - conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); - conn_latency = hci_subevent_le_connection_update_complete_get_conn_latency(packet); - conn_timeout = hci_subevent_le_connection_update_complete_get_supervision_timeout(packet); - - log_info("conn_interval = %d\n", conn_interval); - log_info("conn_latency = %d\n", conn_latency); - log_info("conn_timeout = %d\n", conn_timeout); -} - - -static void cbk_sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) -{ - sm_just_event_t *event = (void *)packet; - u32 tmp32; - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (hci_event_packet_get_type(packet)) { - case SM_EVENT_JUST_WORKS_REQUEST: - sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); - log_info("Just Works Confirmed.\n"); - break; - case SM_EVENT_PASSKEY_DISPLAY_NUMBER: - log_info_hexdump(packet, size); - memcpy(&tmp32, event->data, 4); - log_info("Passkey display: %06u.\n", tmp32); - break; - } - break; - } -} - -static void can_send_now_wakeup(void) -{ - /* putchar('E'); */ - if (ble_resume_send_wakeup) { - ble_resume_send_wakeup(); - } -} - -const char *const phy_result[] = { - "None", - "1M", - "2M", - "Coded", -}; - -static void set_connection_data_length(u16 tx_octets, u16 tx_time) -{ - if (con_handle) { - ble_op_set_data_length(con_handle, tx_octets, tx_time); - } -} - -static void set_connection_data_phy(u8 tx_phy, u8 rx_phy) -{ - if (0 == con_handle) { - return; - } - - u8 all_phys = 0; - u16 phy_options = 0; - - ble_op_set_ext_phy(con_handle, all_phys, tx_phy, rx_phy, phy_options); -} - -static void client_profile_start(u16 con_handle) -{ - ble_op_att_send_init(con_handle, att_ram_buffer, ATT_RAM_BUFSIZE, ATT_LOCAL_MTU_SIZE); - set_ble_work_state(BLE_ST_CONNECT); - printf("[debug] %s, %d\n", __FUNCTION__, __LINE__); - if (0 == client_config->security_en) { - printf("[debug] %s, %d\n", __FUNCTION__, __LINE__); - client_search_profile_start(); - } -} - -/* LISTING_START(packetHandler): Packet Handler */ -static void cbk_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) -{ - int mtu; - u32 tmp; - u8 status; - - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (hci_event_packet_get_type(packet)) { - - /* case DAEMON_EVENT_HCI_PACKET_SENT: */ - /* break; */ - case ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE: - log_info("ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE\n"); - case ATT_EVENT_CAN_SEND_NOW: - can_send_now_wakeup(); - break; - - case HCI_EVENT_LE_META: - switch (hci_event_le_meta_get_subevent_code(packet)) { - - case HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE: - status = hci_subevent_le_enhanced_connection_complete_get_status(packet); - if (status) { - log_info("LE_MASTER CREATE CONNECTION FAIL!!! %0x\n", status); - set_ble_work_state(BLE_ST_DISCONN); - break; - } - con_handle = hci_subevent_le_enhanced_connection_complete_get_connection_handle(packet); - log_info("HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE : 0x%0x\n", con_handle); - log_info("conn_interval = %d\n", hci_subevent_le_enhanced_connection_complete_get_conn_interval(packet)); - log_info("conn_latency = %d\n", hci_subevent_le_enhanced_connection_complete_get_conn_latency(packet)); - log_info("conn_timeout = %d\n", hci_subevent_le_enhanced_connection_complete_get_supervision_timeout(packet)); - client_profile_start(con_handle); - break; - - case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: - if (packet[3]) { - log_info("LE_MASTER CREATE CONNECTION FAIL!!! %0x\n", packet[3]); - set_ble_work_state(BLE_ST_DISCONN); - break; - } - con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); - log_info("HCI_SUBEVENT_LE_CONNECTION_COMPLETE : %0x\n", con_handle); - connection_update_complete_success(packet + 8); - client_profile_start(con_handle); - client_event_report(CLI_EVENT_CONNECTED, packet, size); -#if WIRELESS_PAIR_BONDING - memcpy(cur_peer_address_info, &packet[7], 7); - conn_pair_info.pair_flag = 1; - printf("pair_flag == %d", conn_pair_info.pair_flag); - put_buf(cur_peer_address_info, 7); - memcpy(&conn_pair_info.peer_address_info, cur_peer_address_info, 7); - conn_pair_info.head_tag = BLE_VM_HEAD_TAG; - conn_pair_info.tail_tag = BLE_VM_TAIL_TAG; - conn_pair_vm_do(&conn_pair_info, 1); -#else - if (pair_bond_enable) { - conn_pair_info.pair_flag = 1; - memcpy(&conn_pair_info.peer_address_info, &packet[7], 7); - conn_pair_vm_do(&conn_pair_info, 1); - pair_bond_enable = 0; - } -#endif - break; - - case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: - log_info("APP HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE\n"); - connection_update_complete_success(packet); - - client_event_report(CLI_EVENT_CONNECTION_UPDATE, packet, size); - - break; - - case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: - log_info("APP HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE"); - break; - - case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE: - log_info("APP HCI_SUBEVENT_LE_PHY_UPDATE %s\n", hci_event_le_meta_get_phy_update_complete_status(packet) ? "Fail" : "Succ"); - log_info("Tx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_tx_phy(packet)]); - log_info("Rx PHY: %s\n", phy_result[hci_event_le_meta_get_phy_update_complete_rx_phy(packet)]); - break; - - } - break; - - case HCI_EVENT_DISCONNECTION_COMPLETE: - log_info("HCI_EVENT_DISCONNECTION_COMPLETE: %0x\n", packet[5]); - con_handle = 0; - ble_op_att_send_init(con_handle, 0, 0, 0); - set_ble_work_state(BLE_ST_DISCONN); - client_event_report(CLI_EVENT_DISCONNECT, packet, size); - - //auto to do -#if !WIRELESS_PAIR_BONDING - if (conn_pair_info.pair_flag) { - client_create_connect_api(0, 0, 1); - } else -#endif - { - bt_ble_scan_enable(0, 1); - } - break; - - case ATT_EVENT_MTU_EXCHANGE_COMPLETE: - mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; - log_info("ATT MTU = %u\n", mtu); - ble_op_att_set_send_mtu(mtu); - break; - - case HCI_EVENT_VENDOR_REMOTE_TEST: - log_info("--- HCI_EVENT_VENDOR_REMOTE_TEST\n"); - break; - - case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: - tmp = little_endian_read_16(packet, 4); - log_info("-update_rsp: %02x\n", tmp); - break; - - case GAP_EVENT_ADVERTISING_REPORT: - /* putchar('@'); */ - client_report_adv_data((void *)&packet[2], packet[1]); - break; - - case HCI_EVENT_ENCRYPTION_CHANGE: - log_info("HCI_EVENT_ENCRYPTION_CHANGE= %d\n", packet[2]); - if (client_config->security_en) { - client_search_profile_start(); - } - break; - } - break; - } -} - - -static int get_buffer_vaild_len(void *priv) -{ - u32 vaild_len = 0; - ble_op_att_get_remain(&vaild_len); - return vaild_len; -} - -static int client_operation_send(u16 handle, u8 *data, u16 len, u8 att_op_type) -{ - int ret = APP_BLE_NO_ERROR; - if (!con_handle) { - return APP_BLE_OPERATION_ERROR; - } - - if (!handle) { - log_info("handle is null\n"); - return APP_BLE_OPERATION_ERROR; - } - - - if (get_buffer_vaild_len(0) < len) { - /* log_info("opt_buff_full!!!\n"); */ - return APP_BLE_BUFF_FULL; - } - - ret = ble_op_att_send_data(handle, data, len, att_op_type); - if (ret == BLE_BUFFER_FULL) { - ret = APP_BLE_BUFF_FULL; - } else { -#if SHOW_TX_DATA_RATE - test_tx_data_count += len; -#endif /* SHOW_TX_DATA_RATE */ - } - - return ret; -} - - -//----------------------------------------------- -static int client_write_send(void *priv, u8 *data, u16 len) -{ - return client_operation_send(target_handle.write_handle, data, len, ATT_OP_WRITE); -} - -static int client_write_without_respond_send(void *priv, u8 *data, u16 len) -{ - return client_operation_send(target_handle.write_no_respond, data, len, ATT_OP_WRITE_WITHOUT_RESPOND); -} - -static int client_read_value_send(void *priv) -{ - u16 tmp_flag = 0x55A1; - return client_operation_send(target_handle.read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ); -} - -static int client_read_long_value_send(void *priv) -{ - u16 tmp_flag = 0x55A2; - return client_operation_send(target_handle.read_handle, (u8 *)&tmp_flag, 2, ATT_OP_READ_LONG); -} - - -//扫描数设置 -static void scanning_setup_init(void) -{ - ble_op_set_scan_param(SET_SCAN_TYPE, SET_SCAN_INTERVAL, SET_SCAN_WINDOW); -} - -static int bt_ble_scan_enable(void *priv, u32 en) -{ - ble_state_e next_state, cur_state; - - if (!scan_ctrl_en && en) { - return APP_BLE_OPERATION_ERROR; - } - - if (en) { - next_state = BLE_ST_SCAN; - } else { - next_state = BLE_ST_IDLE; - } - - cur_state = get_ble_work_state(); - switch (cur_state) { - case BLE_ST_SCAN: - case BLE_ST_IDLE: - case BLE_ST_INIT_OK: - case BLE_ST_NULL: - case BLE_ST_DISCONN: - case BLE_ST_CONNECT_FAIL: - case BLE_ST_SEND_CREATE_CONN_CANNEL: - break; - default: - return APP_BLE_OPERATION_ERROR; - break; - } - - if (cur_state == next_state) { - return APP_BLE_NO_ERROR; - } - - - log_info("scan_en:%d\n", en); - set_ble_work_state(next_state); - - if (en) { - scanning_setup_init(); - } - ble_op_scan_enable2(en, 0); - - return APP_BLE_NO_ERROR; -} - -static int client_regiest_wakeup_send(void *priv, void *cbk) -{ - /* att_regist_wakeup_send(cbk); */ - return APP_BLE_NO_ERROR; -} - -static int client_regiest_recieve_cbk(void *priv, void *cbk) -{ - channel_priv = (u32)priv; - app_recieve_callback = cbk; - return APP_BLE_NO_ERROR; -} - -static int client_regiest_state_cbk(void *priv, void *cbk) -{ - channel_priv = (u32)priv; - app_ble_state_callback = cbk; - return APP_BLE_NO_ERROR; -} - -//该接口重新配置搜索的配置项 -static int client_init_config(void *priv, const client_conn_cfg_t *cfg) -{ - log_info("client_init_config\n"); - client_config = cfg;//reset config - return APP_BLE_NO_ERROR; -} - -//可配置进入强制搜索方式连接,更加信号强度过滤设备 -static int client_force_search(u8 onoff, s8 rssi) -{ - force_seach_rssi = rssi; - if (force_seach_onoff != onoff) { - - force_seach_onoff = onoff; - //强制搜索前后,关创建监听 - if (get_ble_work_state() == BLE_ST_CREATE_CONN) { - client_create_connection_cannel(); - } - } - return 0; -} - -static int client_create_connect_api(u8 *addr, u8 addr_type, u8 mode) -{ - u8 cur_state = get_ble_work_state(); - - switch (cur_state) { - case BLE_ST_SCAN: - case BLE_ST_IDLE: - case BLE_ST_INIT_OK: - case BLE_ST_NULL: - case BLE_ST_DISCONN: - case BLE_ST_CONNECT_FAIL: - case BLE_ST_SEND_CREATE_CONN_CANNEL: - break; - default: - return APP_BLE_OPERATION_ERROR; - break; - } - - if (cur_state == BLE_ST_SCAN) { - log_info("stop scan\n"); - bt_ble_scan_enable(0, 0); - } - - //pair mode - if (mode == 1) { - if (conn_pair_info.pair_flag) { - if (conn_pair_info.pair_flag) { - //有配对,跳过搜索,直接创建init_creat - log_info("pair to creat!\n"); - log_info_hexdump(conn_pair_info.peer_address_info, 7); - bt_ble_create_connection(&conn_pair_info.peer_address_info[1], conn_pair_info.peer_address_info[0]); - return 0; - } - - } else { - log_info("no pair to creat!\n"); - return APP_BLE_OPERATION_ERROR; - } - } else { - log_info("addr to creat!\n"); - log_info_hexdump(addr, 7); - bt_ble_create_connection(addr, addr_type); - } - return 0; -} - -static int client_create_cannel_api(void) -{ - if (get_ble_work_state() == BLE_ST_CREATE_CONN) { - client_create_connection_cannel(); - return 0; - } - return 1; -} - -static const struct ble_client_operation_t client_operation = { - .scan_enable = bt_ble_scan_enable, - .disconnect = ble_disconnect, - .get_buffer_vaild = get_buffer_vaild_len, - .write_data = (void *)client_write_without_respond_send, - .read_do = (void *)client_read_value_send, - .regist_wakeup_send = client_regiest_wakeup_send, - .regist_recieve_cbk = client_regiest_recieve_cbk, - .regist_state_cbk = client_regiest_state_cbk, - .init_config = client_init_config, - .opt_comm_send = client_operation_send, - .set_force_search = client_force_search, - .create_connect = client_create_connect_api, - .create_connect_cannel = client_create_cannel_api, - .get_work_state = get_ble_work_state, -}; - -struct ble_client_operation_t *ble_get_client_operation_table(void) -{ - return &client_operation; -} - -#define PASSKEY_ENTER_ENABLE 0 //输入passkey使能,可修改passkey -//重设passkey回调函数,在这里可以重新设置passkey -//passkey为6个数字组成,十万位、万位。。。。个位 各表示一个数字 高位不够为0 -static void reset_passkey_cb(u32 *key) -{ -#if 1 - u32 newkey = rand32();//获取随机数 - - newkey &= 0xfffff; - if (newkey > 999999) { - newkey = newkey - 999999; //不能大于999999 - } - *key = newkey; //小于或等于六位数 - log_info("set new_key= %06u\n", *key); -#else - *key = 123456; //for debug -#endif -} - -#if 0 -//协议栈内部调用 -extern void sm_set_master_request_pair(int enable); -static void ble_sm_setup_init(io_capability_t io_type, u8 auth_req, uint8_t min_key_size, u8 security_en) -{ - //setup SM: Display only - sm_init(); - sm_set_io_capabilities(io_type); - sm_set_authentication_requirements(auth_req); - sm_set_encryption_key_size_range(min_key_size, 16); - sm_set_master_request_pair(security_en); - sm_event_callback_set(&cbk_sm_packet_handler); - - if (io_type == IO_CAPABILITY_DISPLAY_ONLY) { - reset_PK_cb_register(reset_passkey_cb); - } -} -#endif - - -//协议栈内部调用 -void ble_profile_init(void) -{ - log_info("ble profile init\n"); - le_device_db_init(); - ble_stack_gatt_role(1); - -//#if PASSKEY_ENTER_ENABLE -// ble_sm_setup_init(IO_CAPABILITY_DISPLAY_ONLY, SM_AUTHREQ_MITM_PROTECTION | SM_AUTHREQ_BONDING, 7, client_config->security_en); -//#else -// ble_sm_setup_init(IO_CAPABILITY_NO_INPUT_NO_OUTPUT, SM_AUTHREQ_MITM_PROTECTION | SM_AUTHREQ_BONDING, 7, client_config->security_en); -//#endif - - /* setup ATT client */ - gatt_client_init(); - gatt_client_register_packet_handler(cbk_packet_handler); - - // register for HCI events - hci_event_callback_set(&cbk_packet_handler); - le_l2cap_register_packet_handler(&cbk_packet_handler); - - ble_vendor_set_default_att_mtu(ATT_LOCAL_MTU_SIZE); -} - - -static void device_bonding_init(void) -{ - int i; - int cfg_bonding = 0; - client_match_cfg_t *cfg; - for (i = 0; i < CLIENT_MATCH_CONN_MAX; i++) { - cfg = client_config->match_dev_cfg[i]; - if (cfg == NULL) { - continue; - } - if (cfg->bonding_flag) { - cfg_bonding = 1; - } - } - if (!cfg_bonding) { - clear_bonding_info(); - } -} - -static u8 client_idle_query(void) -{ - return 0; -} - -REGISTER_LP_TARGET(client_user_target) = { - .name = "client_user_demo", - .is_idle = client_idle_query, -}; - - - - -void bt_ble_init(void) -{ - log_info("***** ble_init******\n"); -#if WIRELESS_TOOL_BLE_NAME_EN - u8 *config_name; - u8 config_name_len; -#endif - -#if (WIRELESS_24G_ENABLE) - rf_set_24g_hackable_coded(WIRELESS_24G_CODE_ID); -#endif -#if WIRELESS_TOOL_BLE_NAME_EN -#if 1 //使用配置工具的蓝牙名 - extern const char *bt_get_local_name(); - config_name = (u8 *)(bt_get_local_name()); - config_name_len = strlen(config_name) + 1; - put_buf(config_name, config_name_len); - printf("len = %d", config_name_len); -#else - config_name = TEST_BOX_BLE_NAME; - config_name_len = sizeof(TEST_BOX_BLE_NAME); -#endif - match_name = (u8 *)zalloc(config_name_len); - memcpy(match_name, config_name, config_name_len); - printf("match_name"); - put_buf(match_name, config_name_len); - printf("%s", match_name); -#endif - // if (0 == memcmp(match_name, SPECIFIC_STRING, strlen(SPECIFIC_STRING))) { - // match_name = &match_name[strlen(SPECIFIC_STRING)]; - // printf("specific scan%s", match_name); - // flag_specific_sacn = 1; - - // } - - set_ble_work_state(BLE_ST_INIT_OK); - conn_pair_vm_do(&conn_pair_info, 0); - -#if !WIRELESS_PAIR_BONDING - device_bonding_init(); -#endif - ble_module_enable(1); - extern void wifi_detect_set_master_first(u8 first); -#if TCFG_WIFI_DETECT_ENABLE -#if TCFG_WIFI_DETCET_PRIOR - wifi_detect_set_master_first(0); -#else - wifi_detect_set_master_first(1); -#endif -#endif - -#if (SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE) - client_timer_start(); -#endif /* SHOW_RX_DATA_RATE | SHOW_TX_DATA_RATE */ -} - -void bt_ble_exit(void) -{ - log_info("***** ble_exit******\n"); -} - -void ble_app_disconnect(void) -{ - ble_disconnect(NULL); -} -//统一接口,关闭模块 -void bt_ble_adv_enable(u8 enable) -{ - ble_module_enable(enable); -} - -//模块开关 -void ble_module_enable(u8 en) -{ - log_info("mode_en:%d\n", en); - if (en) { - scan_ctrl_en = 1; - bt_ble_scan_enable(0, 1); - } else { - if (con_handle) { - scan_ctrl_en = 0; - ble_disconnect(NULL); - } else { - bt_ble_scan_enable(0, 0); - scan_ctrl_en = 0; - } - } - -} - -void client_send_conn_param_update(void) -{ - struct conn_update_param_t *param = (void *)&connection_param_table[send_param_index];//for test - log_info("client update param:-%d-%d-%d-%d-\n", param->interval_min, param->interval_max, param->latency, param->timeout); - if (con_handle) { - ble_op_conn_param_update(con_handle, param); - } -} - - - -//---------------------------------------------------------------------------------- -//lmx -void rcsp_adv_fill_mac_addr(u8 *mac_addr_buf) -{ -#if (MUTIl_CHARGING_BOX_EN) - u8 *mac_addr = get_chargebox_adv_addr(); - if (mac_addr) { - swapX(mac_addr, mac_addr_buf, 6); - } - - /* printf("mac_addr:"); */ - /* printf_buf(mac_addr_buf, 6); */ - -#else - swapX(bt_get_mac_addr(), mac_addr_buf, 6); -#endif -} - - #endif +#endif /* (TCFG_BLE_DEMO_SELECT == DEF_BLE_DEMO_WIRELESS_MIC_CLIENT) */ diff --git a/apps/earphone/xtell_Sensor/example/example.c b/apps/earphone/remote_control/ble_handler/example/example.c similarity index 100% rename from apps/earphone/xtell_Sensor/example/example.c rename to apps/earphone/remote_control/ble_handler/example/example.c diff --git a/apps/earphone/xtell_Sensor/xtell.h b/apps/earphone/remote_control/ble_handler/xtell.h similarity index 95% rename from apps/earphone/xtell_Sensor/xtell.h rename to apps/earphone/remote_control/ble_handler/xtell.h index dc77190..891f301 100644 --- a/apps/earphone/xtell_Sensor/xtell.h +++ b/apps/earphone/remote_control/ble_handler/xtell.h @@ -7,4 +7,6 @@ #define ACC_RANGE 16 //g,加速度满量程:2、4、8、16 + + #endif \ No newline at end of file diff --git a/apps/earphone/xtell_Sensor/xtell_app_main.c b/apps/earphone/remote_control/ble_handler/xtell_app_main.c similarity index 100% rename from apps/earphone/xtell_Sensor/xtell_app_main.c rename to apps/earphone/remote_control/ble_handler/xtell_app_main.c diff --git a/apps/earphone/xtell_Sensor/xtell_handler.c b/apps/earphone/remote_control/ble_handler/xtell_handler.c similarity index 99% rename from apps/earphone/xtell_Sensor/xtell_handler.c rename to apps/earphone/remote_control/ble_handler/xtell_handler.c index 073a47c..8112d86 100644 --- a/apps/earphone/xtell_Sensor/xtell_handler.c +++ b/apps/earphone/remote_control/ble_handler/xtell_handler.c @@ -302,7 +302,7 @@ static int bt_connction_status_event_handler(struct bt_event *bt) ble_bqb_test_thread_init(); } else { #if !TCFG_WIRELESS_MIC_ENABLE - // bt_ble_init(); + bt_ble_init(); // lmx,初始化完,初始化ble,决定ble是作为主设备还是从设备 // xtell_ble_central_test_start(); //xtell #endif } diff --git a/cpu/br28/tools/app.bin b/cpu/br28/tools/app.bin index 1a58efc693c0f9a0bce6212f7eaa8ca7891c35e7..f636652edf83e75800393edf3465395394ac49ed 100644 GIT binary patch delta 127153 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