/* * SPDX-FileCopyrightText: 2021-2021 SiFli Technologies(Nanjing) Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "bf0_ble_gap.h" #include "bf0_sibles.h" #include "bf0_sibles_advertising.h" #include "ble_connection_manager.h" #define LOG_TAG "ble_app" #include "log.h" // #include "button.h" // CONFIG_USING_BUTTON_LIB=y #include "littlevgl2rtt.h" #include "lv_ex_data.h" #include "gui_guider.h" #include "events_init.h" // 添加硬件故障处理相关头文件 #include #include // 添加更详细的日志宏 #define BLE_DEBUG_LOG #ifdef BLE_DEBUG_LOG #define BLE_LOG_D(...) LOG_D(__VA_ARGS__) #define BLE_LOG_I(...) LOG_I(__VA_ARGS__) #define BLE_LOG_W(...) LOG_W(__VA_ARGS__) #define BLE_LOG_E(...) LOG_E(__VA_ARGS__) #endif lv_ui guider_ui; /* Choose one HID type. */ #define HID_KEYBOARD // #define HID_CONSUMER #define HIDS_TEST /* 24 * 1.25 = 30ms */ #define BLE_APP_HIGH_PERFORMANCE_INTERVAL (24) #define BLE_APP_TIMEOUT_INTERVAL (5000) typedef struct { uint8_t key_code; // Physical keycode uint8_t hid_code; // HID keycode } key_mapping_t; key_mapping_t key_mapping_table[4] = { {0x01, 0x1A}, // HID keycode for 'W' {0x02, 0x04}, // HID keycode for 'A' {0x03, 0x16}, // HID keycode for 'S' {0x04, 0x07} // HID keycode for 'D' }; typedef struct { uint8_t is_power_on; uint8_t conn_idx; uint8_t is_bg_adv_on; struct { bd_addr_t peer_addr; uint16_t mtu; uint16_t conn_interval; uint8_t peer_addr_type; } conn_para; struct { sibles_hdl srv_handle; uint8_t is_config_on; } data; rt_mailbox_t mb_handle; } app_env_t; static app_env_t g_app_env; static rt_mailbox_t g_app_mb; static app_env_t *ble_app_get_env(void); /**********************Start of HID service ****************************************************/ #define BASE_USB_HID_SPEC_VERSION 0x0101 /* Buttons configuration */ /* Note: The configuration below is the same as BOOT mode configuration * This simplifies the code as the BOOT mode is the same as REPORT mode. * Changing this configuration would require separate implementation of * BOOT mode report generation. */ enum { HIDS_REMOTE_WAKE = 1, HIDS_NORMALLY_CONNECTABLE = 2, }; struct hids_info { uint16_t version; /* version number of base USB HID Specification */ uint8_t code; /* country HID Device hardware is localized for. */ uint8_t flags; } __packed; struct hids_report { uint8_t id; /* report id */ uint8_t type; /* report type */ } __packed; static struct hids_info info = { .version = BASE_USB_HID_SPEC_VERSION, .code = 0x00, .flags = HIDS_NORMALLY_CONNECTABLE, }; enum { HIDS_INPUT = 0x01, HIDS_OUTPUT = 0x02, HIDS_FEATURE = 0x03, }; static struct hids_report input = { .id = 0x0, .type = HIDS_INPUT, }; static uint8_t ctrl_point; /* Number of bytes in key report * * 1B - control keys * 1B - reserved * rest - non-control keys */ #define KEY_CTRL_CODE_MIN 224 /* Control key codes - required 8 of them */ #define KEY_CTRL_CODE_MAX 231 /* Control key codes - required 8 of them */ #define KEY_CODE_MIN 0 /* Normal key codes */ #define KEY_CODE_MAX 101 /* Normal key codes */ #define KEY_PRESS_MAX 6 /* Maximum number of non-control keys pressed simultaneously*/ #define INPUT_REPORT_KEYS_MAX_LEN (1 + 1 + KEY_PRESS_MAX) struct keyboard_state { uint8_t ctrl_keys_state; /* Current keys state */ uint8_t reserved; uint8_t keys_state[KEY_PRESS_MAX]; } hid_keyboard_state; static const uint8_t report_map[] = { 0x05, 0x01, /* Usage Page (Generic Desktop) */ 0x09, 0x06, /* Usage (Keyboard) */ 0xA1, 0x01, /* Collection (Application) */ /* Keys */ 0x05, 0x07, /* Usage Page (Key Codes) */ 0x19, 0xe0, /* Usage Minimum (224) */ 0x29, 0xe7, /* Usage Maximum (231) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x01, /* Logical Maximum (1) */ 0x75, 0x01, /* Report Size (1) */ 0x95, 0x08, /* Report Count (8) */ 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 0x95, 0x01, /* Report Count (1) */ 0x75, 0x08, /* Report Size (8) */ 0x81, 0x01, /* Input (Constant) reserved byte(1) */ 0x95, 0x06, /* Report Count (6) */ 0x75, 0x08, /* Report Size (8) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x65, /* Logical Maximum (101) */ 0x05, 0x07, /* Usage Page (Key codes) */ 0x19, 0x00, /* Usage Minimum (0) */ 0x29, 0x65, /* Usage Maximum (101) */ 0x81, 0x00, /* Input (Data, Array) Key array(6 bytes) */ /* LED */ 0x95, 0x05, /* Report Count (5) */ 0x75, 0x01, /* Report Size (1) */ 0x05, 0x08, /* Usage Page (Page# for LEDs) */ 0x19, 0x01, /* Usage Minimum (1) */ 0x29, 0x05, /* Usage Maximum (5) */ 0x91, 0x02, /* Output (Data, Variable, Absolute), */ /* Led report */ 0x95, 0x01, /* Report Count (1) */ 0x75, 0x03, /* Report Size (3) */ 0x91, 0x01, /* Output (Data, Variable, Absolute), */ /* Led report padding */ 0xC0 /* End Collection (Application) */ }; /// HID Service Attributes Indexes enum { HIDS_IDX_SVC, HIDS_IDX_INFO_CHAR, HIDS_IDX_INFO_VAL, HIDS_IDX_REPORT_MAP, HIDS_IDX_REPORT_MAP_VAL, HIDS_IDX_REPORT, HIDS_IDX_REPORT_VAL, HIDS_IDX_REPORT_REF, HIDS_IDX_REPORT_NTF_CFG, HIDS_IDX_CTRL, HIDS_IDX_CTRL_VAL, HDIS_ATT_NB }; struct attm_desc hids_att_db[] = { // HID service [HIDS_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0}, // HID Info [HIDS_IDX_INFO_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, [HIDS_IDX_INFO_VAL] = {ATT_CHAR_HID_INFO, PERM(RD, ENABLE), PERM(RI, ENABLE), sizeof(info)}, // HID Report map [HIDS_IDX_REPORT_MAP] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, [HIDS_IDX_REPORT_MAP_VAL] = {ATT_CHAR_REPORT_MAP, PERM(RD, ENABLE), PERM(RI, ENABLE), sizeof(report_map)}, // HID Report [HIDS_IDX_REPORT] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, [HIDS_IDX_REPORT_VAL] = { ATT_CHAR_REPORT, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE) | PERM(NTF, ENABLE) | PERM(WP, UNAUTH), PERM(UUID_LEN, UUID_16) | PERM(RI, ENABLE), 8 }, [HIDS_IDX_REPORT_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE) | PERM(WP, UNAUTH), PERM(RI, ENABLE), 2}, [HIDS_IDX_REPORT_REF] = {ATT_DESC_REPORT_REF, PERM(RD, ENABLE), PERM(RI, ENABLE), 2}, // HID Control [HIDS_IDX_CTRL] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0}, [HIDS_IDX_CTRL_VAL] = {ATT_CHAR_HID_CTNL_PT, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 1}, }; uint8_t *ble_hids_gatts_get_cbk(uint8_t conn_idx, uint8_t idx, uint16_t *len) { uint8_t *ret_val = NULL; *len = 0; LOG_I("HIDS get: idx=%d\n", idx); switch (idx) { // HID Info case HIDS_IDX_INFO_VAL: ret_val = (uint8_t *)&info; *len = sizeof(info); break; case HIDS_IDX_REPORT_MAP_VAL: ret_val = (uint8_t *)report_map; *len = sizeof(report_map); break; case HIDS_IDX_REPORT_VAL: break; case HIDS_IDX_CTRL_VAL: { ret_val = (uint8_t *)&ctrl_point; *len = sizeof(ctrl_point); LOG_I("HIDS %s", ctrl_point ? "Exit Suspend" : "Suspend"); break; } case HIDS_IDX_REPORT_REF: { ret_val = (uint8_t *)&input; *len = sizeof(input); } default: break; } return ret_val; } uint8_t ble_hids_gatts_set_cbk(uint8_t conn_idx, sibles_set_cbk_t *para) { app_env_t *env = ble_app_get_env(); LOG_I("HIDS get: idx=%d\n", para->idx); switch (para->idx) { case HIDS_IDX_REPORT_NTF_CFG: env->data.is_config_on = *(para->value); LOG_I("CCCD %d", env->data.is_config_on); break; ; case HIDS_IDX_CTRL_VAL: RT_ASSERT(para->len <= sizeof(ctrl_point)); memcpy(&ctrl_point, para->value, para->len); LOG_I("Updated app value to:%x", ctrl_point); break; default: break; } return 0; } static void ble_app_service_init(void) { app_env_t *env = ble_app_get_env(); sibles_register_svc_t svc; svc.att_db = (struct attm_desc *)&hids_att_db; svc.num_entry = HDIS_ATT_NB; /* Service security level to control all characteristic. */ svc.sec_lvl = PERM(SVC_AUTH, NO_AUTH) | PERM(SVC_UUID_LEN, UUID_16); svc.uuid = ATT_SVC_HID; /* Reigster GATT service and related callback for next response. */ env->data.srv_handle = sibles_register_svc(&svc); if (env->data.srv_handle) { LOG_I("Register HID service handle\n"); sibles_register_cbk(env->data.srv_handle, ble_hids_gatts_get_cbk, ble_hids_gatts_set_cbk); } else { LOG_E("Connection failed\n"); } } /*********************** End of HIDS***********************************************/ /********************** Start of HID Application *********************************/ static app_env_t *ble_app_get_env(void) { return &g_app_env; } /** @brief Change key code to ctrl code mask * * Function changes the key code to the mask in the control code * field inside the raport. * Returns 0 if key code is not a control key. * * @param key Key code * * @return Mask of the control key or 0. */ static uint8_t button_ctrl_code(uint8_t key) { if (KEY_CTRL_CODE_MIN <= key && key <= KEY_CTRL_CODE_MAX) { return (uint8_t)(1U << (key - KEY_CTRL_CODE_MIN)); } return 0; } int hid_kbd_state_key_set(uint8_t key) { uint8_t ctrl_mask = button_ctrl_code(key); if (ctrl_mask) { hid_keyboard_state.ctrl_keys_state |= ctrl_mask; return 0; } for (size_t i = 0; i < KEY_CTRL_CODE_MAX; ++i) { if (hid_keyboard_state.keys_state[i] == 0) { hid_keyboard_state.keys_state[i] = key; return 0; } } /* All slots busy */ return -EBUSY; } int hid_kbd_state_key_clear(uint8_t key) { uint8_t ctrl_mask = button_ctrl_code(key); if (ctrl_mask) { hid_keyboard_state.ctrl_keys_state &= ~ctrl_mask; return 0; } for (size_t i = 0; i < KEY_CTRL_CODE_MAX; ++i) { if (hid_keyboard_state.keys_state[i] == key) { hid_keyboard_state.keys_state[i] = 0; return 0; } } /* Key not found */ return -EINVAL; } void key_report_send(uint8_t *key_val, uint16_t key_val_len) { app_env_t *env = ble_app_get_env(); if (env->data.is_config_on) { sibles_value_t value; value.hdl = env->data.srv_handle; value.idx = HIDS_IDX_REPORT_VAL; value.len = key_val_len; value.value = key_val; sibles_write_value(env->conn_idx, &value); LOG_I("key_report_send: sibles_write_value called"); } } /********************** End of HID Application *********************************/ SIBLES_ADVERTISING_CONTEXT_DECLAR(g_app_advertising_bg_context); static uint8_t ble_app_background_advertising_event(uint8_t event, void *context, void *data) { switch (event) { case SIBLES_ADV_EVT_ADV_STARTED: { sibles_adv_evt_startted_t *evt = (sibles_adv_evt_startted_t *)data; LOG_I("Broadcast ADV start resutl %d, mode %d\r\n", evt->status, evt->adv_mode); break; } case SIBLES_ADV_EVT_ADV_STOPPED: { sibles_adv_evt_stopped_t *evt = (sibles_adv_evt_stopped_t *)data; LOG_I("Broadcast ADV stopped reason %d, mode %d\r\n", evt->reason, evt->adv_mode); break; } default: break; } return 0; } /* Enable advertise via advertising service. */ static void ble_app_bg_advertising_start(void) { sibles_advertising_para_t para = {0}; uint8_t ret; char local_name[] = "SIFLI_BG_INFO"; uint8_t manu_additnal_data[] = {0x20, 0xC4, 0x00, 0x91}; uint16_t manu_company_id = 0x01; para.own_addr_type = GAPM_GEN_NON_RSLV_ADDR; para.config.adv_mode = SIBLES_ADV_BROADCAST_MODE; /* Keep advertising till disable it or connected. */ para.config.mode_config.broadcast_config.duration = 0x0; para.config.mode_config.broadcast_config.interval = 0x140; para.config.max_tx_pwr = 0x7F; /* Advertising will re-start after disconnected. */ para.config.is_auto_restart = 1; /* Scan rsp data is same as advertising data. */ para.config.is_rsp_data_duplicate = 1; /* Prepare name filed .*/ para.adv_data.completed_name = rt_malloc(rt_strlen(local_name) + sizeof(sibles_adv_type_name_t)); para.adv_data.completed_name->name_len = rt_strlen(local_name); rt_memcpy(para.adv_data.completed_name->name, local_name, para.adv_data.completed_name->name_len); /* Prepare manufacturer filed .*/ para.adv_data.manufacturer_data = rt_malloc(sizeof(sibles_adv_type_manufacturer_data_t) + sizeof(manu_additnal_data)); para.adv_data.manufacturer_data->company_id = manu_company_id; para.adv_data.manufacturer_data->data_len = sizeof(manu_additnal_data); rt_memcpy(para.adv_data.manufacturer_data->additional_data, manu_additnal_data, sizeof(manu_additnal_data)); para.evt_handler = ble_app_background_advertising_event; ret = sibles_advertising_init(g_app_advertising_bg_context, ¶); if (ret == SIBLES_ADV_NO_ERR) { sibles_advertising_start(g_app_advertising_bg_context); } rt_free(para.adv_data.completed_name); rt_free(para.adv_data.manufacturer_data); } SIBLES_ADVERTISING_CONTEXT_DECLAR(g_app_advertising_context); static uint8_t ble_app_advertising_event(uint8_t event, void *context, void *data) { app_env_t *env = ble_app_get_env(); switch (event) { case SIBLES_ADV_EVT_ADV_STARTED: { sibles_adv_evt_startted_t *evt = (sibles_adv_evt_startted_t *)data; if (!env->is_bg_adv_on) { env->is_bg_adv_on = 1; ble_app_bg_advertising_start(); } LOG_I("ADV start resutl %d, mode %d\r\n", evt->status, evt->adv_mode); break; } case SIBLES_ADV_EVT_ADV_STOPPED: { sibles_adv_evt_stopped_t *evt = (sibles_adv_evt_stopped_t *)data; LOG_I("ADV stopped reason %d, mode %d\r\n", evt->reason, evt->adv_mode); break; } default: break; } return 0; } #define DEFAULT_LOCAL_NAME "SIFLI_APP" /* Enable advertise via advertising service. */ static void ble_app_advertising_start(void) { sibles_advertising_para_t para = {0}; uint8_t ret; char local_name[31] = {0}; uint8_t manu_additnal_data[] = {0x20, 0xC4, 0x00, 0x91}; uint16_t manu_company_id = 0x01; bd_addr_t addr; ret = ble_get_public_address(&addr); if (ret == HL_ERR_NO_ERROR) rt_snprintf(local_name, 31, "SIFLI_APP-%x-%x-%x-%x-%x-%x", addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3], addr.addr[4], addr.addr[5]); else memcpy(local_name, DEFAULT_LOCAL_NAME, sizeof(DEFAULT_LOCAL_NAME)); ble_gap_dev_name_t *dev_name = malloc(sizeof(ble_gap_dev_name_t) + strlen(local_name)); dev_name->len = strlen(local_name); memcpy(dev_name->name, local_name, dev_name->len); ble_gap_set_dev_name(dev_name); free(dev_name); para.own_addr_type = GAPM_STATIC_ADDR; para.config.adv_mode = SIBLES_ADV_CONNECT_MODE; /* Keep advertising till disable it or connected. */ para.config.mode_config.conn_config.duration = 0x0; para.config.mode_config.conn_config.interval = 0x30; para.config.max_tx_pwr = 0x7F; /* Advertising will re-start after disconnected. */ para.config.is_auto_restart = 1; /* Scan rsp data is same as advertising data. */ //para.config.is_rsp_data_duplicate = 1; /* Prepare name filed. Due to name is too long to put adv data, put it to rsp data.*/ para.rsp_data.completed_name = rt_malloc(rt_strlen(local_name) + sizeof(sibles_adv_type_name_t)); para.rsp_data.completed_name->name_len = rt_strlen(local_name); rt_memcpy(para.rsp_data.completed_name->name, local_name, para.rsp_data.completed_name->name_len); /* Prepare service data filed .*/ { uint16_t uuid_hids = ATT_SVC_HID; para.adv_data.completed_uuid = rt_malloc(sizeof(sibles_adv_type_srv_uuid_t) + 1 + sizeof(ATT_UUID_16_LEN)); para.adv_data.completed_uuid->count = 1; para.adv_data.completed_uuid->uuid_list[0].uuid_len = 2; memcpy(para.adv_data.completed_uuid->uuid_list[0].uuid.uuid_16, &uuid_hids, para.adv_data.completed_uuid->uuid_list[0].uuid_len); } /* Prepare appearance filed .*/ para.adv_data.appearance = rt_malloc(2); #ifdef HID_KEYBOARD // Keyboard (HID subtype) uint16_t appearance = 961; #endif memcpy(para.adv_data.appearance, &appearance, 2); // set ad type flag para.adv_data.disc_mode = GAPM_ADV_MODE_GEN_DISC; /* Prepare manufacturer filed .*/ para.adv_data.manufacturer_data = rt_malloc(sizeof(sibles_adv_type_manufacturer_data_t) + sizeof(manu_additnal_data)); para.adv_data.manufacturer_data->company_id = manu_company_id; para.adv_data.manufacturer_data->data_len = sizeof(manu_additnal_data); rt_memcpy(para.adv_data.manufacturer_data->additional_data, manu_additnal_data, sizeof(manu_additnal_data)); para.evt_handler = ble_app_advertising_event; ret = sibles_advertising_init(g_app_advertising_context, ¶); if (ret == SIBLES_ADV_NO_ERR) { sibles_advertising_start(g_app_advertising_context); } rt_free(para.rsp_data.appearance); rt_free(para.rsp_data.completed_name); rt_free(para.adv_data.manufacturer_data); } #ifdef HIDS_TEST #ifdef HID_KEYBOARD #define HID_KEY_SET(key) hid_kbd_state_key_set(key) #define HID_KEY_CLEAR(key) hid_kbd_state_key_clear(key) #define HID_KEY_SEND() key_report_send((uint8_t *)&hid_keyboard_state, sizeof(hid_keyboard_state)) #endif #endif // #ifdef HID_KEYBOARD // static void key_button_handler(int32_t pin, button_action_t action) // { // uint8_t hid_code = key_mapping_table[0].hid_code; // switch (action) // { // case BUTTON_PRESSED: // HID_KEY_SET(hid_code); // HID_KEY_SEND(); // break; // case BUTTON_RELEASED: // HID_KEY_CLEAR(hid_code); // HID_KEY_SEND(); // break; // default: // break; // } // } // #endif // int main(void) // { // int count = 0; // app_env_t *env = ble_app_get_env(); // env->mb_handle = rt_mb_create("app", 8, RT_IPC_FLAG_FIFO); // sifli_ble_enable(); // while (1) // { // uint32_t value; // int ret; // rt_mb_recv(env->mb_handle, (rt_uint32_t *)&value, RT_WAITING_FOREVER); // if (value == BLE_POWER_ON_IND) // { // env->is_power_on = 1; // env->conn_para.mtu = 23; /* Default value. */ // ble_app_service_init(); // /* First enable connectable adv then enable non-connectable. */ // ble_app_advertising_start(); // LOG_I("receive BLE power on!\r\n"); // } // } // return RT_EOK; // } void lvgl_task(void *arg) { rt_err_t ret = RT_EOK; rt_uint32_t ms; /* init littlevGL */ ret = littlevgl2rtt_init("lcd"); if (ret != RT_EOK) { //return ret; LOG_E("Failed to initialize littlevGL2RTT.\n"); } lv_ex_data_pool_init(); setup_ui(&guider_ui); events_init(&guider_ui); while (1) { ms = lv_task_handler(); rt_thread_mdelay(ms); } } void ble_task(void *p) { app_env_t *env = ble_app_get_env(); env->mb_handle = rt_mb_create("app", 8, RT_IPC_FLAG_FIFO); sifli_ble_enable(); rt_thread_mdelay(1000); uint32_t value; int ret; while (1) { ret = rt_mb_recv(env->mb_handle, (rt_uint32_t *)&value, RT_TICK_PER_SECOND * 2);//RT_WAITING_FOREVER if(ret == RT_EOK) { if (value == BLE_POWER_ON_IND) { env->is_power_on = 1; env->conn_para.mtu = 23; /* Default value. */ ble_app_service_init(); /* First enable connectable adv then enable non-connectable. */ ble_app_advertising_start(); LOG_I("receive BLE power on!\r\n"); } else { LOG_E("OFF OFF OFF OFF off!\r\n"); LOG_E("OFF OFF OFF OFF off!\r\n"); LOG_E("OFF OFF OFF OFF off!\r\n"); } } } } int main(void) { rt_thread_t lvgl_thread = rt_thread_create("lvgl_thread", lvgl_task, NULL, 1024 * 4, RT_THREAD_PRIORITY_HIGH - 1, RT_THREAD_TICK_DEFAULT); rt_err_t lv_ret = rt_thread_startup(lvgl_thread); if (lv_ret != RT_EOK) { LOG_E("Failed to start LVGL thread: %d\n", lv_ret); return -1; } rt_thread_t ble_thread = rt_thread_create("ble_thread", ble_task, NULL, 1024 * 3, RT_THREAD_PRIORITY_HIGH, RT_THREAD_TICK_DEFAULT); rt_err_t ble_ret = rt_thread_startup(ble_thread); if (ble_ret != RT_EOK) { LOG_E("Failed to start BLE thread: %d\n", ble_ret); return -1; } while (1) { rt_thread_mdelay(10000); } return 0; } static void ble_app_update_conn_param(uint8_t conn_idx, uint16_t inv_max, uint16_t inv_min, uint16_t timeout) { ble_gap_update_conn_param_t conn_para; conn_para.conn_idx = conn_idx; conn_para.intv_max = inv_max; conn_para.intv_min = inv_min; /* value = argv * 1.25 */ conn_para.ce_len_max = 0x100; conn_para.ce_len_min = 0x1; conn_para.latency = 0; conn_para.time_out = timeout; ble_gap_update_conn_param(&conn_para); } int ble_app_event_handler(uint16_t event_id, uint8_t *data, uint16_t len, uint32_t context) { app_env_t *env = ble_app_get_env(); // 添加环境检查 if (env == NULL) { BLE_LOG_E("BLE environment is NULL"); return -1; } BLE_LOG_D("BLE event received: 0x%04X, len: %d", event_id, len); switch (event_id) { case BLE_POWER_ON_IND: { LOG_I("BLE_POWER_ON_IND received, sending to mailbox...\r\n"); /* Handle in own thread to avoid conflict */ if (env->mb_handle) { rt_mb_send(env->mb_handle, BLE_POWER_ON_IND); LOG_I("BLE_POWER_ON_IND sent to mailbox\r\n"); } else { LOG_E("Mailbox not created yet!\r\n"); } break; } case BLE_GAP_CONNECTED_IND: { // 添加数据有效性检查 if (data == NULL || len < sizeof(ble_gap_connect_ind_t)) { BLE_LOG_E("Invalid data for BLE_GAP_CONNECTED_IND"); break; } ble_gap_connect_ind_t *ind = (ble_gap_connect_ind_t *)data; env->conn_idx = ind->conn_idx; env->conn_para.conn_interval = ind->con_interval; env->conn_para.peer_addr_type = ind->peer_addr_type; env->conn_para.peer_addr = ind->peer_addr; if (ind->role == 0) LOG_E("Peripheral should be slave!!!"); LOG_I("Peer device(%x-%x-%x-%x-%x-%x) connected", env->conn_para.peer_addr.addr[5], env->conn_para.peer_addr.addr[4], env->conn_para.peer_addr.addr[3], env->conn_para.peer_addr.addr[2], env->conn_para.peer_addr.addr[1], env->conn_para.peer_addr.addr[0]); break; } case BLE_GAP_UPDATE_CONN_PARAM_IND: { // 添加数据有效性检查 if (data == NULL || len < sizeof(ble_gap_update_conn_param_ind_t)) { BLE_LOG_E("Invalid data for BLE_GAP_UPDATE_CONN_PARAM_IND"); break; } ble_gap_update_conn_param_ind_t *ind = (ble_gap_update_conn_param_ind_t *)data; env->conn_para.conn_interval = ind->con_interval; LOG_I("Updated connection interval :%d", ind->con_interval); break; } case SIBLES_MTU_EXCHANGE_IND: { // 添加数据有效性检查 if (data == NULL || len < sizeof(sibles_mtu_exchange_ind_t)) { BLE_LOG_E("Invalid data for SIBLES_MTU_EXCHANGE_IND"); break; } /* Negotiated MTU. */ sibles_mtu_exchange_ind_t *ind = (sibles_mtu_exchange_ind_t *)data; env->conn_para.mtu = ind->mtu; LOG_I("Exchanged MTU size: %d", ind->mtu); break; } case BLE_GAP_DISCONNECTED_IND: { // 添加数据有效性检查 if (data == NULL || len < sizeof(ble_gap_disconnected_ind_t)) { BLE_LOG_E("Invalid data for BLE_GAP_DISCONNECTED_IND"); break; } ble_gap_disconnected_ind_t *ind = (ble_gap_disconnected_ind_t *)data; LOG_I("BLE_GAP_DISCONNECTED_IND(%d)", ind->reason); // 添加重连逻辑 if (env->is_power_on) { BLE_LOG_I("Restarting advertising after disconnection"); ble_app_advertising_start(); } break; } case 0x0060: { BLE_LOG_I("BLE security event received (0x0060), data len: %d", len); // 添加数据有效性检查 if (data == NULL || len == 0) { BLE_LOG_W("Invalid data for event 0x0060"); break; } // 根据实际SDK文档处理此事件 // 这里只是一个示例,需要根据实际SDK文档调整 BLE_LOG_D("Processing security request with data: %02X %02X %02X %02X", data[0], len > 1 ? data[1] : 0, len > 2 ? data[2] : 0, len > 3 ? data[3] : 0); break; } default: LOG_I("Unhandled BLE event: 0x%04X\r\n", event_id); break; } return 0; } BLE_EVENT_REGISTER(ble_app_event_handler, NULL); #ifdef HIDS_TEST static rt_err_t test_hids(int argc, char **argv) { if (argc < 3) { rt_kprintf("usage: test_hids key [p|r] \n"); } else { uint8_t key = (uint8_t)atoi(&argv[1][0]); if (argv[2][0] == 'p') HID_KEY_SET(key); else HID_KEY_CLEAR(key); HID_KEY_SEND(); } return 0; } FINSH_FUNCTION_EXPORT(test_hids, Test HIDS); MSH_CMD_EXPORT(test_hids, Test HIDS); #endif