Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/flipperzero-firmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgornekich <n.gorbadey@gmail.com>2021-12-08 14:28:01 +0300
committerGitHub <noreply@github.com>2021-12-08 14:28:01 +0300
commit7170864fe41f783ea84892f2e326ab120e2ee446 (patch)
tree52b6088d427d7216db80e520e38dd052e0ebce1a /applications/bt
parentbb96509ed1e92c12b0b622f105eaa813d32009b4 (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-xapplications/bt/bt_service/bt.c153
-rw-r--r--applications/bt/bt_service/bt.h16
-rwxr-xr-xapplications/bt/bt_service/bt_api.c15
-rw-r--r--applications/bt/bt_service/bt_i.h7
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;
};