diff options
author | gornekich <n.gorbadey@gmail.com> | 2021-12-08 14:28:01 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-08 14:28:01 +0300 |
commit | 7170864fe41f783ea84892f2e326ab120e2ee446 (patch) | |
tree | 52b6088d427d7216db80e520e38dd052e0ebce1a /applications/bt | |
parent | bb96509ed1e92c12b0b622f105eaa813d32009b4 (diff) |
[FL-1976] BLE HID (#852)
* ble: prototype ble hid
* ble: add HID service and characteristics
* debug tools: add ble keyboard app
* ble: change appearance
* ble: working keyboard
* bt: introduce furi-hal-bt-hid
* bt: restart hid service on each keyboard app enter
* bt: introduce switch profile
* bt: add profile to ble glue
* bt: working profile switch
* bt: introduce bt serial profile, rework API
* bt: rewotk HID profile
* bt: rework gap with profile configuration
* bt: move change profile routine to furi hal bt
* bt: change switch profile API to blocking
* bt: move battery update to furi hal bt
* bt: cleanup
* bt: add support for f6 target
* bt: update documentation
* bt: clean up code
* bt: remove NO OUTPUT setting
* bt: set numeric comparison pairing in BLE HID
* bt: support f6 target
* bt: set mac address in profile configuration
* bt: set advertise name in profile config
* bt: rework with furi thread
* bt: support f6 target
* bt: clear hci command buffer on core2 restart
* bt: correct thread kill sequence
* bt: fix freertos functions calls
* bt: add some enterprise delays fo correct memory free
* bt: code cleanup
* bt: change terminate -> stop
* bt: fix memory leakage
Co-authored-by: あく <alleteam@gmail.com>
Diffstat (limited to 'applications/bt')
-rwxr-xr-x | applications/bt/bt_service/bt.c | 153 | ||||
-rw-r--r-- | applications/bt/bt_service/bt.h | 16 | ||||
-rwxr-xr-x | applications/bt/bt_service/bt_api.c | 15 | ||||
-rw-r--r-- | applications/bt/bt_service/bt_i.h | 7 |
4 files changed, 140 insertions, 51 deletions
diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index bcf37fa0..074ab980 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -39,6 +39,19 @@ static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) { string_clear(pin_str); } +static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { + furi_assert(bt); + string_t pin_str; + dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); + string_init_printf(pin_str, "Verify code\n%06d", pin); + dialog_message_set_text( + bt->dialog_message, string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop); + dialog_message_set_buttons(bt->dialog_message, "Cancel", "Ok", NULL); + DialogMessageButton button = dialog_message_show(bt->dialogs, bt->dialog_message); + string_clear(pin_str); + return button == DialogMessageButtonCenter; +} + static void bt_battery_level_changed_callback(const void* _event, void* context) { furi_assert(_event); furi_assert(context); @@ -56,7 +69,8 @@ static void bt_battery_level_changed_callback(const void* _event, void* context) Bt* bt_alloc() { Bt* bt = furi_alloc(sizeof(Bt)); // Init default maximum packet size - bt->max_packet_size = FURI_HAL_BT_PACKET_SIZE_MAX; + bt->max_packet_size = FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX; + bt->profile = BtProfileSerial; // Load settings if(!bt_settings_load(&bt->bt_settings)) { bt_settings_save(&bt->bt_settings); @@ -83,27 +97,30 @@ Bt* bt_alloc() { bt->rpc = furi_record_open("rpc"); bt->rpc_event = osEventFlagsNew(NULL); + // API evnent + bt->api_event = osEventFlagsNew(NULL); + return bt; } // Called from GAP thread from Serial service -static uint16_t bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) { +static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) { furi_assert(context); Bt* bt = context; + uint16_t ret = 0; - size_t bytes_processed = rpc_session_feed(bt->rpc_session, data, size, 1000); - if(bytes_processed != size) { - FURI_LOG_E(TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size); + if(event.event == SerialServiceEventTypeDataReceived) { + size_t bytes_processed = + rpc_session_feed(bt->rpc_session, event.data.buffer, event.data.size, 1000); + if(bytes_processed != event.data.size) { + FURI_LOG_E( + TAG, "Only %d of %d bytes processed by RPC", bytes_processed, event.data.size); + } + ret = rpc_session_get_available_size(bt->rpc_session); + } else if(event.event == SerialServiceEventTypeDataSent) { + osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT); } - return rpc_session_get_available_size(bt->rpc_session); -} - -// Called from GAP thread from Serial service -static void bt_on_data_sent_callback(void* context) { - furi_assert(context); - Bt* bt = context; - - osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT); + return ret; } // Called from RPC thread @@ -115,11 +132,11 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt size_t bytes_sent = 0; while(bytes_sent < bytes_len) { size_t bytes_remain = bytes_len - bytes_sent; - if(bytes_remain > bt->max_packet_size) { - furi_hal_bt_tx(&bytes[bytes_sent], bt->max_packet_size); - bytes_sent += bt->max_packet_size; + if(bytes_remain > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) { + furi_hal_bt_serial_tx(&bytes[bytes_sent], FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX); + bytes_sent += FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX; } else { - furi_hal_bt_tx(&bytes[bytes_sent], bytes_remain); + furi_hal_bt_serial_tx(&bytes[bytes_sent], bytes_remain); bytes_sent += bytes_remain; } uint32_t event_flag = @@ -130,58 +147,65 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt } } -static void bt_rpc_buffer_is_empty_callback(void* context) { - furi_assert(context); - furi_hal_bt_notify_buffer_is_empty(); -} - // Called from GAP thread -static void bt_on_gap_event_callback(BleEvent event, void* context) { +static bool bt_on_gap_event_callback(BleEvent event, void* context) { furi_assert(context); Bt* bt = context; + bool ret = false; if(event.type == BleEventTypeConnected) { // Update status bar bt->status = BtStatusConnected; BtMessage message = {.type = BtMessageTypeUpdateStatusbar}; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); - // Open RPC session - FURI_LOG_I(TAG, "Open RPC connection"); - bt->rpc_session = rpc_session_open(bt->rpc); - rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); - rpc_session_set_buffer_is_empty_callback(bt->rpc_session, bt_rpc_buffer_is_empty_callback); - rpc_session_set_context(bt->rpc_session, bt); - furi_hal_bt_set_data_event_callbacks( - RPC_BUFFER_SIZE, bt_on_data_received_callback, bt_on_data_sent_callback, bt); + if(bt->profile == BtProfileSerial) { + // Open RPC session + FURI_LOG_I(TAG, "Open RPC connection"); + bt->rpc_session = rpc_session_open(bt->rpc); + rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); + rpc_session_set_buffer_is_empty_callback( + bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); + rpc_session_set_context(bt->rpc_session, bt); + furi_hal_bt_serial_set_event_callback(RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + } // Update battery level PowerInfo info; power_get_info(bt->power, &info); message.type = BtMessageTypeUpdateBatteryLevel; message.data.battery_level = info.charge; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + ret = true; } else if(event.type == BleEventTypeDisconnected) { - if(bt->rpc_session) { + if(bt->profile == BtProfileSerial && bt->rpc_session) { FURI_LOG_I(TAG, "Close RPC connection"); osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); rpc_session_close(bt->rpc_session); - furi_hal_bt_set_data_event_callbacks(0, NULL, NULL, NULL); + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); bt->rpc_session = NULL; } + ret = true; } else if(event.type == BleEventTypeStartAdvertising) { bt->status = BtStatusAdvertising; BtMessage message = {.type = BtMessageTypeUpdateStatusbar}; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + ret = true; } else if(event.type == BleEventTypeStopAdvertising) { bt->status = BtStatusOff; BtMessage message = {.type = BtMessageTypeUpdateStatusbar}; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + ret = true; } else if(event.type == BleEventTypePinCodeShow) { BtMessage message = { .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code}; furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + ret = true; + } else if(event.type == BleEventTypePinCodeVerify) { + ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code); } else if(event.type == BleEventTypeUpdateMTU) { bt->max_packet_size = event.data.max_packet_size; + ret = true; } + return ret; } static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) { @@ -204,29 +228,56 @@ static void bt_statusbar_update(Bt* bt) { } } +static void bt_change_profile(Bt* bt, BtMessage* message) { + if(bt->profile == BtProfileSerial && bt->rpc_session) { + FURI_LOG_I(TAG, "Close RPC connection"); + osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); + rpc_session_close(bt->rpc_session); + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + bt->rpc_session = NULL; + } + + FuriHalBtProfile furi_profile; + if(message->data.profile == BtProfileHidKeyboard) { + furi_profile = FuriHalBtProfileHidKeyboard; + } else { + furi_profile = FuriHalBtProfileSerial; + } + + if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { + FURI_LOG_I(TAG, "Bt App started"); + if(bt->bt_settings.enabled) { + furi_hal_bt_start_advertising(); + } + furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); + bt->profile = message->data.profile; + *message->result = true; + } else { + FURI_LOG_E(TAG, "Failed to start Bt App"); + *message->result = false; + } + osEventFlagsSet(bt->api_event, BT_API_UNLOCK_EVENT); +} + int32_t bt_srv() { Bt* bt = bt_alloc(); furi_record_create("bt", bt); // Read keys if(!bt_load_key_storage(bt)) { - FURI_LOG_W(TAG, "Failed to load saved bonding keys"); + FURI_LOG_W(TAG, "Failed to load bonding keys"); } - // Start 2nd core - if(!furi_hal_bt_start_core2()) { - FURI_LOG_E(TAG, "Core2 startup failed"); - } else { - view_port_enabled_set(bt->statusbar_view_port, true); - if(furi_hal_bt_init_app(bt_on_gap_event_callback, bt)) { - FURI_LOG_I(TAG, "BLE stack started"); - if(bt->bt_settings.enabled) { - furi_hal_bt_start_advertising(); - } - } else { - FURI_LOG_E(TAG, "BT App start failed"); + + // Start BLE stack + if(furi_hal_bt_start_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt)) { + FURI_LOG_I(TAG, "BLE stack started"); + if(bt->bt_settings.enabled) { + furi_hal_bt_start_advertising(); } + furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); + } else { + FURI_LOG_E(TAG, "BT App start failed"); } - furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); // Update statusbar bt_statusbar_update(bt); @@ -239,14 +290,14 @@ int32_t bt_srv() { bt_statusbar_update(bt); } else if(message.type == BtMessageTypeUpdateBatteryLevel) { // Update battery level - if(furi_hal_bt_is_active()) { - battery_svc_update_level(message.data.battery_level); - } + furi_hal_bt_update_battery_level(message.data.battery_level); } else if(message.type == BtMessageTypePinCodeShow) { // Display PIN code bt_pin_code_show_event_handler(bt, message.data.pin_code); } else if(message.type == BtMessageTypeKeysStorageUpdated) { bt_save_key_storage(bt); + } else if(message.type == BtMessageTypeSetProfile) { + bt_change_profile(bt, &message); } } return 0; diff --git a/applications/bt/bt_service/bt.h b/applications/bt/bt_service/bt.h index 0a2a924c..9f609937 100644 --- a/applications/bt/bt_service/bt.h +++ b/applications/bt/bt_service/bt.h @@ -9,6 +9,22 @@ extern "C" { typedef struct Bt Bt; +typedef enum { + BtProfileSerial, + BtProfileHidKeyboard, +} BtProfile; + +/** + * Change BLE Profile + * @note Call of this function leads to 2nd core restart + * + * @param bt Bt instance + * @param profile BtProfile + * + * @return true on success + */ +bool bt_set_profile(Bt* bt, BtProfile profile); + #ifdef __cplusplus } #endif diff --git a/applications/bt/bt_service/bt_api.c b/applications/bt/bt_service/bt_api.c new file mode 100755 index 00000000..9ff9017a --- /dev/null +++ b/applications/bt/bt_service/bt_api.c @@ -0,0 +1,15 @@ +#include "bt_i.h" + +bool bt_set_profile(Bt* bt, BtProfile profile) { + furi_assert(bt); + + // Send message + bool result = false; + BtMessage message = { + .type = BtMessageTypeSetProfile, .data.profile = profile, .result = &result}; + furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); + // Wait for unlock + osEventFlagsWait(bt->api_event, BT_API_UNLOCK_EVENT, osFlagsWaitAny, osWaitForever); + + return result; +} diff --git a/applications/bt/bt_service/bt_i.h b/applications/bt/bt_service/bt_i.h index 840d920e..4961f041 100644 --- a/applications/bt/bt_service/bt_i.h +++ b/applications/bt/bt_service/bt_i.h @@ -15,6 +15,8 @@ #include "../bt_settings.h" +#define BT_API_UNLOCK_EVENT (1UL << 0) + typedef enum { BtStatusOff, BtStatusAdvertising, @@ -26,16 +28,19 @@ typedef enum { BtMessageTypeUpdateBatteryLevel, BtMessageTypePinCodeShow, BtMessageTypeKeysStorageUpdated, + BtMessageTypeSetProfile, } BtMessageType; typedef union { uint32_t pin_code; uint8_t battery_level; + BtProfile profile; } BtMessageData; typedef struct { BtMessageType type; BtMessageData data; + bool* result; } BtMessage; struct Bt { @@ -44,6 +49,7 @@ struct Bt { uint16_t max_packet_size; BtSettings bt_settings; BtStatus status; + BtProfile profile; osMessageQueueId_t message_queue; Gui* gui; ViewPort* statusbar_view_port; @@ -53,4 +59,5 @@ struct Bt { Rpc* rpc; RpcSession* rpc_session; osEventFlagsId_t rpc_event; + osEventFlagsId_t api_event; }; |